Advertisement
Guest User

libretro_citra_cheat_attempt

a guest
May 16th, 2019
223
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Diff 67.77 KB | None | 0 0
  1. diff --git a/.travis.yml b/.travis.yml
  2. deleted file mode 100644
  3. index 19e2664f..00000000
  4. --- a/.travis.yml
  5. +++ /dev/null
  6. @@ -1,86 +0,0 @@
  7. -language: cpp
  8. -matrix:
  9. -  include:
  10. -    - os: linux
  11. -      env: NAME="clang-format"
  12. -      sudo: required
  13. -      dist: trusty
  14. -      services: docker
  15. -      install: "./.travis/clang-format/deps.sh"
  16. -      script: "./.travis/clang-format/build.sh"
  17. -    - os: linux
  18. -      env: NAME="linux build"
  19. -      sudo: required
  20. -      dist: trusty
  21. -      services: docker
  22. -      addons:
  23. -        apt:
  24. -          packages:
  25. -            - p7zip-full
  26. -      install: "./.travis/linux/deps.sh"
  27. -      script: "./.travis/linux/build.sh"
  28. -      after_success: "./.travis/linux/upload.sh"
  29. -      cache: ccache
  30. -    - if: repo = citra-emu/citra AND branch = master AND type = push
  31. -      os: linux
  32. -      env: NAME="transifex push"
  33. -      sudo: required
  34. -      dist: trusty
  35. -      services: docker
  36. -      install: "./.travis/transifex/deps.sh"
  37. -      script: "./.travis/transifex/build.sh"
  38. -    - os: osx
  39. -      env: NAME="macos build"
  40. -      sudo: false
  41. -      osx_image: xcode10
  42. -      install: "./.travis/macos/deps.sh"
  43. -      script: "./.travis/macos/build.sh"
  44. -      after_success: "./.travis/macos/upload.sh"
  45. -      cache: ccache
  46. -    - os: linux
  47. -      env: NAME="linux build (frozen versions of dependencies)"
  48. -      sudo: required
  49. -      dist: trusty
  50. -      services: docker
  51. -      cache: ccache
  52. -      script: "./.travis/linux-frozen/build.sh"
  53. -    - os: linux
  54. -      env: NAME="MinGW build"
  55. -      sudo: required
  56. -      dist: trusty
  57. -      services: docker
  58. -      addons:
  59. -        apt:
  60. -          packages:
  61. -            - p7zip-full
  62. -      install: "./.travis/linux-mingw/deps.sh"
  63. -      script: "./.travis/linux-mingw/build.sh"
  64. -      after_success: "./.travis/linux-mingw/upload.sh"
  65. -      cache: ccache
  66. -    - if: repo =~ ^.*\/(citra-canary|citra-nightly)$ AND tag IS present
  67. -      git:
  68. -        depth: false
  69. -      os: linux
  70. -      env: NAME="flatpak build"
  71. -      sudo: required
  72. -      dist: trusty
  73. -      services: docker
  74. -      cache: ccache
  75. -      install: "./.travis/linux-flatpak/deps.sh"
  76. -      script: "./.travis/linux-flatpak/build.sh"
  77. -      after_script: "./.travis/linux-flatpak/finish.sh"
  78. -
  79. -deploy:
  80. -  provider: releases
  81. -  api_key:
  82. -    secure: Mck15DIWaJdxDiS3aYVlM9N3G6y8VKUI1rnwII7/iolfm1s94U+tgvbheZDmT7SSbFyaGaYO/E8HrV/uZR9Vvs7ev20sHsTN1u60OTWfDIIyHs9SqjhcGbtq95m9/dMFschOYqTOR+gAs5BsxjuoeAotHdhpQEwvkO2oo5oR0zhGy45gjFnVvtcxT/IfpZBIpVgcK3aLb9zT6ekcJbSiPmEB15iLq3xXd0nFUNtEZdX3D6Veye4n5jB6n72qN8JVoKvPZAwaC2K0pZxpcGJaXDchLsw1q+4eCvdz6UJfUemeQ/uMAmjfeQ3wrzYGXe3nCM3WmX5wosCsB0mw4zYatzl3si6CZ1W+0GkV4Rwlx03dfp7v3EeFhTsXYCaXqhwuLZnWOLUik8t9vaSoFUx4nUIRwfO9kAMUJQSpLuHNO2nT01s3GxvqxzczuLQ9he5nGSi0RRodUzDwek1qUp6I4uV3gRHKz4B07YIc1i2fK88NLXjyQ0uLVZ+7Oq1+kgDp6+N7vvXXZ5qZ17tdaysSbKEE0Y8zsoXw7Rk1tPN19vrCS+TSpomNMyQyne1k+I5iZ/qkxPTLAS5qI6Utc2dL3GJdxWRAEfGNO9AIX3GV/jmmKfdcvwGsCYP8hxqs5vLYfgacw3D8NLf1941lQUwavC17jm9EV9g5G3Pn1Cp516E=
  83. -  file_glob: true
  84. -  file: "artifacts/*"
  85. -  skip_cleanup: true
  86. -  on:
  87. -    tags: true
  88. -
  89. -notifications:
  90. -  webhooks:
  91. -    urls:
  92. -      - https://api.citra-emu.org/code/travis/notify
  93. diff --git a/CMakeLists.txt b/CMakeLists.txt
  94. index 46ad976a..a7781560 100644
  95. --- a/CMakeLists.txt
  96. +++ b/CMakeLists.txt
  97. @@ -6,6 +6,7 @@ include(DownloadExternals)
  98.  include(CMakeDependentOption)
  99.  
  100.  project(citra)
  101. +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fpic")
  102.  
  103.  # Set bundled sdl2/qt as dependent options.
  104.  # OFF by default, but if ENABLE_SDL2 and MSVC are true then ON
  105. @@ -16,6 +17,8 @@ option(ENABLE_QT "Enable the Qt frontend" ON)
  106.  option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF)
  107.  CMAKE_DEPENDENT_OPTION(CITRA_USE_BUNDLED_QT "Download bundled Qt binaries" ON "ENABLE_QT;MSVC" OFF)
  108.  
  109. +option(ENABLE_LIBRETRO "Enable the LibRetro frontend" ON)
  110. +
  111.  option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
  112.  
  113.  option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
  114. diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
  115. index ae92ce86..5341fe86 100644
  116. --- a/externals/CMakeLists.txt
  117. +++ b/externals/CMakeLists.txt
  118. @@ -78,6 +78,11 @@ if (USE_DISCORD_PRESENCE)
  119.      target_include_directories(discord-rpc INTERFACE ./discord-rpc/include)
  120.  endif()
  121.  
  122. +# LibRetro
  123. +add_library(libretro INTERFACE)
  124. +target_include_directories(libretro INTERFACE ./libretro)
  125. +
  126. +
  127.  if (ENABLE_WEB_SERVICE)
  128.      # LibreSSL
  129.      set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
  130. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
  131. index 6138791c..75cb8e73 100644
  132. --- a/src/CMakeLists.txt
  133. +++ b/src/CMakeLists.txt
  134. @@ -19,6 +19,9 @@ if (ANDROID)
  135.  else()
  136.      add_subdirectory(dedicated_room)
  137.  endif()
  138. +if (ENABLE_LIBRETRO)
  139. +    add_subdirectory(citra_libretro)
  140. +endif()
  141.  if (ENABLE_WEB_SERVICE)
  142.      add_subdirectory(web_service)
  143.  endif()
  144. diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
  145. index caddd0a3..8f948a51 100644
  146. --- a/src/audio_core/CMakeLists.txt
  147. +++ b/src/audio_core/CMakeLists.txt
  148. @@ -30,6 +30,7 @@ add_library(audio_core STATIC
  149.      time_stretch.h
  150.  
  151.      $<$<BOOL:${SDL2_FOUND}>:sdl2_sink.cpp sdl2_sink.h>
  152. +    $<$<BOOL:${ENABLE_LIBRETRO}>:libretro_sink.cpp libretro_sink.h>
  153.      $<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h cubeb_input.cpp cubeb_input.h>
  154.      $<$<BOOL:${FFMPEG_FOUND}>:hle/ffmpeg_decoder.cpp hle/ffmpeg_decoder.h hle/ffmpeg_dl.cpp hle/ffmpeg_dl.h>
  155.      $<$<BOOL:${ENABLE_MF}>:hle/wmf_decoder.cpp hle/wmf_decoder.h hle/wmf_decoder_utils.cpp hle/wmf_decoder_utils.h>
  156. @@ -59,6 +60,11 @@ if(SDL2_FOUND)
  157.      target_compile_definitions(audio_core PRIVATE HAVE_SDL2)
  158.  endif()
  159.  
  160. +if (ENABLE_LIBRETRO)
  161. +    target_link_libraries(audio_core PRIVATE libretro)
  162. +    target_compile_definitions(audio_core PRIVATE HAVE_LIBRETRO)
  163. +endif()
  164. +
  165.  if(ENABLE_CUBEB)
  166.      target_link_libraries(audio_core PRIVATE cubeb)
  167.      target_compile_definitions(audio_core PUBLIC HAVE_CUBEB)
  168. diff --git a/src/audio_core/sink_details.cpp b/src/audio_core/sink_details.cpp
  169. index 25c71917..aa317595 100644
  170. --- a/src/audio_core/sink_details.cpp
  171. +++ b/src/audio_core/sink_details.cpp
  172. @@ -11,6 +11,9 @@
  173.  #ifdef HAVE_SDL2
  174.  #include "audio_core/sdl2_sink.h"
  175.  #endif
  176. +#ifdef HAVE_LIBRETRO
  177. +#include "audio_core/libretro_sink.h"
  178. +#endif
  179.  #ifdef HAVE_CUBEB
  180.  #include "audio_core/cubeb_sink.h"
  181.  #endif
  182. @@ -32,6 +35,13 @@ struct SinkDetails {
  183.  
  184.  // sink_details is ordered in terms of desirability, with the best choice at the top.
  185.  constexpr SinkDetails sink_details[] = {
  186. +#ifdef HAVE_LIBRETRO
  187. +    SinkDetails{"libretro",
  188. +           [](std::string_view device_id) -> std::unique_ptr<Sink> {
  189. +               return std::make_unique<LibRetroSink>(std::string(device_id));
  190. +           },
  191. +           &ListLibretroSinkDevices},
  192. +#endif
  193.  #ifdef HAVE_CUBEB
  194.      SinkDetails{"cubeb",
  195.                  [](std::string_view device_id) -> std::unique_ptr<Sink> {
  196. diff --git a/src/citra_libretro/CMakeLists.txt b/src/citra_libretro/CMakeLists.txt
  197. new file mode 100644
  198. index 00000000..62c329ec
  199. --- /dev/null
  200. +++ b/src/citra_libretro/CMakeLists.txt
  201. @@ -0,0 +1,28 @@
  202. +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
  203. +
  204. +add_library(citra_libretro SHARED
  205. +        emu_window/libretro_window.cpp
  206. +        emu_window/libretro_window.h
  207. +        input/input_factory.cpp
  208. +        input/input_factory.h
  209. +        input/mouse_tracker.cpp
  210. +        input/mouse_tracker.h
  211. +        citra_libretro.cpp
  212. +        citra_libretro.h
  213. +        environment.cpp
  214. +        environment.h
  215. +        core_settings.cpp
  216. +        core_settings.h
  217. +        libretro_logger.cpp
  218. +        libretro_logger.h)
  219. +
  220. +create_target_directory_groups(citra_libretro)
  221. +
  222. +target_link_libraries(citra_libretro PRIVATE common core)
  223. +target_link_libraries(citra_libretro PRIVATE glad libretro)
  224. +target_link_libraries(citra_libretro PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
  225. +if(DEFINED LIBRETRO_STATIC)
  226. +target_link_libraries(citra_libretro PRIVATE -static-libstdc++)
  227. +endif()
  228. +
  229. +set_target_properties(citra_libretro PROPERTIES PREFIX "")
  230. diff --git a/src/citra_libretro/citra_libretro.cpp b/src/citra_libretro/citra_libretro.cpp
  231. new file mode 100644
  232. index 00000000..b4f2f6cc
  233. --- /dev/null
  234. +++ b/src/citra_libretro/citra_libretro.cpp
  235. @@ -0,0 +1,545 @@
  236. +// Copyright 2017 Citra Emulator Project
  237. +// Licensed under GPLv2 or any later version
  238. +// Refer to the license.txt file included.
  239. +
  240. +#include <list>
  241. +#include <numeric>
  242. +#include <math.h>
  243. +#include <stdint.h>
  244. +#include <stdio.h>
  245. +#include <stdlib.h>
  246. +#include <common/file_util.h>
  247. +
  248. +#include "glad/glad.h"
  249. +#include "libretro.h"
  250. +
  251. +#include "audio_core/libretro_sink.h"
  252. +#include "citra_libretro/citra_libretro.h"
  253. +#include "citra_libretro/core_settings.h"
  254. +#include "citra_libretro/environment.h"
  255. +#include "citra_libretro/libretro_logger.h"
  256. +#include "citra_libretro/input/input_factory.h"
  257. +#include "common/logging/backend.h"
  258. +#include "common/logging/filter.h"
  259. +#include "common/string_util.h"
  260. +#include "core/core.h"
  261. +#include "core/memory.h"
  262. +#include "core/hle/kernel/memory.h"
  263. +#include "core/loader/loader.h"
  264. +#include "core/settings.h"
  265. +#include "core/frontend/applets/default_applets.h"
  266. +#include "video_core/renderer_opengl/renderer_opengl.h"
  267. +#include "video_core/video_core.h"
  268. +
  269. +class CitraLibRetro {
  270. +public:
  271. +    CitraLibRetro() : log_filter(Log::Level::Info) {}
  272. +
  273. +    Log::Filter log_filter;
  274. +    std::unique_ptr<EmuWindow_LibRetro> emu_window;
  275. +    struct retro_hw_render_callback hw_render {};
  276. +};
  277. +
  278. +CitraLibRetro* emu_instance;
  279. +
  280. +void* load_opengl_func(const char* name) {
  281. +    return (void*)emu_instance->hw_render.get_proc_address(name);
  282. +}
  283. +
  284. +
  285. +void retro_init() {
  286. +    emu_instance = new CitraLibRetro();
  287. +    //Log::Init();
  288. +    Log::SetGlobalFilter(emu_instance->log_filter);
  289. +
  290. +    // Check to see if the frontend is providing us with logging functionality
  291. +    auto callback = LibRetro::GetLoggingBackend();
  292. +    if (callback != nullptr) {
  293. +        Log::AddBackend(std::make_unique<LibRetroLogger>(callback));
  294. +    } else {
  295. +        Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
  296. +    }
  297. +
  298. +    LOG_DEBUG(Frontend, "Initialising core...");
  299. +
  300. +    // Set up LLE cores
  301. +    for (const auto& service_module : Service::service_module_map) {
  302. +        Settings::values.lle_modules.emplace(service_module.name, false);
  303. +    }
  304. +
  305. +    // Setup default, stub handlers for HLE applets
  306. +    Frontend::RegisterDefaultApplets();
  307. +
  308. +    LibRetro::Input::Init();
  309. +
  310. +}
  311. +
  312. +void retro_deinit() {
  313. +    LOG_DEBUG(Frontend, "Shutting down core...");
  314. +    if (Core::System::GetInstance().IsPoweredOn()) {
  315. +        Core::System::GetInstance().Shutdown();
  316. +    }
  317. +
  318. +    LibRetro::Input::Shutdown();
  319. +
  320. +    delete emu_instance;
  321. +
  322. +    //Log::Destroy();
  323. +}
  324. +
  325. +unsigned retro_api_version() {
  326. +    return RETRO_API_VERSION;
  327. +}
  328. +
  329. +void LibRetro::OnConfigureEnvironment() {
  330. +    static const retro_variable values[] = {
  331. +        {"citra_use_cpu_jit", "Enable CPU JIT; enabled|disabled"},
  332. +        {"citra_use_hw_renderer", "Enable hardware renderer; enabled|disabled"},
  333. +        {"citra_use_shader_jit", "Enable shader JIT; enabled|disabled"},
  334. +        {"citra_use_hw_shaders", "Enable hardware shaders; enabled|disabled"},
  335. +        {"citra_use_acc_geo_shaders", "Enable accurate geometry shaders (only for H/W shaders); enabled|disabled"},
  336. +        {"citra_use_acc_mul", "Enable accurate shaders multiplication (only for H/W shaders); enabled|disabled"},
  337. +        {"citra_resolution_factor",
  338. +         "Resolution scale factor; 1x (Native)|2x|3x|4x|5x|6x|7x|8x|9x|10x"},
  339. +        {"citra_layout_option", "Screen layout positioning; Default Top-Bottom Screen|Single "
  340. +                                "Screen Only|Large Screen, Small Screen|Side by Side"},
  341. +        {"citra_swap_screen", "Prominent 3DS screen; Top|Bottom"},
  342. +        {"citra_analog_function",
  343. +         "Right analog function; C-Stick and Touchscreen Pointer|Touchscreen Pointer|C-Stick"},
  344. +        {"citra_deadzone", "Emulated pointer deadzone (%); 15|20|25|30|35|0|5|10"},
  345. +        {"citra_use_virtual_sd", "Enable virtual SD card; enabled|disabled"},
  346. +        {"citra_use_libretro_save_path", "Savegame location; LibRetro Default|Citra Default"},
  347. +        {"citra_is_new_3ds", "3DS system model; Old 3DS|New 3DS"},
  348. +        {"citra_region_value",
  349. +         "3DS system region; Auto|Japan|USA|Europe|Australia|China|Korea|Taiwan"},
  350. +        {"citra_use_gdbstub", "Enable GDB stub; disabled|enabled"},
  351. +        {nullptr, nullptr}};
  352. +
  353. +    LibRetro::SetVariables(values);
  354. +
  355. +    static const struct retro_controller_description controllers[] = {
  356. +        {"Nintendo 3DS", RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 0)},
  357. +    };
  358. +
  359. +    static const struct retro_controller_info ports[] = {
  360. +        {controllers, 1},
  361. +        {nullptr, 0},
  362. +    };
  363. +
  364. +    LibRetro::SetControllerInfo(ports);
  365. +}
  366. +
  367. +uintptr_t LibRetro::GetFramebuffer() {
  368. +    return emu_instance->hw_render.get_current_framebuffer();
  369. +}
  370. +
  371. +/**
  372. + * Updates Citra's settings with Libretro's.
  373. + */
  374. +void UpdateSettings() {
  375. +    struct retro_input_descriptor desc[] = {
  376. +        {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left"},
  377. +        {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up"},
  378. +        {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down"},
  379. +        {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right"},
  380. +        {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "X"},
  381. +        {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Y"},
  382. +        {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B"},
  383. +        {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "A"},
  384. +        {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "L"},
  385. +        {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2, "ZL"},
  386. +        {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R"},
  387. +        {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2, "ZR"},
  388. +        {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start"},
  389. +        {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select"},
  390. +        {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3, "Home"},
  391. +        {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3, "Touch Screen Touch"},
  392. +        {0, 0},
  393. +    };
  394. +
  395. +    LibRetro::SetInputDescriptors(desc);
  396. +
  397. +    // Some settings cannot be set by LibRetro frontends - options have to be
  398. +    // finite. Make assumptions.
  399. +    Settings::values.log_filter = "*:Info";
  400. +    Settings::values.sink_id = "libretro";
  401. +    Settings::values.volume = 1.0f;
  402. +
  403. +    // We don't need these, as this is the frontend's responsibility.
  404. +    Settings::values.enable_audio_stretching = false;
  405. +    Settings::values.use_frame_limit = false;
  406. +    Settings::values.frame_limit = 100;
  407. +
  408. +    // For our other settings, import them from LibRetro.
  409. +    Settings::values.use_cpu_jit =
  410. +        LibRetro::FetchVariable("citra_use_cpu_jit", "enabled") == "enabled";
  411. +    Settings::values.use_hw_renderer =
  412. +        LibRetro::FetchVariable("citra_use_hw_renderer", "enabled") == "enabled";
  413. +    Settings::values.use_hw_shader =
  414. +            LibRetro::FetchVariable("citra_use_hw_shaders", "enabled") == "enabled";
  415. +    Settings::values.use_shader_jit =
  416. +        LibRetro::FetchVariable("citra_use_shader_jit", "enabled") == "enabled";
  417. +    Settings::values.shaders_accurate_gs =
  418. +            LibRetro::FetchVariable("citra_use_acc_geo_shaders", "enabled") == "enabled";
  419. +    Settings::values.shaders_accurate_mul =
  420. +            LibRetro::FetchVariable("citra_use_acc_mul", "enabled") == "enabled";
  421. +    Settings::values.use_virtual_sd =
  422. +        LibRetro::FetchVariable("citra_use_virtual_sd", "enabled") == "enabled";
  423. +    Settings::values.is_new_3ds =
  424. +        LibRetro::FetchVariable("citra_is_new_3ds", "Old 3DS") == "New 3DS";
  425. +    Settings::values.swap_screen = LibRetro::FetchVariable("citra_swap_screen", "Top") == "Bottom";
  426. +    Settings::values.use_gdbstub =
  427. +        LibRetro::FetchVariable("citra_use_gdbstub", "disabled") == "enabled";
  428. +
  429. +    // These values are a bit more hard to define, unfortunately.
  430. +    auto scaling = LibRetro::FetchVariable("citra_resolution_factor", "1x (Native)");
  431. +    auto endOfScale = scaling.find('x'); // All before 'x' in "_x ...", e.g "1x (Native)"
  432. +    if (endOfScale == std::string::npos) {
  433. +        LOG_ERROR(Frontend, "Failed to parse resolution scale!");
  434. +        Settings::values.resolution_factor = 1;
  435. +    } else {
  436. +        int scale = stoi(scaling.substr(0, endOfScale));
  437. +        Settings::values.resolution_factor = scale;
  438. +    }
  439. +
  440. +    auto layout = LibRetro::FetchVariable("citra_layout_option", "Default Top-Bottom Screen");
  441. +
  442. +    if (layout == "Default Top-Bottom Screen") {
  443. +        Settings::values.layout_option = Settings::LayoutOption::Default;
  444. +    } else if (layout == "Single Screen Only") {
  445. +        Settings::values.layout_option = Settings::LayoutOption::SingleScreen;
  446. +    } else if (layout == "Large Screen, Small Screen") {
  447. +        Settings::values.layout_option = Settings::LayoutOption::LargeScreen;
  448. +    } else if (layout == "Side by Side") {
  449. +        Settings::values.layout_option = Settings::LayoutOption::SideScreen;
  450. +    } else {
  451. +        LOG_ERROR(Frontend, "Unknown layout type: {}.", layout);
  452. +        Settings::values.layout_option = Settings::LayoutOption::Default;
  453. +    }
  454. +
  455. +    auto deadzone = LibRetro::FetchVariable("citra_deadzone", "15");
  456. +    LibRetro::settings.deadzone = (float)std::stoi(deadzone) / 100;
  457. +
  458. +    auto analog_function =
  459. +        LibRetro::FetchVariable("citra_analog_function", "C-Stick and Touchscreen Pointer");
  460. +
  461. +    if (analog_function == "C-Stick and Touchscreen Pointer") {
  462. +        LibRetro::settings.analog_function = LibRetro::CStickFunction::Both;
  463. +    } else if (analog_function == "C-Stick") {
  464. +        LibRetro::settings.analog_function = LibRetro::CStickFunction::CStick;
  465. +    } else if (analog_function == "Touchscreen Pointer") {
  466. +        LibRetro::settings.analog_function = LibRetro::CStickFunction::Touchscreen;
  467. +    } else {
  468. +        LOG_ERROR(Frontend, "Unknown right analog function: {}.", analog_function);
  469. +        LibRetro::settings.analog_function = LibRetro::CStickFunction::Both;
  470. +    }
  471. +
  472. +    auto region = LibRetro::FetchVariable("citra_region_value", "Auto");
  473. +    std::map<std::string, int> region_values;
  474. +    region_values["Auto"] = -1;
  475. +    region_values["Japan"] = 0;
  476. +    region_values["USA"] = 1;
  477. +    region_values["Europe"] = 2;
  478. +    region_values["Australia"] = 3;
  479. +    region_values["China"] = 4;
  480. +    region_values["Korea"] = 5;
  481. +    region_values["Taiwan"] = 6;
  482. +
  483. +    auto result = region_values.find(region);
  484. +    if (result == region_values.end()) {
  485. +        LOG_ERROR(Frontend, "Invalid region: {}.", region);
  486. +        Settings::values.region_value = -1;
  487. +    } else {
  488. +        Settings::values.region_value = result->second;
  489. +    }
  490. +
  491. +    Settings::values.current_input_profile.touch_device = "engine:emu_window";
  492. +
  493. +    // Hardcode buttons to bind to libretro - it is entirely redundant to have
  494. +    //  two methods of rebinding controls.
  495. +    // Citra: A = RETRO_DEVICE_ID_JOYPAD_A (8)
  496. +    Settings::values.current_input_profile.buttons[0] = "button:8,joystick:0,engine:libretro";
  497. +    // Citra: B = RETRO_DEVICE_ID_JOYPAD_B (0)
  498. +    Settings::values.current_input_profile.buttons[1] = "button:0,joystick:0,engine:libretro";
  499. +    // Citra: X = RETRO_DEVICE_ID_JOYPAD_X (9)
  500. +    Settings::values.current_input_profile.buttons[2] = "button:9,joystick:0,engine:libretro";
  501. +    // Citra: Y = RETRO_DEVICE_ID_JOYPAD_Y (1)
  502. +    Settings::values.current_input_profile.buttons[3] = "button:1,joystick:0,engine:libretro";
  503. +    // Citra: UP = RETRO_DEVICE_ID_JOYPAD_UP (4)
  504. +    Settings::values.current_input_profile.buttons[4] = "button:4,joystick:0,engine:libretro";
  505. +    // Citra: DOWN = RETRO_DEVICE_ID_JOYPAD_DOWN (5)
  506. +    Settings::values.current_input_profile.buttons[5] = "button:5,joystick:0,engine:libretro";
  507. +    // Citra: LEFT = RETRO_DEVICE_ID_JOYPAD_LEFT (6)
  508. +    Settings::values.current_input_profile.buttons[6] = "button:6,joystick:0,engine:libretro";
  509. +    // Citra: RIGHT = RETRO_DEVICE_ID_JOYPAD_RIGHT (7)
  510. +    Settings::values.current_input_profile.buttons[7] = "button:7,joystick:0,engine:libretro";
  511. +    // Citra: L = RETRO_DEVICE_ID_JOYPAD_L (10)
  512. +    Settings::values.current_input_profile.buttons[8] = "button:10,joystick:0,engine:libretro";
  513. +    // Citra: R = RETRO_DEVICE_ID_JOYPAD_R (11)
  514. +    Settings::values.current_input_profile.buttons[9] = "button:11,joystick:0,engine:libretro";
  515. +    // Citra: START = RETRO_DEVICE_ID_JOYPAD_START (3)
  516. +    Settings::values.current_input_profile.buttons[10] = "button:3,joystick:0,engine:libretro";
  517. +    // Citra: SELECT = RETRO_DEVICE_ID_JOYPAD_SELECT (2)
  518. +    Settings::values.current_input_profile.buttons[11] = "button:2,joystick:0,engine:libretro";
  519. +    // Citra: ZL = RETRO_DEVICE_ID_JOYPAD_L2 (12)
  520. +    Settings::values.current_input_profile.buttons[12] = "button:12,joystick:0,engine:libretro";
  521. +    // Citra: ZR = RETRO_DEVICE_ID_JOYPAD_R2 (13)
  522. +    Settings::values.current_input_profile.buttons[13] = "button:13,joystick:0,engine:libretro";
  523. +    // Citra: HOME = RETRO_DEVICE_ID_JOYPAD_L3 (as per above bindings) (14)
  524. +    Settings::values.current_input_profile.buttons[14] = "button:14,joystick:0,engine:libretro";
  525. +
  526. +    // Circle Pad
  527. +    Settings::values.current_input_profile.analogs[0] = "axis:0,joystick:0,engine:libretro";
  528. +    // C-Stick
  529. +    if (LibRetro::settings.analog_function != LibRetro::CStickFunction::Touchscreen) {
  530. +        Settings::values.current_input_profile.analogs[1] = "axis:1,joystick:0,engine:libretro";
  531. +    } else {
  532. +        Settings::values.current_input_profile.analogs[1] = "";
  533. +    }
  534. +
  535. +    // Configure the file storage location
  536. +    auto use_libretro_saves = LibRetro::FetchVariable("citra_use_libretro_save_path",
  537. +                                                      "LibRetro Default") == "LibRetro Default";
  538. +
  539. +    if (use_libretro_saves) {
  540. +        auto target_dir = LibRetro::GetSaveDir();
  541. +        if (target_dir.empty()) {
  542. +            LOG_INFO(Frontend, "No save dir provided; trying system dir...");
  543. +            target_dir = LibRetro::GetSystemDir();
  544. +        }
  545. +
  546. +        if (!target_dir.empty()) {
  547. +            target_dir += "/Citra";
  548. +
  549. +            // Ensure that this new dir exists
  550. +            if (!FileUtil::CreateDir(target_dir)) {
  551. +                LOG_ERROR(Frontend, "Failed to create \"{}\". Using Citra's default paths.", target_dir);
  552. +            } else {
  553. +                FileUtil::GetUserPath(FileUtil::UserPath::RootDir) + target_dir;
  554. +                const auto& target_dir_result = FileUtil::GetUserPath(FileUtil::UserPath::UserDir) + target_dir;
  555. +                LOG_INFO(Frontend, "User dir set to \"{}\".", target_dir_result);
  556. +            }
  557. +        }
  558. +    }
  559. +
  560. +    // Update the framebuffer sizing.
  561. +    emu_instance->emu_window->UpdateLayout();
  562. +
  563. +    Settings::Apply();
  564. +}
  565. +
  566. +/**
  567. + * libretro callback; Called every game tick.
  568. + */
  569. +void retro_run() {
  570. +    // Check to see if we actually have any config updates to process.
  571. +    if (LibRetro::HasUpdatedConfig()) {
  572. +        UpdateSettings();
  573. +    }
  574. +
  575. +    // We can't assume that the frontend has been nice and preserved all OpenGL settings. Reset.
  576. +    auto last_state = OpenGL::OpenGLState::GetCurState();
  577. +    ResetGLState();
  578. +    last_state.Apply();
  579. +
  580. +    while (!emu_instance->emu_window->HasSubmittedFrame()) {
  581. +        auto result = Core::System::GetInstance().RunLoop();
  582. +
  583. +        if (result != Core::System::ResultStatus::Success) {
  584. +            std::string errorContent = Core::System::GetInstance().GetStatusDetails();
  585. +            std::string msg;
  586. +
  587. +            switch (result) {
  588. +            case Core::System::ResultStatus::ErrorSystemFiles:
  589. +                msg = "Citra was unable to locate a 3DS system archive: " + errorContent;
  590. +                break;
  591. +            default:
  592. +                msg = "Fatal Error encountered: " + errorContent;
  593. +                break;
  594. +            }
  595. +
  596. +            LibRetro::DisplayMessage(msg.c_str());
  597. +        }
  598. +    }
  599. +}
  600. +
  601. +
  602. +void context_reset() {
  603. +    if (!Core::System::GetInstance().IsPoweredOn()) {
  604. +        LOG_CRITICAL(Frontend, "Cannot reset system core if isn't on!");
  605. +        return;
  606. +    }
  607. +
  608. +    // Recreate our renderer, so it can reset it's state.
  609. +    if (VideoCore::g_renderer != nullptr) {
  610. +        LOG_ERROR(Frontend,
  611. +                  "Likely memory leak: context_destroy() was not called before context_reset()!");
  612. +    }
  613. +
  614. +    // Check to see if the frontend provides us with OpenGL symbols
  615. +        if (emu_instance->hw_render.get_proc_address != nullptr) {
  616. +            if (!gladLoadGLLoader((GLADloadproc)load_opengl_func)) {
  617. +                LOG_CRITICAL(Frontend, "Glad failed to load (frontend-provided symbols)!");
  618. +                return;
  619. +            }
  620. +        } else {
  621. +            // Else, try to load them on our own
  622. +            if (!gladLoadGL()) {
  623. +                LOG_CRITICAL(Frontend, "Glad failed to load (internal symbols)!");
  624. +                return;
  625. +            }
  626. +        }
  627. +
  628. +    VideoCore::g_renderer = std::make_unique<OpenGL::RendererOpenGL>(*emu_instance->emu_window);
  629. +    if (VideoCore::g_renderer->Init() != Core::System::ResultStatus::Success) {
  630. +        LOG_DEBUG(Render, "initialized OK");
  631. +    } else {
  632. +        LOG_ERROR(Render, "initialization failed!");
  633. +    }
  634. +
  635. +    emu_instance->emu_window->UpdateLayout();
  636. +    emu_instance->emu_window->CreateContext();
  637. +}
  638. +
  639. +void context_destroy() {
  640. +    if (VideoCore::g_renderer != nullptr) {
  641. +        VideoCore::g_renderer->ShutDown();
  642. +    }
  643. +
  644. +    emu_instance->emu_window->DestroyContext();
  645. +    VideoCore::g_renderer = nullptr;
  646. +}
  647. +
  648. +void retro_reset() {
  649. +    Core::System::GetInstance().Shutdown();
  650. +    Core::System::GetInstance().Load(*emu_instance->emu_window, LibRetro::settings.file_path);
  651. +    context_reset(); // Force the renderer to appear
  652. +}
  653. +
  654. +/**
  655. + * libretro callback; Called when a game is to be loaded.
  656. + */
  657. +bool retro_load_game(const struct retro_game_info* info) {
  658. +    LOG_INFO(Frontend, "Starting Citra RetroArch game...");
  659. +
  660. +    LibRetro::settings.file_path = info->path;
  661. +
  662. +    LibRetro::SetHWSharedContext();
  663. +
  664. +    if (!LibRetro::SetPixelFormat(RETRO_PIXEL_FORMAT_XRGB8888)) {
  665. +        LOG_CRITICAL(Frontend, "XRGB8888 is not supported.");
  666. +        LibRetro::DisplayMessage("XRGB8888 is not supported.");
  667. +        return false;
  668. +    }
  669. +
  670. +    emu_instance->hw_render.context_type = RETRO_HW_CONTEXT_OPENGL_CORE;
  671. +    emu_instance->hw_render.version_major = 3;
  672. +    emu_instance->hw_render.version_minor = 3;
  673. +    emu_instance->hw_render.context_reset = context_reset;
  674. +    emu_instance->hw_render.context_destroy = context_destroy;
  675. +    emu_instance->hw_render.cache_context = false;
  676. +    emu_instance->hw_render.bottom_left_origin = true;
  677. +    if (!LibRetro::SetHWRenderer(&emu_instance->hw_render)) {
  678. +        LOG_CRITICAL(Frontend, "OpenGL 3.3 is not supported.");
  679. +        LibRetro::DisplayMessage("OpenGL 3.3 is not supported.");
  680. +        return false;
  681. +    }
  682. +
  683. +    emu_instance->emu_window = std::make_unique<EmuWindow_LibRetro>();
  684. +    UpdateSettings();
  685. +
  686. +    const Core::System::ResultStatus load_result{Core::System::GetInstance().Load(
  687. +        *emu_instance->emu_window, LibRetro::settings.file_path)};
  688. +
  689. +    switch (load_result) {
  690. +    case Core::System::ResultStatus::ErrorGetLoader:
  691. +        LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!",
  692. +                     LibRetro::settings.file_path);
  693. +        LibRetro::DisplayMessage("Failed to obtain loader for specified ROM!");
  694. +        return false;
  695. +    case Core::System::ResultStatus::ErrorLoader:
  696. +        LOG_CRITICAL(Frontend, "Failed to load ROM!");
  697. +        LibRetro::DisplayMessage("Failed to load ROM!");
  698. +        return false;
  699. +    case Core::System::ResultStatus::ErrorLoader_ErrorEncrypted:
  700. +        LOG_CRITICAL(Frontend, "The game that you are trying to load must be decrypted before "
  701. +                               "being used with Citra. \n\n For more information on dumping and "
  702. +                               "decrypting games, please refer to: "
  703. +                               "https://citra-emu.org/wiki/Dumping-Game-Cartridges");
  704. +        LibRetro::DisplayMessage("The game that you are trying to load must be decrypted before "
  705. +                                 "being used with Citra. \n\n For more information on dumping and "
  706. +                                 "decrypting games, please refer to: "
  707. +                                 "https://citra-emu.org/wiki/Dumping-Game-Cartridges");
  708. +        return false;
  709. +    case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat:
  710. +        LOG_CRITICAL(Frontend, "Error while loading ROM: The ROM format is not supported.");
  711. +        LibRetro::DisplayMessage("Error while loading ROM: The ROM format is not supported.");
  712. +        return false;
  713. +    case Core::System::ResultStatus::ErrorNotInitialized:
  714. +        LOG_CRITICAL(Frontend, "CPUCore not initialized");
  715. +        LibRetro::DisplayMessage("CPUCore not initialized");
  716. +        return false;
  717. +    case Core::System::ResultStatus::ErrorSystemMode:
  718. +        LOG_CRITICAL(Frontend, "Failed to determine system mode!");
  719. +        LibRetro::DisplayMessage("Failed to determine system mode!");
  720. +        return false;
  721. +    case Core::System::ResultStatus::ErrorVideoCore:
  722. +        LOG_CRITICAL(Frontend, "VideoCore not initialized");
  723. +        LibRetro::DisplayMessage("VideoCore not initialized");
  724. +        return false;
  725. +    case Core::System::ResultStatus::Success:
  726. +        break; // Expected case
  727. +    default:
  728. +        LOG_CRITICAL(Frontend, "Unknown error");
  729. +        LibRetro::DisplayMessage("Unknown error");
  730. +        return false;
  731. +    }
  732. +
  733. +    return true;
  734. +}
  735. +
  736. +void retro_unload_game() {
  737. +    LOG_DEBUG(Frontend, "Unloading game...");
  738. +    Core::System::GetInstance().Shutdown();
  739. +}
  740. +
  741. +unsigned retro_get_region() {
  742. +    return RETRO_REGION_NTSC;
  743. +}
  744. +
  745. +bool retro_load_game_special(unsigned game_type, const struct retro_game_info* info,
  746. +                             size_t num_info) {
  747. +    return retro_load_game(info);
  748. +}
  749. +
  750. +size_t retro_serialize_size() {
  751. +    return 0;
  752. +}
  753. +
  754. +bool retro_serialize(void* data_, size_t size) {
  755. +    return true;
  756. +}
  757. +
  758. +bool retro_unserialize(const void* data_, size_t size) {
  759. +    return true;
  760. +}
  761. +
  762. +void* retro_get_memory_data(unsigned id) {
  763. +/*
  764. +    if ( id == RETRO_MEMORY_SYSTEM_RAM )
  765. +        return impl->fcram.get();
  766. +*/
  767. +    return NULL;
  768. +}
  769. +
  770. +size_t retro_get_memory_size(unsigned id) {
  771. +/*
  772. +    if ( id == RETRO_MEMORY_SYSTEM_RAM )
  773. +        return Kernel::memory_regions[0].size;
  774. +*/
  775. +    return 0;
  776. +}
  777. +
  778. +void retro_cheat_reset() {}
  779. +
  780. +void retro_cheat_set(unsigned index, bool enabled, const char* code) {}
  781. diff --git a/src/citra_libretro/citra_libretro.h b/src/citra_libretro/citra_libretro.h
  782. new file mode 100644
  783. index 00000000..aa759532
  784. --- /dev/null
  785. +++ b/src/citra_libretro/citra_libretro.h
  786. @@ -0,0 +1,10 @@
  787. +// Copyright 2017 Citra Emulator Project
  788. +// Licensed under GPLv2 or any later version
  789. +// Refer to the license.txt file included.
  790. +
  791. +#pragma once
  792. +
  793. +#include "core/core.h"
  794. +#include "emu_window/libretro_window.h"
  795. +
  796. +namespace LibRetro {} // namespace LibRetro
  797. diff --git a/src/citra_libretro/core_settings.cpp b/src/citra_libretro/core_settings.cpp
  798. new file mode 100644
  799. index 00000000..9bdc90f5
  800. --- /dev/null
  801. +++ b/src/citra_libretro/core_settings.cpp
  802. @@ -0,0 +1,11 @@
  803. +// Copyright 2017 Citra Emulator Project
  804. +// Licensed under GPLv2 or any later version
  805. +// Refer to the license.txt file included.
  806. +
  807. +#include "core_settings.h"
  808. +
  809. +namespace LibRetro {
  810. +
  811. +CoreSettings settings = {};
  812. +
  813. +} // namespace LibRetro
  814. diff --git a/src/citra_libretro/core_settings.h b/src/citra_libretro/core_settings.h
  815. new file mode 100644
  816. index 00000000..8a1d4f55
  817. --- /dev/null
  818. +++ b/src/citra_libretro/core_settings.h
  819. @@ -0,0 +1,23 @@
  820. +// Copyright 2017 Citra Emulator Project
  821. +// Licensed under GPLv2 or any later version
  822. +// Refer to the license.txt file included.
  823. +
  824. +#pragma once
  825. +
  826. +#include <string>
  827. +
  828. +namespace LibRetro {
  829. +
  830. +enum CStickFunction { Both, CStick, Touchscreen };
  831. +
  832. +struct CoreSettings {
  833. +
  834. +    ::std::string file_path;
  835. +
  836. +    float deadzone = 1.f;
  837. +
  838. +    LibRetro::CStickFunction analog_function;
  839. +
  840. +} extern settings;
  841. +
  842. +} // namespace LibRetro
  843. diff --git a/src/citra_libretro/emu_window/libretro_window.cpp b/src/citra_libretro/emu_window/libretro_window.cpp
  844. new file mode 100644
  845. index 00000000..1444169d
  846. --- /dev/null
  847. +++ b/src/citra_libretro/emu_window/libretro_window.cpp
  848. @@ -0,0 +1,237 @@
  849. +// Copyright 2017 Citra Emulator Project
  850. +// Licensed under GPLv2 or any later version
  851. +// Refer to the license.txt file included.
  852. +
  853. +#include "glad/glad.h"
  854. +#include <libretro.h>
  855. +
  856. +#include "audio_core/audio_types.h"
  857. +#include "citra_libretro/citra_libretro.h"
  858. +#include "citra_libretro/environment.h"
  859. +#include "citra_libretro/input/input_factory.h"
  860. +#include "core/3ds.h"
  861. +#include "core/settings.h"
  862. +#include "video_core/renderer_opengl/gl_state.h"
  863. +
  864. +/// LibRetro expects a "default" GL state.
  865. +void ResetGLState() {
  866. +    // Reset internal state.
  867. +    OpenGL::OpenGLState state {};
  868. +    state.Apply();
  869. +
  870. +    // Clean up global state.
  871. +    glLogicOp(GL_COPY);
  872. +
  873. +    glEnable(GL_DEPTH_TEST);
  874. +    glDepthFunc(GL_LESS);
  875. +    glDepthMask(GL_TRUE);
  876. +
  877. +    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  878. +
  879. +    glDisable(GL_STENCIL_TEST);
  880. +    glStencilFunc(GL_ALWAYS, 0, 0xFFFFFFFF);
  881. +
  882. +    glEnable(GL_BLEND);
  883. +    glBlendFunc(GL_ONE, GL_ZERO);
  884. +    glBlendEquation(GL_FUNC_ADD);
  885. +    glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO);
  886. +    glBlendColor(0, 0, 0, 0);
  887. +
  888. +    glDisable(GL_COLOR_LOGIC_OP);
  889. +
  890. +    glDisable(GL_DITHER);
  891. +
  892. +    glDisable(GL_CULL_FACE);
  893. +    glCullFace(GL_BACK);
  894. +
  895. +    glActiveTexture(GL_TEXTURE0);
  896. +}
  897. +
  898. +EmuWindow_LibRetro::EmuWindow_LibRetro() {}
  899. +
  900. +EmuWindow_LibRetro::~EmuWindow_LibRetro() {}
  901. +
  902. +void EmuWindow_LibRetro::SwapBuffers() {
  903. +    submittedFrame = true;
  904. +
  905. +    auto current_state = OpenGL::OpenGLState::GetCurState();
  906. +
  907. +    ResetGLState();
  908. +
  909. +    if (tracker != nullptr) {
  910. +        tracker->Render(width, height);
  911. +    }
  912. +
  913. +    LibRetro::UploadVideoFrame(RETRO_HW_FRAME_BUFFER_VALID, static_cast<unsigned>(width),
  914. +                               static_cast<unsigned>(height), 0);
  915. +
  916. +    ResetGLState();
  917. +
  918. +    current_state.Apply();
  919. +}
  920. +
  921. +/*
  922. +void EmuWindow_LibRetro::SetupFramebuffer() {
  923. +    // TODO: Expose interface in renderer_opengl to configure this in it's internal state
  924. +    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer);
  925. +
  926. +    // glClear can be a slow path - skip clearing if we don't need to.
  927. +    if (doCleanFrame) {
  928. +        glClear(GL_COLOR_BUFFER_BIT);
  929. +
  930. +        doCleanFrame = false;
  931. +    }
  932. +}
  933. +*/
  934. +void EmuWindow_LibRetro::PollEvents() {
  935. +    LibRetro::PollInput();
  936. +
  937. +    // TODO: Poll for right click for motion emu
  938. +
  939. +    if (tracker != nullptr) {
  940. +        tracker->Update(width, height, GetFramebufferLayout().bottom_screen);
  941. +
  942. +        if (tracker->IsPressed()) {
  943. +            auto mousePos = tracker->GetPressedPosition();
  944. +
  945. +            if (hasTouched) {
  946. +                TouchMoved(mousePos.first, mousePos.second);
  947. +            } else {
  948. +                TouchPressed(mousePos.first, mousePos.second);
  949. +                hasTouched = true;
  950. +            }
  951. +        } else if (hasTouched) {
  952. +            hasTouched = false;
  953. +            TouchReleased();
  954. +        }
  955. +    }
  956. +}
  957. +
  958. +
  959. +void EmuWindow_LibRetro::MakeCurrent() {
  960. +    // They don't get any say in the matter - GL context is always current!
  961. +}
  962. +
  963. +void EmuWindow_LibRetro::DoneCurrent() {
  964. +    // They don't get any say in the matter - GL context is always current!
  965. +}
  966. +
  967. +void EmuWindow_LibRetro::OnMinimalClientAreaChangeRequest(
  968. +    const std::pair<unsigned, unsigned>& minimal_size) {
  969. +    width = minimal_size.first;
  970. +    height = minimal_size.second;
  971. +}
  972. +
  973. +void EmuWindow_LibRetro::UpdateLayout() {
  974. +    // TODO: Handle custom layouts
  975. +    // TODO: Extract this ugly thing somewhere else
  976. +    unsigned baseX;
  977. +    unsigned baseY;
  978. +
  979. +    float scaling = Settings::values.resolution_factor;
  980. +
  981. +    bool swapped = Settings::values.swap_screen;
  982. +
  983. +    enableEmulatedPointer = true;
  984. +
  985. +    switch (Settings::values.layout_option) {
  986. +    case Settings::LayoutOption::SingleScreen:
  987. +        if (swapped) { // Bottom screen visible
  988. +            baseX = Core::kScreenBottomWidth;
  989. +            baseY = Core::kScreenBottomHeight;
  990. +        } else { // Top screen visible
  991. +            baseX = Core::kScreenTopWidth;
  992. +            baseY = Core::kScreenTopHeight;
  993. +            enableEmulatedPointer = false;
  994. +        }
  995. +        baseX *= scaling;
  996. +        baseY *= scaling;
  997. +        break;
  998. +    case Settings::LayoutOption::LargeScreen:
  999. +        if (swapped) { // Bottom screen biggest
  1000. +            baseX = Core::kScreenBottomWidth + Core::kScreenTopWidth / 4;
  1001. +            baseY = Core::kScreenBottomHeight;
  1002. +        } else { // Top screen biggest
  1003. +            baseX = Core::kScreenTopWidth + Core::kScreenBottomWidth / 4;
  1004. +            baseY = Core::kScreenTopHeight;
  1005. +        }
  1006. +
  1007. +        if (scaling < 4) {
  1008. +            // Unfortunately, to get this aspect ratio correct (and have non-blurry 1x scaling),
  1009. +            //  we have to have a pretty large buffer for the minimum ratio.
  1010. +            baseX *= 4;
  1011. +            baseY *= 4;
  1012. +        } else {
  1013. +            baseX *= scaling;
  1014. +            baseY *= scaling;
  1015. +        }
  1016. +        break;
  1017. +    case Settings::LayoutOption::SideScreen:
  1018. +        baseX = Core::kScreenBottomWidth + Core::kScreenTopWidth;
  1019. +        baseY = Core::kScreenTopHeight;
  1020. +        baseX *= scaling;
  1021. +        baseY *= scaling;
  1022. +        break;
  1023. +    case Settings::LayoutOption::Default:
  1024. +    default:
  1025. +        if (swapped) { // Bottom screen on top
  1026. +            baseX = Core::kScreenBottomWidth;
  1027. +        } else { // Top screen on top
  1028. +            baseX = Core::kScreenTopWidth;
  1029. +        }
  1030. +        baseY = Core::kScreenTopHeight + Core::kScreenBottomHeight;
  1031. +        baseX *= scaling;
  1032. +        baseY *= scaling;
  1033. +        break;
  1034. +    }
  1035. +
  1036. +    // Update Libretro with our status
  1037. +    struct retro_system_av_info info {};
  1038. +    info.timing.fps = 60.0;
  1039. +    info.timing.sample_rate = AudioCore::native_sample_rate;
  1040. +    info.geometry.aspect_ratio = (float)baseX / (float)baseY;
  1041. +    info.geometry.base_width = baseX;
  1042. +    info.geometry.base_height = baseY;
  1043. +    info.geometry.max_width = baseX;
  1044. +    info.geometry.max_height = baseY;
  1045. +    if (!LibRetro::SetGeometry(&info)) {
  1046. +        LOG_CRITICAL(Frontend, "Failed to update 3DS layout in frontend!");
  1047. +    }
  1048. +
  1049. +    NotifyClientAreaSizeChanged(std::pair<unsigned, unsigned>(baseX, baseY));
  1050. +    OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned>(baseX, baseY));
  1051. +    UpdateCurrentFramebufferLayout(baseX, baseY);
  1052. +}
  1053. +
  1054. +/*
  1055. +bool EmuWindow_LibRetro::ShouldDeferRendererInit() const {
  1056. +    // load_game doesn't always provide a GL context.
  1057. +    return true;
  1058. +}
  1059. +
  1060. +bool EmuWindow_LibRetro::NeedsClearing() const {
  1061. +    // We manage this ourselves.
  1062. +    return false;
  1063. +}
  1064. +*/
  1065. +
  1066. +bool EmuWindow_LibRetro::HasSubmittedFrame() {
  1067. +    bool state = submittedFrame;
  1068. +    submittedFrame = false;
  1069. +    return state;
  1070. +}
  1071. +
  1072. +void EmuWindow_LibRetro::CreateContext() {
  1073. +    printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!! Create context\n");
  1074. +    framebuffer = static_cast<GLuint>(LibRetro::GetFramebuffer());
  1075. +
  1076. +    if (enableEmulatedPointer) {
  1077. +        tracker = std::make_unique<LibRetro::Input::MouseTracker>();
  1078. +    }
  1079. +
  1080. +    doCleanFrame = true;
  1081. +}
  1082. +
  1083. +void EmuWindow_LibRetro::DestroyContext() {
  1084. +    tracker = nullptr;
  1085. +}
  1086. diff --git a/src/citra_libretro/emu_window/libretro_window.h b/src/citra_libretro/emu_window/libretro_window.h
  1087. new file mode 100644
  1088. index 00000000..56e01019
  1089. --- /dev/null
  1090. +++ b/src/citra_libretro/emu_window/libretro_window.h
  1091. @@ -0,0 +1,77 @@
  1092. +// Copyright 2017 Citra Emulator Project
  1093. +// Licensed under GPLv2 or any later version
  1094. +// Refer to the license.txt file included.
  1095. +
  1096. +#pragma once
  1097. +
  1098. +#include <glad/glad.h>
  1099. +
  1100. +#include <memory>
  1101. +#include <utility>
  1102. +#include "core/frontend/emu_window.h"
  1103. +
  1104. +#include "citra_libretro/input/mouse_tracker.h"
  1105. +
  1106. +void ResetGLState();
  1107. +
  1108. +class EmuWindow_LibRetro : public Frontend::EmuWindow {
  1109. +public:
  1110. +    EmuWindow_LibRetro();
  1111. +    ~EmuWindow_LibRetro();
  1112. +
  1113. +    /// Swap buffers to display the next frame
  1114. +    void SwapBuffers() override;
  1115. +
  1116. +    /// Polls window events
  1117. +    void PollEvents() override;
  1118. +
  1119. +    /// Makes the graphics context current for the caller thread
  1120. +    void MakeCurrent() override;
  1121. +
  1122. +    /// Releases the GL context from the caller thread
  1123. +    void DoneCurrent() override;
  1124. +
  1125. +    //void SetupFramebuffer() override;
  1126. +
  1127. +    /// Prepares the window for rendering
  1128. +    void UpdateLayout();
  1129. +
  1130. +    /// Enables for deferring a renderer's initalisation.
  1131. +    //bool ShouldDeferRendererInit() const override;
  1132. +
  1133. +    /// States whether a frame has been submitted. Resets after call.
  1134. +    bool HasSubmittedFrame();
  1135. +
  1136. +    /// Flags that the framebuffer should be cleared.
  1137. +    //bool NeedsClearing() const override;
  1138. +
  1139. +    /// Creates state for a currently running OpenGL context.
  1140. +    void CreateContext();
  1141. +
  1142. +    /// Destroys a currently running OpenGL context.
  1143. +    void DestroyContext();
  1144. +
  1145. +private:
  1146. +    /// Called when a configuration change affects the minimal size of the window
  1147. +    void OnMinimalClientAreaChangeRequest(
  1148. +        const std::pair<unsigned, unsigned>& minimal_size) override;
  1149. +
  1150. +    float scale = 2;
  1151. +    int width;
  1152. +    int height;
  1153. +
  1154. +    bool submittedFrame = false;
  1155. +
  1156. +    // Hack to ensure stuff runs on the main thread
  1157. +    bool doCleanFrame = false;
  1158. +
  1159. +    // For tracking LibRetro state
  1160. +    bool hasTouched = false;
  1161. +
  1162. +    GLuint framebuffer;
  1163. +
  1164. +    // For tracking mouse cursor
  1165. +    std::unique_ptr<LibRetro::Input::MouseTracker> tracker = nullptr;
  1166. +
  1167. +    bool enableEmulatedPointer = true;
  1168. +};
  1169. diff --git a/src/citra_libretro/environment.cpp b/src/citra_libretro/environment.cpp
  1170. new file mode 100644
  1171. index 00000000..85a5d051
  1172. --- /dev/null
  1173. +++ b/src/citra_libretro/environment.cpp
  1174. @@ -0,0 +1,168 @@
  1175. +// Copyright 2017 Citra Emulator Project
  1176. +// Licensed under GPLv2 or any later version
  1177. +// Refer to the license.txt file included.
  1178. +
  1179. +#include <cstring>
  1180. +
  1181. +#include "core/settings.h"
  1182. +#include "audio_core/audio_types.h"
  1183. +#include "audio_core/libretro_sink.h"
  1184. +#include "common/scm_rev.h"
  1185. +#include "environment.h"
  1186. +
  1187. +using namespace LibRetro;
  1188. +
  1189. +namespace LibRetro {
  1190. +
  1191. +static retro_video_refresh_t video_cb;
  1192. +static retro_audio_sample_t audio_cb;
  1193. +static retro_environment_t environ_cb;
  1194. +static retro_input_poll_t input_poll_cb;
  1195. +static retro_input_state_t input_state_cb;
  1196. +
  1197. +void UploadVideoFrame(const void* data, unsigned width, unsigned height, size_t pitch) {
  1198. +    return video_cb(data, width, height, pitch);
  1199. +}
  1200. +
  1201. +bool SetHWSharedContext() {
  1202. +    return environ_cb(RETRO_ENVIRONMENT_SET_HW_SHARED_CONTEXT, NULL);
  1203. +}
  1204. +
  1205. +void PollInput() {
  1206. +    return input_poll_cb();
  1207. +}
  1208. +
  1209. +bool SetVariables(const retro_variable vars[]) {
  1210. +    return environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void*)vars);
  1211. +}
  1212. +
  1213. +bool SetControllerInfo(const retro_controller_info info[]) {
  1214. +    return environ_cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)info);
  1215. +}
  1216. +
  1217. +bool SetPixelFormat(const retro_pixel_format fmt) {
  1218. +    return environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, (void*)&fmt);
  1219. +}
  1220. +
  1221. +bool SetHWRenderer(retro_hw_render_callback* cb) {
  1222. +    return environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER, cb);
  1223. +}
  1224. +
  1225. +bool SetAudioCallback(retro_audio_callback* cb) {
  1226. +    return environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_CALLBACK, cb);
  1227. +}
  1228. +
  1229. +bool SetFrameTimeCallback(retro_frame_time_callback* cb) {
  1230. +    return environ_cb(RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK, cb);
  1231. +}
  1232. +
  1233. +bool SetGeometry(retro_system_av_info* cb) {
  1234. +    return environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, cb);
  1235. +}
  1236. +
  1237. +bool SetInputDescriptors(const retro_input_descriptor desc[]) {
  1238. +    return environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, (void*)desc);
  1239. +}
  1240. +
  1241. +bool HasUpdatedConfig() {
  1242. +    bool updated = false;
  1243. +    return environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated;
  1244. +}
  1245. +
  1246. +bool Shutdown() {
  1247. +    return environ_cb(RETRO_ENVIRONMENT_SHUTDOWN, NULL);
  1248. +}
  1249. +
  1250. +/// Displays the specified message to the screen.
  1251. +bool DisplayMessage(const char* sg) {
  1252. +    retro_message msg;
  1253. +    msg.msg = sg;
  1254. +    msg.frames = 60 * 10;
  1255. +    return environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE, &msg);
  1256. +}
  1257. +
  1258. +std::string FetchVariable(std::string key, std::string def) {
  1259. +    struct retro_variable var = {nullptr};
  1260. +    var.key = key.c_str();
  1261. +    if (!environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value == nullptr) {
  1262. +        // Fetching variable failed.
  1263. +        LOG_ERROR(Frontend, "Fetching variable {} failed.", key);
  1264. +        return def;
  1265. +    }
  1266. +    return std::string(var.value);
  1267. +}
  1268. +
  1269. +std::string GetSaveDir() {
  1270. +    char* var = nullptr;
  1271. +    if (!environ_cb(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &var) || var == nullptr) {
  1272. +        // Fetching variable failed.
  1273. +        LOG_ERROR(Frontend, "No save directory provided by LibRetro.");
  1274. +        return std::string();
  1275. +    }
  1276. +    return std::string(var);
  1277. +}
  1278. +
  1279. +std::string GetSystemDir() {
  1280. +    char* var = nullptr;
  1281. +    if (!environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &var) || var == nullptr) {
  1282. +        // Fetching variable failed.
  1283. +        LOG_ERROR(Frontend, "No system directory provided by LibRetro.");
  1284. +        return std::string();
  1285. +    }
  1286. +    return std::string(var);
  1287. +}
  1288. +
  1289. +retro_log_printf_t GetLoggingBackend() {
  1290. +    retro_log_callback callback{};
  1291. +    if (!environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &callback)) {
  1292. +        LOG_WARNING(Frontend, "No logging backend provided by LibRetro.");
  1293. +        return nullptr;
  1294. +    }
  1295. +    return callback.log;
  1296. +}
  1297. +
  1298. +int16_t CheckInput(unsigned port, unsigned device, unsigned index, unsigned id) {
  1299. +    return input_state_cb(port, device, index, id);
  1300. +}
  1301. +}; // namespace LibRetro
  1302. +
  1303. +void retro_get_system_info(struct retro_system_info* info) {
  1304. +    memset(info, 0, sizeof(*info));
  1305. +    info->library_name = "Citra";
  1306. +    info->library_version = Common::g_scm_desc;
  1307. +    info->need_fullpath = true;
  1308. +    info->valid_extensions = "3ds|3dsx|cia|elf";
  1309. +}
  1310. +
  1311. +void retro_set_audio_sample(retro_audio_sample_t cb) {
  1312. +    // We don't need single audio sample callbacks.
  1313. +}
  1314. +
  1315. +void retro_set_input_poll(retro_input_poll_t cb) {
  1316. +    LibRetro::input_poll_cb = cb;
  1317. +}
  1318. +
  1319. +void retro_set_video_refresh(retro_video_refresh_t cb) {
  1320. +    LibRetro::video_cb = cb;
  1321. +}
  1322. +void retro_set_environment(retro_environment_t cb) {
  1323. +    LibRetro::environ_cb = cb;
  1324. +    LibRetro::OnConfigureEnvironment();
  1325. +}
  1326. +
  1327. +void retro_set_controller_port_device(unsigned port, unsigned device) {}
  1328. +
  1329. +void retro_set_input_state(retro_input_state_t cb) {
  1330. +    input_state_cb = cb;
  1331. +}
  1332. +
  1333. +void retro_get_system_av_info(struct retro_system_av_info* info) {
  1334. +    // These are placeholders until we get control.
  1335. +    info->timing.fps = 60.0;
  1336. +    info->timing.sample_rate = AudioCore::native_sample_rate;
  1337. +    info->geometry.base_width = 400;
  1338. +    info->geometry.base_height = 480;
  1339. +    info->geometry.max_width = 400 * 10;
  1340. +    info->geometry.max_height = 480 * 10;
  1341. +    info->geometry.aspect_ratio = 0;
  1342. +}
  1343. diff --git a/src/citra_libretro/environment.h b/src/citra_libretro/environment.h
  1344. new file mode 100644
  1345. index 00000000..e6e18386
  1346. --- /dev/null
  1347. +++ b/src/citra_libretro/environment.h
  1348. @@ -0,0 +1,85 @@
  1349. +// Copyright 2017 Citra Emulator Project
  1350. +// Licensed under GPLv2 or any later version
  1351. +// Refer to the license.txt file included.
  1352. +
  1353. +#pragma once
  1354. +
  1355. +#include <cstdint>
  1356. +#include "citra_libretro.h"
  1357. +#include "common/logging/backend.h"
  1358. +#include "common/logging/filter.h"
  1359. +#include "common/logging/log.h"
  1360. +#include "core/core.h"
  1361. +#include "libretro.h"
  1362. +
  1363. +namespace LibRetro {
  1364. +
  1365. +/// Calls back to LibRetro to upload a particular video frame.
  1366. +/// @see retro_video_refresh_t
  1367. +void UploadVideoFrame(const void* data, unsigned width, unsigned height, size_t pitch);
  1368. +
  1369. +/// Calls back to LibRetro to poll input.
  1370. +/// @see retro_input_poll_t
  1371. +void PollInput();
  1372. +
  1373. +/// Sets the environmental variables used for settings.
  1374. +bool SetVariables(const retro_variable vars[]);
  1375. +
  1376. +bool SetHWSharedContext(void);
  1377. +
  1378. +/// Returns the LibRetro save directory, or a empty string if one doesn't exist.
  1379. +std::string GetSaveDir();
  1380. +
  1381. +/// Returns the LibRetro system directory, or a empty string if one doesn't exist.
  1382. +std::string GetSystemDir();
  1383. +
  1384. +/// Fetches a variable by key name.
  1385. +std::string FetchVariable(std::string key, std::string def);
  1386. +
  1387. +/// Returns a logging backend, or null if the frontend refuses to provide one.
  1388. +retro_log_printf_t GetLoggingBackend();
  1389. +
  1390. +/// Displays information about the kinds of controllers that this Citra recreates.
  1391. +bool SetControllerInfo(const retro_controller_info info[]);
  1392. +
  1393. +/// Sets the framebuffer pixel format.
  1394. +bool SetPixelFormat(const retro_pixel_format fmt);
  1395. +
  1396. +/// Sets the H/W rendering context.
  1397. +bool SetHWRenderer(retro_hw_render_callback* cb);
  1398. +
  1399. +/// Sets the async audio callback.
  1400. +bool SetAudioCallback(retro_audio_callback* cb);
  1401. +
  1402. +/// Sets the frame time callback.
  1403. +bool SetFrameTimeCallback(retro_frame_time_callback* cb);
  1404. +
  1405. +/// Set the size of the new screen buffer.
  1406. +bool SetGeometry(retro_system_av_info* cb);
  1407. +
  1408. +/// Tells LibRetro what input buttons are labelled on the 3DS.
  1409. +bool SetInputDescriptors(const retro_input_descriptor desc[]);
  1410. +
  1411. +/// Returns the current status of a input.
  1412. +int16_t CheckInput(unsigned port, unsigned device, unsigned index, unsigned id);
  1413. +
  1414. +/// Called when the emulator environment is ready to be configured.
  1415. +void OnConfigureEnvironment();
  1416. +
  1417. +/// Submits audio frames to LibRetro.
  1418. +/// @see retro_audio_sample_batch_t
  1419. +void SubmitAudio(const int16_t* data, size_t frames);
  1420. +
  1421. +/// Checks to see if the frontend configuration has been updated.
  1422. +bool HasUpdatedConfig();
  1423. +
  1424. +/// Returns the current framebuffer.
  1425. +uintptr_t GetFramebuffer();
  1426. +
  1427. +/// Tells the frontend that we are done.
  1428. +bool Shutdown();
  1429. +
  1430. +/// Displays the specified message to the screen.
  1431. +bool DisplayMessage(const char* sg);
  1432. +
  1433. +} // namespace LibRetro
  1434. diff --git a/src/citra_libretro/input/input_factory.cpp b/src/citra_libretro/input/input_factory.cpp
  1435. new file mode 100644
  1436. index 00000000..904de2f2
  1437. --- /dev/null
  1438. +++ b/src/citra_libretro/input/input_factory.cpp
  1439. @@ -0,0 +1,110 @@
  1440. +// Copyright 2017 Citra Emulator Project
  1441. +// Licensed under GPLv2 or any later version
  1442. +// Refer to the license.txt file included.
  1443. +
  1444. +#include <cmath>
  1445. +#include <memory>
  1446. +#include <unordered_map>
  1447. +#include <libretro.h>
  1448. +
  1449. +#include "common/math_util.h"
  1450. +#include "core/frontend/input.h"
  1451. +
  1452. +#include "citra_libretro/environment.h"
  1453. +#include "citra_libretro/input/input_factory.h"
  1454. +
  1455. +namespace LibRetro {
  1456. +
  1457. +namespace Input {
  1458. +
  1459. +class LibRetroButtonFactory;
  1460. +class LibRetroAxisFactory;
  1461. +
  1462. +class LibRetroButton final : public ::Input::ButtonDevice {
  1463. +public:
  1464. +    explicit LibRetroButton(int joystick_, int button_) : joystick(joystick_), button(button_) {}
  1465. +
  1466. +    bool GetStatus() const override {
  1467. +        return CheckInput((unsigned int)joystick, RETRO_DEVICE_JOYPAD, 0, (unsigned int)button) > 0;
  1468. +    }
  1469. +
  1470. +private:
  1471. +    int joystick;
  1472. +    int button;
  1473. +};
  1474. +
  1475. +/// A button device factory that creates button devices from LibRetro joystick
  1476. +class LibRetroButtonFactory final : public ::Input::Factory<::Input::ButtonDevice> {
  1477. +public:
  1478. +    /**
  1479. +     * Creates a button device from a joystick button
  1480. +     * @param params contains parameters for creating the device:
  1481. +     *     - "joystick": the index of the joystick to bind
  1482. +     *     - "button": the index of the button to bind
  1483. +     */
  1484. +    std::unique_ptr<::Input::ButtonDevice> Create(const Common::ParamPackage& params) override {
  1485. +        const int joystick_index = params.Get("joystick", 0);
  1486. +
  1487. +        const int button = params.Get("button", 0);
  1488. +        return std::make_unique<LibRetroButton>(joystick_index, button);
  1489. +    }
  1490. +};
  1491. +
  1492. +/// A axis device factory that creates axis devices from LibRetro joystick
  1493. +class LibRetroAxis final : public ::Input::AnalogDevice {
  1494. +public:
  1495. +    explicit LibRetroAxis(int joystick_, int button_) : joystick(joystick_), button(button_) {}
  1496. +
  1497. +    std::tuple<float, float> GetStatus() const override {
  1498. +        auto axis_x =
  1499. +            (float)CheckInput((unsigned int)joystick, RETRO_DEVICE_ANALOG, (unsigned int)button, 0);
  1500. +        auto axis_y =
  1501. +            (float)CheckInput((unsigned int)joystick, RETRO_DEVICE_ANALOG, (unsigned int)button, 1);
  1502. +        return std::make_tuple(axis_x / INT16_MAX, -axis_y / INT16_MAX);
  1503. +    }
  1504. +
  1505. +private:
  1506. +    int joystick;
  1507. +    int button;
  1508. +};
  1509. +
  1510. +/// A axis device factory that creates axis devices from SDL joystick
  1511. +class LibRetroAxisFactory final : public ::Input::Factory<::Input::AnalogDevice> {
  1512. +public:
  1513. +    /**
  1514. +     * Creates a button device from a joystick button
  1515. +     * @param params contains parameters for creating the device:
  1516. +     *     - "joystick": the index of the joystick to bind
  1517. +     *     - "button"(optional): the index of the button to bind
  1518. +     *     - "hat"(optional): the index of the hat to bind as direction buttons
  1519. +     *     - "axis"(optional): the index of the axis to bind
  1520. +     *     - "direction"(only used for hat): the direction name of the hat to bind. Can be "up",
  1521. +     *         "down", "left" or "right"
  1522. +     *     - "threshould"(only used for axis): a float value in (-1.0, 1.0) which the button is
  1523. +     *         triggered if the axis value crosses
  1524. +     *     - "direction"(only used for axis): "+" means the button is triggered when the axis value
  1525. +     *         is greater than the threshold; "-" means the button is triggered when the axis value
  1526. +     *         is smaller than the threshold
  1527. +     */
  1528. +    std::unique_ptr<::Input::AnalogDevice> Create(const Common::ParamPackage& params) override {
  1529. +        const int joystick_index = params.Get("joystick", 0);
  1530. +
  1531. +        const int button = params.Get("axis", 0);
  1532. +        return std::make_unique<LibRetroAxis>(joystick_index, button);
  1533. +    }
  1534. +};
  1535. +
  1536. +void Init() {
  1537. +    using namespace ::Input;
  1538. +    RegisterFactory<ButtonDevice>("libretro", std::make_shared<LibRetroButtonFactory>());
  1539. +    RegisterFactory<AnalogDevice>("libretro", std::make_shared<LibRetroAxisFactory>());
  1540. +}
  1541. +
  1542. +void Shutdown() {
  1543. +    using namespace ::Input;
  1544. +    UnregisterFactory<ButtonDevice>("libretro");
  1545. +    UnregisterFactory<AnalogDevice>("libretro");
  1546. +}
  1547. +
  1548. +} // namespace Input
  1549. +} // namespace LibRetro
  1550. diff --git a/src/citra_libretro/input/input_factory.h b/src/citra_libretro/input/input_factory.h
  1551. new file mode 100644
  1552. index 00000000..f0591ce5
  1553. --- /dev/null
  1554. +++ b/src/citra_libretro/input/input_factory.h
  1555. @@ -0,0 +1,20 @@
  1556. +// Copyright 2017 Citra Emulator Project
  1557. +// Licensed under GPLv2 or any later version
  1558. +// Refer to the license.txt file included.
  1559. +
  1560. +#pragma once
  1561. +
  1562. +#include "core/frontend/input.h"
  1563. +
  1564. +namespace LibRetro {
  1565. +
  1566. +namespace Input {
  1567. +
  1568. +/// Initializes and registers LibRetro device factories
  1569. +void Init();
  1570. +
  1571. +/// Unresisters LibRetro device factories and shut them down.
  1572. +void Shutdown();
  1573. +
  1574. +} // namespace Input
  1575. +} // namespace LibRetro
  1576. diff --git a/src/citra_libretro/input/mouse_tracker.cpp b/src/citra_libretro/input/mouse_tracker.cpp
  1577. new file mode 100644
  1578. index 00000000..bca078b5
  1579. --- /dev/null
  1580. +++ b/src/citra_libretro/input/mouse_tracker.cpp
  1581. @@ -0,0 +1,213 @@
  1582. +// Copyright 2017 Citra Emulator Project
  1583. +// Licensed under GPLv2 or any later version
  1584. +// Refer to the license.txt file included.
  1585. +
  1586. +#include <cmath>
  1587. +
  1588. +#include "glad/glad.h"
  1589. +
  1590. +#include "citra_libretro/core_settings.h"
  1591. +#include "citra_libretro/environment.h"
  1592. +#include "citra_libretro/input/mouse_tracker.h"
  1593. +
  1594. +#include "video_core/renderer_opengl/gl_shader_util.h"
  1595. +
  1596. +namespace LibRetro {
  1597. +
  1598. +namespace Input {
  1599. +
  1600. +MouseTracker::MouseTracker() {
  1601. +    // Could potentially also use Citra's built-in shaders, if they can be
  1602. +    //  wrangled to cooperate.
  1603. +    const GLchar* vertex = R"(
  1604. +        #version 330 core
  1605. +
  1606. +        in vec2 position;
  1607. +
  1608. +        void main()
  1609. +        {
  1610. +            gl_Position = vec4(position, 0.0, 1.0);
  1611. +        }
  1612. +    )";
  1613. +
  1614. +    const GLchar* fragment = R"(
  1615. +        #version 330 core
  1616. +
  1617. +        out vec4 color;
  1618. +
  1619. +        void main()
  1620. +        {
  1621. +            color = vec4(1.0, 1.0, 1.0, 1.0);
  1622. +        }
  1623. +    )";
  1624. +
  1625. +    vao.Create();
  1626. +    vbo.Create();
  1627. +
  1628. +    glBindVertexArray(vao.handle);
  1629. +    glBindBuffer(GL_ARRAY_BUFFER, vbo.handle);
  1630. +
  1631. +    shader.Create(vertex, fragment);
  1632. +
  1633. +    auto positionVariable = (GLuint)glGetAttribLocation(shader.handle, "position");
  1634. +    glEnableVertexAttribArray(positionVariable);
  1635. +
  1636. +    glVertexAttribPointer(positionVariable, 2, GL_FLOAT, GL_FALSE, 0, 0);
  1637. +}
  1638. +
  1639. +MouseTracker::~MouseTracker() {
  1640. +    shader.Release();
  1641. +    vao.Release();
  1642. +    vbo.Release();
  1643. +}
  1644. +
  1645. +void MouseTracker::OnMouseMove(int deltaX, int deltaY) {
  1646. +    x += deltaX;
  1647. +    y += deltaY;
  1648. +}
  1649. +
  1650. +void MouseTracker::Restrict(int minX, int minY, int maxX, int maxY) {
  1651. +    x = std::min(std::max(minX, x), maxX);
  1652. +    y = std::min(std::max(minY, y), maxY);
  1653. +}
  1654. +
  1655. +void MouseTracker::Update(int bufferWidth, int bufferHeight,
  1656. +                          Common::Rectangle<unsigned> bottomScreen) {
  1657. +    // Check mouse input
  1658. +    bool state =
  1659. +        (bool)(LibRetro::CheckInput(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT)) ||
  1660. +        (bool)(LibRetro::CheckInput(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3));
  1661. +
  1662. +    // TODO: Provide config option for ratios here
  1663. +    auto widthSpeed = (bottomScreen.GetWidth() / 20.0);
  1664. +    auto heightSpeed = (bottomScreen.GetHeight() / 20.0);
  1665. +
  1666. +    // Read in and convert pointer values to absolute values on the canvas
  1667. +    auto pointerX = LibRetro::CheckInput(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X);
  1668. +    auto pointerY = LibRetro::CheckInput(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y);
  1669. +    auto newX = static_cast<int>((pointerX + 0x7fff) / (float)(0x7fff * 2) * bufferWidth);
  1670. +    auto newY = static_cast<int>((pointerY + 0x7fff) / (float)(0x7fff * 2) * bufferHeight);
  1671. +
  1672. +    if ((pointerX != 0 || pointerY != 0) && (newX != lastMouseX || newY != lastMouseY)) {
  1673. +        // Use mouse pointer movement
  1674. +        lastMouseX = newX;
  1675. +        lastMouseY = newY;
  1676. +
  1677. +        x = std::max(static_cast<int>(bottomScreen.left),
  1678. +                     std::min(newX, static_cast<int>(bottomScreen.right))) -
  1679. +            bottomScreen.left;
  1680. +        y = std::max(static_cast<int>(bottomScreen.top),
  1681. +                     std::min(newY, static_cast<int>(bottomScreen.bottom))) -
  1682. +            bottomScreen.top;
  1683. +    } else if (LibRetro::settings.analog_function != LibRetro::CStickFunction::CStick) {
  1684. +        // Use controller movement
  1685. +        float controllerX =
  1686. +            ((float)LibRetro::CheckInput(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT,
  1687. +                                         RETRO_DEVICE_ID_ANALOG_X) /
  1688. +             INT16_MAX);
  1689. +        float controllerY =
  1690. +            ((float)LibRetro::CheckInput(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT,
  1691. +                                         RETRO_DEVICE_ID_ANALOG_Y) /
  1692. +             INT16_MAX);
  1693. +
  1694. +        // Deadzone the controller inputs
  1695. +        float smoothedX = std::abs(controllerX);
  1696. +        float smoothedY = std::abs(controllerY);
  1697. +
  1698. +        if (smoothedX < LibRetro::settings.deadzone) {
  1699. +            controllerX = 0;
  1700. +        }
  1701. +        if (smoothedY < LibRetro::settings.deadzone) {
  1702. +            controllerY = 0;
  1703. +        }
  1704. +
  1705. +        OnMouseMove(static_cast<int>(controllerX * widthSpeed),
  1706. +                    static_cast<int>(controllerY * heightSpeed));
  1707. +    }
  1708. +
  1709. +    Restrict(0, 0, bottomScreen.GetWidth(), bottomScreen.GetHeight());
  1710. +
  1711. +    // Make the coordinates 0 -> 1
  1712. +    projectedX = (float)x / bottomScreen.GetWidth();
  1713. +    projectedY = (float)y / bottomScreen.GetHeight();
  1714. +
  1715. +    // Ensure that the projected position doesn't overlap outside the bottom screen framebuffer.
  1716. +    // TODO: Provide config option
  1717. +    renderRatio = (float)bottomScreen.GetHeight() / 30;
  1718. +
  1719. +    // Map the mouse coord to the bottom screen's position
  1720. +    projectedX = bottomScreen.left + projectedX * bottomScreen.GetWidth();
  1721. +    projectedY = bottomScreen.top + projectedY * bottomScreen.GetHeight();
  1722. +
  1723. +    isPressed = state;
  1724. +
  1725. +    this->bottomScreen = bottomScreen;
  1726. +}
  1727. +
  1728. +void MouseTracker::Render(int bufferWidth, int bufferHeight) {
  1729. +    // Convert to OpenGL coordinates
  1730. +    float centerX = (projectedX / bufferWidth) * 2 - 1;
  1731. +    float centerY = (projectedY / bufferHeight) * 2 - 1;
  1732. +
  1733. +    float renderWidth = renderRatio / bufferWidth;
  1734. +    float renderHeight = renderRatio / bufferHeight;
  1735. +
  1736. +    float boundingLeft = (bottomScreen.left / (float)bufferWidth) * 2 - 1;
  1737. +    float boundingTop = (bottomScreen.top / (float)bufferHeight) * 2 - 1;
  1738. +    float boundingRight = (bottomScreen.right / (float)bufferWidth) * 2 - 1;
  1739. +    float boundingBottom = (bottomScreen.bottom / (float)bufferHeight) * 2 - 1;
  1740. +
  1741. +    // Calculate the size of the vertical stalk
  1742. +    float verticalLeft = std::fmax(centerX - renderWidth / 5, boundingLeft);
  1743. +    float verticalRight = std::fmin(centerX + renderWidth / 5, boundingRight);
  1744. +    float verticalTop = -std::fmax(centerY - renderHeight, boundingTop);
  1745. +    float verticalBottom = -std::fmin(centerY + renderHeight, boundingBottom);
  1746. +
  1747. +    // Calculate the size of the horizontal stalk
  1748. +    float horizontalLeft = std::fmax(centerX - renderWidth, boundingLeft);
  1749. +    float horizontalRight = std::fmin(centerX + renderWidth, boundingRight);
  1750. +    float horizontalTop = -std::fmax(centerY - renderHeight / 5, boundingTop);
  1751. +    float horizontalBottom = -std::fmin(centerY + renderHeight / 5, boundingBottom);
  1752. +
  1753. +    glUseProgram(shader.handle);
  1754. +
  1755. +    glBindVertexArray(vao.handle);
  1756. +
  1757. +    // clang-format off
  1758. +    GLfloat cursor[] = {
  1759. +            // | in the cursor
  1760. +            verticalLeft,  verticalTop,
  1761. +            verticalRight, verticalTop,
  1762. +            verticalRight, verticalBottom,
  1763. +
  1764. +            verticalLeft,  verticalTop,
  1765. +            verticalRight, verticalBottom,
  1766. +            verticalLeft,  verticalBottom,
  1767. +
  1768. +            // - in the cursor
  1769. +            horizontalLeft,  horizontalTop,
  1770. +            horizontalRight, horizontalTop,
  1771. +            horizontalRight, horizontalBottom,
  1772. +
  1773. +            horizontalLeft,  horizontalTop,
  1774. +            horizontalRight, horizontalBottom,
  1775. +            horizontalLeft,  horizontalBottom
  1776. +    };
  1777. +    // clang-format on
  1778. +
  1779. +    glEnable(GL_BLEND);
  1780. +    glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
  1781. +
  1782. +    glBindBuffer(GL_ARRAY_BUFFER, vbo.handle);
  1783. +    glBufferData(GL_ARRAY_BUFFER, sizeof(cursor), cursor, GL_STATIC_DRAW);
  1784. +
  1785. +    glDrawArrays(GL_TRIANGLES, 0, 12);
  1786. +
  1787. +    glBindVertexArray(0);
  1788. +    glUseProgram(0);
  1789. +    glDisable(GL_BLEND);
  1790. +}
  1791. +
  1792. +} // namespace Input
  1793. +
  1794. +} // namespace LibRetro
  1795. diff --git a/src/citra_libretro/input/mouse_tracker.h b/src/citra_libretro/input/mouse_tracker.h
  1796. new file mode 100644
  1797. index 00000000..4a17ca25
  1798. --- /dev/null
  1799. +++ b/src/citra_libretro/input/mouse_tracker.h
  1800. @@ -0,0 +1,77 @@
  1801. +// Copyright 2017 Citra Emulator Project
  1802. +// Licensed under GPLv2 or any later version
  1803. +// Refer to the license.txt file included.
  1804. +
  1805. +#pragma once
  1806. +
  1807. +#include "common/math_util.h"
  1808. +#include <array>
  1809. +#include <glad/glad.h>
  1810. +#include "common/common_types.h"
  1811. +#include "core/hw/gpu.h"
  1812. +#include "video_core/renderer_base.h"
  1813. +#include "video_core/renderer_opengl/gl_resource_manager.h"
  1814. +#include "video_core/renderer_opengl/gl_state.h"
  1815. +
  1816. +
  1817. +#include "video_core/renderer_opengl/gl_resource_manager.h"
  1818. +#include "video_core/renderer_opengl/gl_state.h"
  1819. +
  1820. +
  1821. +namespace LibRetro {
  1822. +
  1823. +namespace Input {
  1824. +
  1825. +/// The mouse tracker provides a mechanism to handle relative mouse/joypad input
  1826. +///  for a touch-screen device.
  1827. +class MouseTracker {
  1828. +public:
  1829. +    MouseTracker();
  1830. +    ~MouseTracker();
  1831. +
  1832. +    /// Called whenever a mouse moves.
  1833. +    void OnMouseMove(int xDelta, int yDelta);
  1834. +
  1835. +    /// Restricts the mouse cursor to a specified rectangle.
  1836. +    void Restrict(int minX, int minY, int maxX, int maxY);
  1837. +
  1838. +    /// Updates the tracker.
  1839. +    void Update(int bufferWidth, int bufferHeight, Common::Rectangle<unsigned> bottomScreen);
  1840. +
  1841. +    /// Renders the cursor to the screen.
  1842. +    void Render(int bufferWidth, int bufferHeight);
  1843. +
  1844. +    /// If the touchscreen is being pressed.
  1845. +    bool IsPressed() {
  1846. +        return isPressed;
  1847. +    }
  1848. +
  1849. +    /// Get the pressed position, relative to the framebuffer.
  1850. +    std::pair<unsigned, unsigned> GetPressedPosition() {
  1851. +        return {static_cast<const unsigned int&>(projectedX),
  1852. +                static_cast<const unsigned int&>(projectedY)};
  1853. +    }
  1854. +
  1855. +private:
  1856. +    int x;
  1857. +    int y;
  1858. +
  1859. +    float lastMouseX;
  1860. +    float lastMouseY;
  1861. +
  1862. +    float projectedX;
  1863. +    float projectedY;
  1864. +    float renderRatio;
  1865. +
  1866. +    bool isPressed;
  1867. +
  1868. +    OpenGL::OGLProgram shader;
  1869. +    OpenGL::OGLVertexArray vao;
  1870. +    OpenGL::OGLBuffer vbo;
  1871. +
  1872. +    Common::Rectangle<unsigned> bottomScreen;
  1873. +};
  1874. +
  1875. +} // namespace Input
  1876. +
  1877. +} // namespace LibRetro
  1878. diff --git a/src/citra_libretro/libretro_logger.cpp b/src/citra_libretro/libretro_logger.cpp
  1879. new file mode 100644
  1880. index 00000000..d999dd69
  1881. --- /dev/null
  1882. +++ b/src/citra_libretro/libretro_logger.cpp
  1883. @@ -0,0 +1,48 @@
  1884. +// Copyright 2018 Citra Emulator Project
  1885. +// Licensed under GPLv2 or any later version
  1886. +// Refer to the license.txt file included.
  1887. +
  1888. +#include "common/logging/text_formatter.h"
  1889. +#include "common/assert.h"
  1890. +
  1891. +#include "libretro_logger.h"
  1892. +
  1893. +LibRetroLogger::LibRetroLogger(retro_log_printf_t callback) : callback(callback) {}
  1894. +
  1895. +const char *LibRetroLogger::GetName() const {
  1896. +    return "LibRetro";
  1897. +}
  1898. +
  1899. +void LibRetroLogger::Write(const Log::Entry &entry) {
  1900. +    retro_log_level log_level;
  1901. +
  1902. +    switch (entry.log_level) {
  1903. +        case Log::Level::Trace:
  1904. +            log_level = retro_log_level::RETRO_LOG_DEBUG;
  1905. +            break;
  1906. +        case Log::Level::Debug:
  1907. +            log_level = retro_log_level::RETRO_LOG_DEBUG;
  1908. +            break;
  1909. +        case Log::Level::Info:
  1910. +            log_level = retro_log_level::RETRO_LOG_INFO;
  1911. +            break;
  1912. +        case Log::Level::Warning:
  1913. +            log_level = retro_log_level::RETRO_LOG_WARN;
  1914. +            break;
  1915. +        case Log::Level::Error:
  1916. +            log_level = retro_log_level::RETRO_LOG_ERROR;
  1917. +            break;
  1918. +        case Log::Level::Critical:
  1919. +            log_level = retro_log_level::RETRO_LOG_ERROR;
  1920. +            break;
  1921. +        default:
  1922. +            UNREACHABLE();
  1923. +    }
  1924. +
  1925. +    const char* class_name = GetLogClassName(entry.log_class);
  1926. +
  1927. +    auto str = fmt::format("{} @ {}:{}:{}: {}\n", class_name, entry.filename, entry.function, entry.line_num,
  1928. +                       entry.message);
  1929. +
  1930. +    callback(log_level, str.c_str());
  1931. +}
  1932. diff --git a/src/citra_libretro/libretro_logger.h b/src/citra_libretro/libretro_logger.h
  1933. new file mode 100644
  1934. index 00000000..098ed742
  1935. --- /dev/null
  1936. +++ b/src/citra_libretro/libretro_logger.h
  1937. @@ -0,0 +1,21 @@
  1938. +// Copyright 2018 Citra Emulator Project
  1939. +// Licensed under GPLv2 or any later version
  1940. +// Refer to the license.txt file included.
  1941. +
  1942. +#pragma once
  1943. +
  1944. +#include "common/logging/backend.h"
  1945. +
  1946. +#include "libretro.h"
  1947. +
  1948. +class LibRetroLogger : public Log::Backend {
  1949. +public:
  1950. +    explicit LibRetroLogger(retro_log_printf_t callback);
  1951. +
  1952. +    const char *GetName() const override;
  1953. +
  1954. +    void Write(const Log::Entry &entry) override;
  1955. +
  1956. +private:
  1957. +    retro_log_printf_t callback;
  1958. +};
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement