mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2024-11-26 19:44:16 +01:00
Dropped lime3ds-cli
executable and SDL2 frontend
This commit is contained in:
parent
5bd98f2168
commit
07858a015e
@ -11,7 +11,7 @@ BASE_ARTIFACT_ARCH="${BASE_ARTIFACT##*-}"
|
||||
mv $BASE_ARTIFACT $BUNDLE_DIR
|
||||
|
||||
# Executable binary paths that need to be combined.
|
||||
BIN_PATHS=(lime3ds-cli lime3ds-room lime3ds-gui.app/Contents/MacOS/lime3ds-gui)
|
||||
BIN_PATHS=(lime3ds-room lime3ds-gui.app/Contents/MacOS/lime3ds-gui)
|
||||
|
||||
# Dylib paths that need to be combined.
|
||||
IFS=$'\n'
|
||||
@ -37,7 +37,7 @@ for OTHER_ARTIFACT in "${ARTIFACTS_LIST[@]:1}"; do
|
||||
done
|
||||
|
||||
# Re-sign executables and bundles after combining.
|
||||
APP_PATHS=(lime3ds-cli lime3ds-room lime3ds-gui.app)
|
||||
APP_PATHS=(lime3ds-room lime3ds-gui.app)
|
||||
for APP_PATH in "${APP_PATHS[@]}"; do
|
||||
codesign --deep -fs - $BUNDLE_DIR/$APP_PATH
|
||||
done
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -52,9 +52,6 @@
|
||||
[submodule "libyuv"]
|
||||
path = externals/libyuv
|
||||
url = https://github.com/lemenkov/libyuv.git
|
||||
[submodule "sdl2"]
|
||||
path = externals/sdl2/SDL
|
||||
url = https://github.com/libsdl-org/SDL
|
||||
[submodule "cryptopp-cmake"]
|
||||
path = externals/cryptopp-cmake
|
||||
url = https://github.com/abdes/cryptopp-cmake.git
|
||||
|
@ -56,10 +56,6 @@ else()
|
||||
set(DEFAULT_ENABLE_LTO OFF)
|
||||
endif()
|
||||
|
||||
option(ENABLE_SDL2 "Enable using SDL2" ON)
|
||||
CMAKE_DEPENDENT_OPTION(ENABLE_SDL2_FRONTEND "Enable the SDL2 frontend" ON "ENABLE_SDL2;NOT ANDROID" OFF)
|
||||
option(USE_SYSTEM_SDL2 "Use the system SDL2 lib (instead of the bundled one)" OFF)
|
||||
|
||||
# Set bundled qt as dependent options.
|
||||
option(ENABLE_QT "Enable the Qt frontend" ON)
|
||||
option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF)
|
||||
@ -397,15 +393,6 @@ if (NOT USE_SYSTEM_BOOST)
|
||||
add_library(Boost::iostreams ALIAS boost_iostreams)
|
||||
endif()
|
||||
|
||||
# SDL2
|
||||
if (ENABLE_SDL2 AND USE_SYSTEM_SDL2)
|
||||
find_package(SDL2 REQUIRED)
|
||||
add_library(SDL2 INTERFACE)
|
||||
target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARY}")
|
||||
target_include_directories(SDL2 INTERFACE "${SDL2_INCLUDE_DIR}")
|
||||
add_library(SDL2::SDL2 ALIAS SDL2)
|
||||
endif()
|
||||
|
||||
if (ENABLE_LIBUSB AND USE_SYSTEM_LIBUSB)
|
||||
include(FindPkgConfig)
|
||||
find_package(LibUSB)
|
||||
@ -426,17 +413,12 @@ add_subdirectory(dist/installer)
|
||||
# Set lime-qt project or lime project as default StartUp Project in Visual Studio depending on whether QT is enabled or not
|
||||
if(ENABLE_QT)
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT lime-qt)
|
||||
else()
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT lime)
|
||||
endif()
|
||||
|
||||
# Create target for outputting distributable bundles.
|
||||
# Not supported for mobile platforms as distributables are built differently.
|
||||
if (NOT ANDROID)
|
||||
include(BundleTarget)
|
||||
if (ENABLE_SDL2_FRONTEND)
|
||||
bundle_target(lime)
|
||||
endif()
|
||||
if (ENABLE_QT)
|
||||
bundle_target(lime-qt)
|
||||
endif()
|
||||
|
15
dist/lime3ds-cli.desktop
vendored
15
dist/lime3ds-cli.desktop
vendored
@ -1,15 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Name=Lime3DS
|
||||
GenericName=3DS Emulator
|
||||
GenericName[fr]=Émulateur 3DS
|
||||
Comment=Nintendo 3DS video game console emulator
|
||||
Comment[fr]=Émulateur de console de jeu Nintendo 3DS
|
||||
Icon=lime
|
||||
TryExec=lime3ds-cli
|
||||
Exec=lime3ds-cli %f
|
||||
Categories=Game;Emulator;
|
||||
MimeType=application/x-ctr-3dsx;application/x-ctr-cci;application/x-ctr-cia;application/x-ctr-cxi;
|
||||
Keywords=3DS;Nintendo;
|
||||
PrefersNonDefaultGPU=true
|
8
externals/CMakeLists.txt
vendored
8
externals/CMakeLists.txt
vendored
@ -183,14 +183,6 @@ endif()
|
||||
# Teakra
|
||||
add_subdirectory(teakra EXCLUDE_FROM_ALL)
|
||||
|
||||
# SDL2
|
||||
if (ENABLE_SDL2 AND NOT USE_SYSTEM_SDL2)
|
||||
if (MSVC)
|
||||
set (SDL_LIBC ON)
|
||||
endif()
|
||||
add_subdirectory(sdl2)
|
||||
endif()
|
||||
|
||||
# libusb
|
||||
if (ENABLE_LIBUSB AND NOT USE_SYSTEM_LIBUSB)
|
||||
add_subdirectory(libusb)
|
||||
|
25
externals/sdl2/CMakeLists.txt
vendored
25
externals/sdl2/CMakeLists.txt
vendored
@ -1,25 +0,0 @@
|
||||
# Configure static library build
|
||||
set(SDL_SHARED OFF CACHE BOOL "")
|
||||
set(SDL_STATIC ON CACHE BOOL "")
|
||||
|
||||
# Subsystems
|
||||
set(SDL_ATOMIC ON CACHE BOOL "")
|
||||
set(SDL_AUDIO ON CACHE BOOL "")
|
||||
set(SDL_VIDEO ON CACHE BOOL "")
|
||||
set(SDL_RENDER OFF CACHE BOOL "")
|
||||
set(SDL_EVENTS ON CACHE BOOL "")
|
||||
set(SDL_JOYSTICK ON CACHE BOOL "")
|
||||
set(SDL_HAPTIC OFF CACHE BOOL "")
|
||||
set(SDL_HIDAPI ON CACHE BOOL "")
|
||||
set(SDL_POWER OFF CACHE BOOL "")
|
||||
set(SDL_THREADS ON CACHE BOOL "")
|
||||
set(SDL_TIMERS ON CACHE BOOL "")
|
||||
set(SDL_FILE ON CACHE BOOL "")
|
||||
set(SDL_LOADSO ON CACHE BOOL "")
|
||||
set(SDL_CPUINFO ON CACHE BOOL "")
|
||||
set(SDL_FILESYSTEM OFF CACHE BOOL "")
|
||||
set(SDL_DLOPEN ON CACHE BOOL "")
|
||||
set(SDL_SENSOR OFF CACHE BOOL "")
|
||||
set(SDL_LOCALE OFF CACHE BOOL "")
|
||||
|
||||
add_subdirectory(SDL)
|
1
externals/sdl2/SDL
vendored
1
externals/sdl2/SDL
vendored
@ -1 +0,0 @@
|
||||
Subproject commit ba2f78a0069118a6c583f1fbf1420144ffa35bad
|
@ -180,10 +180,6 @@ if (ENABLE_TESTS)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
if (ENABLE_SDL2 AND ENABLE_SDL2_FRONTEND)
|
||||
add_subdirectory(lime)
|
||||
endif()
|
||||
|
||||
if (ENABLE_QT)
|
||||
add_subdirectory(lime_qt)
|
||||
endif()
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include "common/math_util.h"
|
||||
#include "common/param_package.h"
|
||||
#include "input_common/main.h"
|
||||
#include "input_common/sdl/sdl.h"
|
||||
#include "jni/input_manager.h"
|
||||
#include "jni/ndk_motion.h"
|
||||
|
||||
|
@ -36,7 +36,6 @@ add_library(audio_core STATIC
|
||||
time_stretch.cpp
|
||||
time_stretch.h
|
||||
|
||||
$<$<BOOL:${ENABLE_SDL2}>:sdl2_sink.cpp sdl2_sink.h>
|
||||
$<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h cubeb_input.cpp cubeb_input.h>
|
||||
$<$<BOOL:${ENABLE_OPENAL}>:openal_input.cpp openal_input.h openal_sink.cpp openal_sink.h>
|
||||
)
|
||||
@ -46,11 +45,6 @@ create_target_directory_groups(audio_core)
|
||||
target_link_libraries(audio_core PUBLIC lime_common lime_core)
|
||||
target_link_libraries(audio_core PRIVATE faad2 SoundTouch teakra)
|
||||
|
||||
if(ENABLE_SDL2)
|
||||
target_link_libraries(audio_core PRIVATE SDL2::SDL2)
|
||||
target_compile_definitions(audio_core PRIVATE HAVE_SDL2)
|
||||
endif()
|
||||
|
||||
if(ENABLE_CUBEB)
|
||||
target_link_libraries(audio_core PRIVATE cubeb)
|
||||
target_compile_definitions(audio_core PUBLIC HAVE_CUBEB)
|
||||
|
@ -1,108 +0,0 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <SDL.h>
|
||||
#include "audio_core/audio_types.h"
|
||||
#include "audio_core/sdl2_sink.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
struct SDL2Sink::Impl {
|
||||
unsigned int sample_rate = 0;
|
||||
|
||||
SDL_AudioDeviceID audio_device_id = 0;
|
||||
|
||||
std::function<void(s16*, std::size_t)> cb;
|
||||
|
||||
static void Callback(void* impl_, u8* buffer, int buffer_size_in_bytes);
|
||||
};
|
||||
|
||||
SDL2Sink::SDL2Sink(std::string device_name) : impl(std::make_unique<Impl>()) {
|
||||
if (SDL_Init(SDL_INIT_AUDIO) < 0) {
|
||||
LOG_CRITICAL(Audio_Sink, "SDL_Init(SDL_INIT_AUDIO) failed with: {}", SDL_GetError());
|
||||
impl->audio_device_id = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_AudioSpec desired_audiospec;
|
||||
SDL_zero(desired_audiospec);
|
||||
desired_audiospec.format = AUDIO_S16;
|
||||
desired_audiospec.channels = 2;
|
||||
desired_audiospec.freq = native_sample_rate;
|
||||
desired_audiospec.samples = 512;
|
||||
desired_audiospec.userdata = impl.get();
|
||||
desired_audiospec.callback = &Impl::Callback;
|
||||
|
||||
SDL_AudioSpec obtained_audiospec;
|
||||
SDL_zero(obtained_audiospec);
|
||||
|
||||
const char* device = nullptr;
|
||||
if (device_name != auto_device_name && !device_name.empty()) {
|
||||
device = device_name.c_str();
|
||||
}
|
||||
|
||||
impl->audio_device_id =
|
||||
SDL_OpenAudioDevice(device, false, &desired_audiospec, &obtained_audiospec, 0);
|
||||
if (impl->audio_device_id <= 0) {
|
||||
LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed with code {} for device \"{}\"",
|
||||
impl->audio_device_id, device_name);
|
||||
return;
|
||||
}
|
||||
|
||||
impl->sample_rate = obtained_audiospec.freq;
|
||||
|
||||
// SDL2 audio devices start out paused, unpause it:
|
||||
SDL_PauseAudioDevice(impl->audio_device_id, 0);
|
||||
}
|
||||
|
||||
SDL2Sink::~SDL2Sink() {
|
||||
if (impl->audio_device_id <= 0)
|
||||
return;
|
||||
|
||||
SDL_CloseAudioDevice(impl->audio_device_id);
|
||||
}
|
||||
|
||||
unsigned int SDL2Sink::GetNativeSampleRate() const {
|
||||
if (impl->audio_device_id <= 0)
|
||||
return native_sample_rate;
|
||||
|
||||
return impl->sample_rate;
|
||||
}
|
||||
|
||||
void SDL2Sink::SetCallback(std::function<void(s16*, std::size_t)> cb) {
|
||||
impl->cb = cb;
|
||||
}
|
||||
|
||||
void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes) {
|
||||
Impl* impl = reinterpret_cast<Impl*>(impl_);
|
||||
if (!impl || !impl->cb)
|
||||
return;
|
||||
|
||||
const std::size_t num_frames = buffer_size_in_bytes / (2 * sizeof(s16));
|
||||
|
||||
impl->cb(reinterpret_cast<s16*>(buffer), num_frames);
|
||||
}
|
||||
|
||||
std::vector<std::string> ListSDL2SinkDevices() {
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
||||
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem failed with: {}", SDL_GetError());
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::string> device_list;
|
||||
const int device_count = SDL_GetNumAudioDevices(0);
|
||||
for (int i = 0; i < device_count; ++i) {
|
||||
device_list.push_back(SDL_GetAudioDeviceName(i, 0));
|
||||
}
|
||||
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
|
||||
return device_list;
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
@ -1,29 +0,0 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include "audio_core/sink.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class SDL2Sink final : public Sink {
|
||||
public:
|
||||
explicit SDL2Sink(std::string device_id);
|
||||
~SDL2Sink() override;
|
||||
|
||||
unsigned int GetNativeSampleRate() const override;
|
||||
|
||||
void SetCallback(std::function<void(s16*, std::size_t)> cb) override;
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
std::vector<std::string> ListSDL2SinkDevices();
|
||||
|
||||
} // namespace AudioCore
|
@ -8,9 +8,6 @@
|
||||
#include <vector>
|
||||
#include "audio_core/null_sink.h"
|
||||
#include "audio_core/sink_details.h"
|
||||
#ifdef HAVE_SDL2
|
||||
#include "audio_core/sdl2_sink.h"
|
||||
#endif
|
||||
#ifdef HAVE_CUBEB
|
||||
#include "audio_core/cubeb_sink.h"
|
||||
#endif
|
||||
@ -36,13 +33,6 @@ constexpr std::array sink_details = {
|
||||
return std::make_unique<OpenALSink>(std::string(device_id));
|
||||
},
|
||||
&ListOpenALSinkDevices},
|
||||
#endif
|
||||
#ifdef HAVE_SDL2
|
||||
SinkDetails{SinkType::SDL2, "SDL2",
|
||||
[](std::string_view device_id) -> std::unique_ptr<Sink> {
|
||||
return std::make_unique<SDL2Sink>(std::string(device_id));
|
||||
},
|
||||
&ListSDL2SinkDevices},
|
||||
#endif
|
||||
SinkDetails{SinkType::Null, "None",
|
||||
[](std::string_view device_id) -> std::unique_ptr<Sink> {
|
||||
|
@ -10,8 +10,6 @@ add_library(input_common STATIC
|
||||
precompiled_headers.h
|
||||
touch_from_button.cpp
|
||||
touch_from_button.h
|
||||
sdl/sdl.cpp
|
||||
sdl/sdl.h
|
||||
udp/client.cpp
|
||||
udp/client.h
|
||||
udp/protocol.cpp
|
||||
@ -20,15 +18,6 @@ add_library(input_common STATIC
|
||||
udp/udp.h
|
||||
)
|
||||
|
||||
if(ENABLE_SDL2)
|
||||
target_sources(input_common PRIVATE
|
||||
sdl/sdl_impl.cpp
|
||||
sdl/sdl_impl.h
|
||||
)
|
||||
target_link_libraries(input_common PRIVATE SDL2::SDL2)
|
||||
target_compile_definitions(input_common PRIVATE HAVE_SDL2)
|
||||
endif()
|
||||
|
||||
if(ENABLE_LIBUSB)
|
||||
target_sources(input_common PRIVATE
|
||||
gcadapter/gc_adapter.cpp
|
||||
|
@ -13,8 +13,6 @@
|
||||
#include "input_common/keyboard.h"
|
||||
#include "input_common/main.h"
|
||||
#include "input_common/motion_emu.h"
|
||||
#include "input_common/sdl/sdl.h"
|
||||
#include "input_common/sdl/sdl_impl.h"
|
||||
#include "input_common/touch_from_button.h"
|
||||
#include "input_common/udp/udp.h"
|
||||
|
||||
@ -28,7 +26,6 @@ std::shared_ptr<GCAdapter::Adapter> gcadapter;
|
||||
static std::shared_ptr<Keyboard> keyboard;
|
||||
static std::shared_ptr<MotionEmu> motion_emu;
|
||||
static std::unique_ptr<CemuhookUDP::State> udp;
|
||||
static std::unique_ptr<SDL::State> sdl;
|
||||
|
||||
void Init() {
|
||||
#ifdef ENABLE_GCADAPTER
|
||||
@ -47,8 +44,6 @@ void Init() {
|
||||
Input::RegisterFactory<Input::TouchDevice>("touch_from_button",
|
||||
std::make_shared<TouchFromButtonFactory>());
|
||||
|
||||
sdl = SDL::Init();
|
||||
|
||||
udp = CemuhookUDP::Init();
|
||||
}
|
||||
|
||||
@ -66,7 +61,6 @@ void Shutdown() {
|
||||
motion_emu.reset();
|
||||
Input::UnregisterFactory<Input::TouchDevice>("emu_window");
|
||||
Input::UnregisterFactory<Input::TouchDevice>("touch_from_button");
|
||||
sdl.reset();
|
||||
udp.reset();
|
||||
}
|
||||
|
||||
@ -103,10 +97,6 @@ std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left,
|
||||
Common::ParamPackage GetControllerButtonBinds(const Common::ParamPackage& params, int button) {
|
||||
const auto native_button{static_cast<Settings::NativeButton::Values>(button)};
|
||||
const auto engine{params.Get("engine", "")};
|
||||
if (engine == "sdl") {
|
||||
return dynamic_cast<SDL::SDLState*>(sdl.get())->GetSDLControllerButtonBindByGUID(
|
||||
params.Get("guid", "0"), params.Get("port", 0), native_button);
|
||||
}
|
||||
#ifdef ENABLE_GCADAPTER
|
||||
if (engine == "gcpad") {
|
||||
return gcbuttons->GetGcTo3DSMappedButton(params.Get("port", 0), native_button);
|
||||
@ -118,10 +108,6 @@ Common::ParamPackage GetControllerButtonBinds(const Common::ParamPackage& params
|
||||
Common::ParamPackage GetControllerAnalogBinds(const Common::ParamPackage& params, int analog) {
|
||||
const auto native_analog{static_cast<Settings::NativeAnalog::Values>(analog)};
|
||||
const auto engine{params.Get("engine", "")};
|
||||
if (engine == "sdl") {
|
||||
return dynamic_cast<SDL::SDLState*>(sdl.get())->GetSDLControllerAnalogBindByGUID(
|
||||
params.Get("guid", "0"), params.Get("port", 0), native_analog);
|
||||
}
|
||||
#ifdef ENABLE_GCADAPTER
|
||||
if (engine == "gcpad") {
|
||||
return gcanalog->GetGcTo3DSMappedAnalog(params.Get("port", 0), native_analog);
|
||||
@ -142,9 +128,6 @@ namespace Polling {
|
||||
std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type) {
|
||||
std::vector<std::unique_ptr<DevicePoller>> pollers;
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
pollers = sdl->GetPollers(type);
|
||||
#endif
|
||||
#ifdef ENABLE_GCADAPTER
|
||||
switch (type) {
|
||||
case DeviceType::Analog:
|
||||
|
@ -1,19 +0,0 @@
|
||||
// Copyright 2018 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "input_common/sdl/sdl.h"
|
||||
#ifdef HAVE_SDL2
|
||||
#include "input_common/sdl/sdl_impl.h"
|
||||
#endif
|
||||
|
||||
namespace InputCommon::SDL {
|
||||
|
||||
std::unique_ptr<State> Init() {
|
||||
#ifdef HAVE_SDL2
|
||||
return std::make_unique<SDLState>();
|
||||
#else
|
||||
return std::make_unique<NullState>();
|
||||
#endif
|
||||
}
|
||||
} // namespace InputCommon::SDL
|
@ -1,44 +0,0 @@
|
||||
// Copyright 2018 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "core/frontend/input.h"
|
||||
#include "input_common/main.h"
|
||||
|
||||
union SDL_Event;
|
||||
|
||||
namespace Common {
|
||||
class ParamPackage;
|
||||
} // namespace Common
|
||||
|
||||
namespace InputCommon::Polling {
|
||||
class DevicePoller;
|
||||
enum class DeviceType;
|
||||
} // namespace InputCommon::Polling
|
||||
|
||||
namespace InputCommon::SDL {
|
||||
|
||||
class State {
|
||||
public:
|
||||
using Pollers = std::vector<std::unique_ptr<Polling::DevicePoller>>;
|
||||
|
||||
/// Unregisters SDL device factories and shut them down.
|
||||
virtual ~State() = default;
|
||||
|
||||
virtual Pollers GetPollers(Polling::DeviceType type) = 0;
|
||||
};
|
||||
|
||||
class NullState : public State {
|
||||
public:
|
||||
Pollers GetPollers(Polling::DeviceType type) override {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<State> Init();
|
||||
|
||||
} // namespace InputCommon::SDL
|
File diff suppressed because it is too large
Load Diff
@ -1,74 +0,0 @@
|
||||
// Copyright 2018 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include "common/settings.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "input_common/sdl/sdl.h"
|
||||
|
||||
union SDL_Event;
|
||||
using SDL_Joystick = struct _SDL_Joystick;
|
||||
using SDL_JoystickID = s32;
|
||||
using SDL_GameController = struct _SDL_GameController;
|
||||
|
||||
namespace InputCommon::SDL {
|
||||
|
||||
class SDLJoystick;
|
||||
class SDLGameController;
|
||||
class SDLButtonFactory;
|
||||
class SDLAnalogFactory;
|
||||
class SDLMotionFactory;
|
||||
|
||||
class SDLState : public State {
|
||||
public:
|
||||
/// Initializes and registers SDL device factories
|
||||
SDLState();
|
||||
|
||||
/// Unregisters SDL device factories and shut them down.
|
||||
~SDLState() override;
|
||||
|
||||
/// Handle SDL_Events for joysticks from SDL_PollEvent
|
||||
void HandleGameControllerEvent(const SDL_Event& event);
|
||||
|
||||
std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id);
|
||||
std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port);
|
||||
|
||||
Common::ParamPackage GetSDLControllerButtonBindByGUID(const std::string& guid, int port,
|
||||
Settings::NativeButton::Values button);
|
||||
Common::ParamPackage GetSDLControllerAnalogBindByGUID(const std::string& guid, int port,
|
||||
Settings::NativeAnalog::Values analog);
|
||||
|
||||
/// Get all DevicePoller that use the SDL backend for a specific device type
|
||||
Pollers GetPollers(Polling::DeviceType type) override;
|
||||
|
||||
/// Used by the Pollers during config
|
||||
std::atomic<bool> polling = false;
|
||||
Common::SPSCQueue<SDL_Event> event_queue;
|
||||
|
||||
private:
|
||||
void InitJoystick(int joystick_index);
|
||||
void CloseJoystick(SDL_Joystick* sdl_joystick);
|
||||
|
||||
/// Needs to be called before SDL_QuitSubSystem.
|
||||
void CloseJoysticks();
|
||||
|
||||
/// Map of GUID of a list of corresponding virtual Joysticks
|
||||
std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
|
||||
std::mutex joystick_map_mutex;
|
||||
|
||||
std::shared_ptr<SDLButtonFactory> button_factory;
|
||||
std::shared_ptr<SDLAnalogFactory> analog_factory;
|
||||
std::shared_ptr<SDLMotionFactory> motion_factory;
|
||||
|
||||
bool start_thread = false;
|
||||
std::atomic<bool> initialized = false;
|
||||
|
||||
std::thread poll_thread;
|
||||
};
|
||||
} // namespace InputCommon::SDL
|
@ -1,61 +0,0 @@
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
|
||||
|
||||
add_executable(lime
|
||||
lime.cpp
|
||||
lime.rc
|
||||
config.cpp
|
||||
config.h
|
||||
default_ini.h
|
||||
emu_window/emu_window_sdl2.cpp
|
||||
emu_window/emu_window_sdl2.h
|
||||
precompiled_headers.h
|
||||
resource.h
|
||||
)
|
||||
|
||||
set_target_properties(lime PROPERTIES OUTPUT_NAME "lime3ds-cli")
|
||||
|
||||
if (ENABLE_SOFTWARE_RENDERER)
|
||||
target_sources(lime PRIVATE
|
||||
emu_window/emu_window_sdl2_sw.cpp
|
||||
emu_window/emu_window_sdl2_sw.h
|
||||
)
|
||||
endif()
|
||||
if (ENABLE_OPENGL)
|
||||
target_sources(lime PRIVATE
|
||||
emu_window/emu_window_sdl2_gl.cpp
|
||||
emu_window/emu_window_sdl2_gl.h
|
||||
)
|
||||
endif()
|
||||
if (ENABLE_VULKAN)
|
||||
target_sources(lime PRIVATE
|
||||
emu_window/emu_window_sdl2_vk.cpp
|
||||
emu_window/emu_window_sdl2_vk.h
|
||||
)
|
||||
endif()
|
||||
|
||||
create_target_directory_groups(lime)
|
||||
|
||||
target_link_libraries(lime PRIVATE lime_common lime_core input_common network)
|
||||
target_link_libraries(lime PRIVATE inih)
|
||||
if (MSVC)
|
||||
target_link_libraries(lime PRIVATE getopt)
|
||||
endif()
|
||||
target_link_libraries(lime PRIVATE ${PLATFORM_LIBRARIES} SDL2::SDL2 Threads::Threads)
|
||||
|
||||
if (ENABLE_OPENGL)
|
||||
target_link_libraries(lime PRIVATE glad)
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
install(TARGETS lime RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
|
||||
endif()
|
||||
|
||||
if (LIME3DS_USE_PRECOMPILED_HEADERS)
|
||||
target_precompile_headers(lime PRIVATE precompiled_headers.h)
|
||||
endif()
|
||||
|
||||
# Bundle in-place on MSVC so dependencies can be resolved by builds.
|
||||
if (MSVC)
|
||||
include(BundleTarget)
|
||||
bundle_target_in_place(lime)
|
||||
endif()
|
@ -1,383 +0,0 @@
|
||||
// Copyright Citra Emulator Project / Lime3DS Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
#include <INIReader.h>
|
||||
#include <SDL.h>
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "input_common/main.h"
|
||||
#include "input_common/udp/client.h"
|
||||
#include "lime/config.h"
|
||||
#include "lime/default_ini.h"
|
||||
#include "network/network_settings.h"
|
||||
|
||||
Config::Config() {
|
||||
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
|
||||
sdl2_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "sdl2-config.ini";
|
||||
sdl2_config = std::make_unique<INIReader>(sdl2_config_loc);
|
||||
|
||||
Reload();
|
||||
}
|
||||
|
||||
Config::~Config() = default;
|
||||
|
||||
bool Config::LoadINI(const std::string& default_contents, bool retry) {
|
||||
const std::string& location = this->sdl2_config_loc;
|
||||
if (sdl2_config->ParseError() < 0) {
|
||||
if (retry) {
|
||||
LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location);
|
||||
FileUtil::CreateFullPath(location);
|
||||
FileUtil::WriteStringToFile(true, location, default_contents);
|
||||
sdl2_config = std::make_unique<INIReader>(location); // Reopen file
|
||||
|
||||
return LoadINI(default_contents, false);
|
||||
}
|
||||
LOG_ERROR(Config, "Failed.");
|
||||
return false;
|
||||
}
|
||||
LOG_INFO(Config, "Successfully loaded {}", location);
|
||||
return true;
|
||||
}
|
||||
|
||||
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons = {
|
||||
SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_T, SDL_SCANCODE_G,
|
||||
SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_M, SDL_SCANCODE_N,
|
||||
SDL_SCANCODE_O, SDL_SCANCODE_P, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B,
|
||||
};
|
||||
|
||||
static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs{{
|
||||
{
|
||||
SDL_SCANCODE_UP,
|
||||
SDL_SCANCODE_DOWN,
|
||||
SDL_SCANCODE_LEFT,
|
||||
SDL_SCANCODE_RIGHT,
|
||||
SDL_SCANCODE_D,
|
||||
},
|
||||
{
|
||||
SDL_SCANCODE_I,
|
||||
SDL_SCANCODE_K,
|
||||
SDL_SCANCODE_J,
|
||||
SDL_SCANCODE_L,
|
||||
SDL_SCANCODE_D,
|
||||
},
|
||||
}};
|
||||
|
||||
template <>
|
||||
void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) {
|
||||
std::string setting_value = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault());
|
||||
if (setting_value.empty()) {
|
||||
setting_value = setting.GetDefault();
|
||||
}
|
||||
setting = std::move(setting_value);
|
||||
}
|
||||
|
||||
template <>
|
||||
void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& setting) {
|
||||
setting = sdl2_config->GetBoolean(group, setting.GetLabel(), setting.GetDefault());
|
||||
}
|
||||
|
||||
template <typename Type, bool ranged>
|
||||
void Config::ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting) {
|
||||
if constexpr (std::is_floating_point_v<Type>) {
|
||||
setting = static_cast<Type>(
|
||||
sdl2_config->GetReal(group, setting.GetLabel(), setting.GetDefault()));
|
||||
} else {
|
||||
setting = static_cast<Type>(sdl2_config->GetInteger(
|
||||
group, setting.GetLabel(), static_cast<long>(setting.GetDefault())));
|
||||
}
|
||||
}
|
||||
|
||||
void Config::ReadValues() {
|
||||
// Controls
|
||||
// TODO: add multiple input profile support
|
||||
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||
Settings::values.current_input_profile.buttons[i] =
|
||||
sdl2_config->GetString("Controls", Settings::NativeButton::mapping[i], default_param);
|
||||
if (Settings::values.current_input_profile.buttons[i].empty())
|
||||
Settings::values.current_input_profile.buttons[i] = default_param;
|
||||
}
|
||||
|
||||
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
|
||||
std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
|
||||
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
|
||||
default_analogs[i][3], default_analogs[i][4], 0.5f);
|
||||
Settings::values.current_input_profile.analogs[i] =
|
||||
sdl2_config->GetString("Controls", Settings::NativeAnalog::mapping[i], default_param);
|
||||
if (Settings::values.current_input_profile.analogs[i].empty())
|
||||
Settings::values.current_input_profile.analogs[i] = default_param;
|
||||
}
|
||||
|
||||
Settings::values.current_input_profile.motion_device = sdl2_config->GetString(
|
||||
"Controls", "motion_device",
|
||||
"engine:motion_emu,update_period:100,sensitivity:0.01,tilt_clamp:90.0");
|
||||
Settings::values.current_input_profile.touch_device =
|
||||
sdl2_config->GetString("Controls", "touch_device", "engine:emu_window");
|
||||
Settings::values.current_input_profile.udp_input_address = sdl2_config->GetString(
|
||||
"Controls", "udp_input_address", InputCommon::CemuhookUDP::DEFAULT_ADDR);
|
||||
Settings::values.current_input_profile.udp_input_port =
|
||||
static_cast<u16>(sdl2_config->GetInteger("Controls", "udp_input_port",
|
||||
InputCommon::CemuhookUDP::DEFAULT_PORT));
|
||||
|
||||
// Core
|
||||
ReadSetting("Core", Settings::values.use_cpu_jit);
|
||||
ReadSetting("Core", Settings::values.cpu_clock_percentage);
|
||||
|
||||
// Renderer
|
||||
ReadSetting("Renderer", Settings::values.graphics_api);
|
||||
ReadSetting("Renderer", Settings::values.physical_device);
|
||||
ReadSetting("Renderer", Settings::values.spirv_shader_gen);
|
||||
ReadSetting("Renderer", Settings::values.async_shader_compilation);
|
||||
ReadSetting("Renderer", Settings::values.async_presentation);
|
||||
ReadSetting("Renderer", Settings::values.use_gles);
|
||||
ReadSetting("Renderer", Settings::values.use_hw_shader);
|
||||
ReadSetting("Renderer", Settings::values.shaders_accurate_mul);
|
||||
ReadSetting("Renderer", Settings::values.use_shader_jit);
|
||||
ReadSetting("Renderer", Settings::values.resolution_factor);
|
||||
ReadSetting("Renderer", Settings::values.use_disk_shader_cache);
|
||||
ReadSetting("Renderer", Settings::values.frame_limit);
|
||||
ReadSetting("Renderer", Settings::values.use_vsync_new);
|
||||
ReadSetting("Renderer", Settings::values.texture_filter);
|
||||
ReadSetting("Renderer", Settings::values.texture_sampling);
|
||||
|
||||
ReadSetting("Renderer", Settings::values.mono_render_option);
|
||||
ReadSetting("Renderer", Settings::values.render_3d);
|
||||
ReadSetting("Renderer", Settings::values.factor_3d);
|
||||
ReadSetting("Renderer", Settings::values.pp_shader_name);
|
||||
ReadSetting("Renderer", Settings::values.anaglyph_shader_name);
|
||||
ReadSetting("Renderer", Settings::values.filter_mode);
|
||||
|
||||
ReadSetting("Renderer", Settings::values.bg_red);
|
||||
ReadSetting("Renderer", Settings::values.bg_green);
|
||||
ReadSetting("Renderer", Settings::values.bg_blue);
|
||||
|
||||
// Layout
|
||||
ReadSetting("Layout", Settings::values.layout_option);
|
||||
ReadSetting("Layout", Settings::values.swap_screen);
|
||||
ReadSetting("Layout", Settings::values.upright_screen);
|
||||
ReadSetting("Layout", Settings::values.large_screen_proportion);
|
||||
ReadSetting("Layout", Settings::values.custom_top_x);
|
||||
ReadSetting("Layout", Settings::values.custom_top_y);
|
||||
ReadSetting("Layout", Settings::values.custom_top_width);
|
||||
ReadSetting("Layout", Settings::values.custom_top_height);
|
||||
ReadSetting("Layout", Settings::values.custom_bottom_x);
|
||||
ReadSetting("Layout", Settings::values.custom_bottom_y);
|
||||
ReadSetting("Layout", Settings::values.custom_bottom_width);
|
||||
ReadSetting("Layout", Settings::values.custom_bottom_height);
|
||||
ReadSetting("Layout", Settings::values.custom_second_layer_opacity);
|
||||
|
||||
ReadSetting("Layout", Settings::values.screen_top_stretch);
|
||||
ReadSetting("Layout", Settings::values.screen_top_leftright_padding);
|
||||
ReadSetting("Layout", Settings::values.screen_top_topbottom_padding);
|
||||
ReadSetting("Layout", Settings::values.screen_bottom_stretch);
|
||||
ReadSetting("Layout", Settings::values.screen_bottom_leftright_padding);
|
||||
ReadSetting("Layout", Settings::values.screen_bottom_topbottom_padding);
|
||||
|
||||
ReadSetting("Layout", Settings::values.portrait_layout_option);
|
||||
ReadSetting("Layout", Settings::values.custom_portrait_top_x);
|
||||
ReadSetting("Layout", Settings::values.custom_portrait_top_y);
|
||||
ReadSetting("Layout", Settings::values.custom_portrait_top_width);
|
||||
ReadSetting("Layout", Settings::values.custom_portrait_top_height);
|
||||
ReadSetting("Layout", Settings::values.custom_portrait_bottom_x);
|
||||
ReadSetting("Layout", Settings::values.custom_portrait_bottom_y);
|
||||
ReadSetting("Layout", Settings::values.custom_portrait_bottom_width);
|
||||
ReadSetting("Layout", Settings::values.custom_portrait_bottom_height);
|
||||
|
||||
// Utility
|
||||
ReadSetting("Utility", Settings::values.dump_textures);
|
||||
ReadSetting("Utility", Settings::values.custom_textures);
|
||||
ReadSetting("Utility", Settings::values.preload_textures);
|
||||
ReadSetting("Utility", Settings::values.async_custom_loading);
|
||||
|
||||
// Audio
|
||||
ReadSetting("Audio", Settings::values.audio_emulation);
|
||||
ReadSetting("Audio", Settings::values.enable_audio_stretching);
|
||||
ReadSetting("Audio", Settings::values.enable_realtime_audio);
|
||||
ReadSetting("Audio", Settings::values.volume);
|
||||
ReadSetting("Audio", Settings::values.output_type);
|
||||
ReadSetting("Audio", Settings::values.output_device);
|
||||
ReadSetting("Audio", Settings::values.input_type);
|
||||
ReadSetting("Audio", Settings::values.input_device);
|
||||
|
||||
// Data Storage
|
||||
ReadSetting("Data Storage", Settings::values.use_virtual_sd);
|
||||
ReadSetting("Data Storage", Settings::values.use_custom_storage);
|
||||
|
||||
if (Settings::values.use_custom_storage) {
|
||||
FileUtil::UpdateUserPath(FileUtil::UserPath::NANDDir,
|
||||
sdl2_config->GetString("Data Storage", "nand_directory", ""));
|
||||
FileUtil::UpdateUserPath(FileUtil::UserPath::SDMCDir,
|
||||
sdl2_config->GetString("Data Storage", "sdmc_directory", ""));
|
||||
}
|
||||
|
||||
// System
|
||||
ReadSetting("System", Settings::values.is_new_3ds);
|
||||
ReadSetting("System", Settings::values.lle_applets);
|
||||
ReadSetting("System", Settings::values.region_value);
|
||||
ReadSetting("System", Settings::values.init_clock);
|
||||
{
|
||||
std::tm t;
|
||||
t.tm_sec = 1;
|
||||
t.tm_min = 0;
|
||||
t.tm_hour = 0;
|
||||
t.tm_mday = 1;
|
||||
t.tm_mon = 0;
|
||||
t.tm_year = 100;
|
||||
t.tm_isdst = 0;
|
||||
std::istringstream string_stream(
|
||||
sdl2_config->GetString("System", "init_time", "2000-01-01 00:00:01"));
|
||||
string_stream >> std::get_time(&t, "%Y-%m-%d %H:%M:%S");
|
||||
if (string_stream.fail()) {
|
||||
LOG_ERROR(Config, "Failed To parse init_time. Using 2000-01-01 00:00:01");
|
||||
}
|
||||
Settings::values.init_time =
|
||||
std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::from_time_t(std::mktime(&t)).time_since_epoch())
|
||||
.count();
|
||||
}
|
||||
ReadSetting("System", Settings::values.init_ticks_type);
|
||||
ReadSetting("System", Settings::values.init_ticks_override);
|
||||
ReadSetting("System", Settings::values.plugin_loader_enabled);
|
||||
ReadSetting("System", Settings::values.allow_plugin_loader);
|
||||
|
||||
{
|
||||
constexpr const char* default_init_time_offset = "0 00:00:00";
|
||||
|
||||
std::string offset_string =
|
||||
sdl2_config->GetString("System", "init_time_offset", default_init_time_offset);
|
||||
|
||||
std::size_t sep_index = offset_string.find(' ');
|
||||
|
||||
if (sep_index == std::string::npos) {
|
||||
LOG_ERROR(Config, "Failed to parse init_time_offset. Using 0 00:00:00");
|
||||
offset_string = default_init_time_offset;
|
||||
|
||||
sep_index = offset_string.find(' ');
|
||||
}
|
||||
|
||||
std::string day_string = offset_string.substr(0, sep_index);
|
||||
long long days = 0;
|
||||
|
||||
try {
|
||||
days = std::stoll(day_string);
|
||||
} catch (std::logic_error&) {
|
||||
LOG_ERROR(Config, "Failed to parse days in init_time_offset. Using 0");
|
||||
days = 0;
|
||||
}
|
||||
|
||||
long long days_in_seconds = days * 86400;
|
||||
|
||||
std::tm t;
|
||||
t.tm_sec = 0;
|
||||
t.tm_min = 0;
|
||||
t.tm_hour = 0;
|
||||
t.tm_mday = 1;
|
||||
t.tm_mon = 0;
|
||||
t.tm_year = 100;
|
||||
t.tm_isdst = 0;
|
||||
|
||||
std::istringstream string_stream(offset_string.substr(sep_index + 1));
|
||||
string_stream >> std::get_time(&t, "%H:%M:%S");
|
||||
|
||||
if (string_stream.fail()) {
|
||||
LOG_ERROR(Config,
|
||||
"Failed to parse hours, minutes and seconds in init_time_offset. 00:00:00");
|
||||
}
|
||||
|
||||
auto time_offset =
|
||||
std::chrono::system_clock::from_time_t(std::mktime(&t)).time_since_epoch();
|
||||
|
||||
auto secs = std::chrono::duration_cast<std::chrono::seconds>(time_offset).count();
|
||||
|
||||
Settings::values.init_time_offset = static_cast<long long>(secs) + days_in_seconds;
|
||||
}
|
||||
|
||||
// Camera
|
||||
using namespace Service::CAM;
|
||||
Settings::values.camera_name[OuterRightCamera] =
|
||||
sdl2_config->GetString("Camera", "camera_outer_right_name", "blank");
|
||||
Settings::values.camera_config[OuterRightCamera] =
|
||||
sdl2_config->GetString("Camera", "camera_outer_right_config", "");
|
||||
Settings::values.camera_flip[OuterRightCamera] =
|
||||
sdl2_config->GetInteger("Camera", "camera_outer_right_flip", 0);
|
||||
Settings::values.camera_name[InnerCamera] =
|
||||
sdl2_config->GetString("Camera", "camera_inner_name", "blank");
|
||||
Settings::values.camera_config[InnerCamera] =
|
||||
sdl2_config->GetString("Camera", "camera_inner_config", "");
|
||||
Settings::values.camera_flip[InnerCamera] =
|
||||
sdl2_config->GetInteger("Camera", "camera_inner_flip", 0);
|
||||
Settings::values.camera_name[OuterLeftCamera] =
|
||||
sdl2_config->GetString("Camera", "camera_outer_left_name", "blank");
|
||||
Settings::values.camera_config[OuterLeftCamera] =
|
||||
sdl2_config->GetString("Camera", "camera_outer_left_config", "");
|
||||
Settings::values.camera_flip[OuterLeftCamera] =
|
||||
sdl2_config->GetInteger("Camera", "camera_outer_left_flip", 0);
|
||||
|
||||
// Miscellaneous
|
||||
ReadSetting("Miscellaneous", Settings::values.log_filter);
|
||||
|
||||
// Apply the log_filter setting as the logger has already been initialized
|
||||
// and doesn't pick up the filter on its own.
|
||||
Common::Log::Filter filter;
|
||||
filter.ParseFilterString(Settings::values.log_filter.GetValue());
|
||||
Common::Log::SetGlobalFilter(filter);
|
||||
|
||||
// Debugging
|
||||
Settings::values.record_frame_times =
|
||||
sdl2_config->GetBoolean("Debugging", "record_frame_times", false);
|
||||
ReadSetting("Debugging", Settings::values.renderer_debug);
|
||||
ReadSetting("Debugging", Settings::values.use_gdbstub);
|
||||
ReadSetting("Debugging", Settings::values.gdbstub_port);
|
||||
|
||||
for (const auto& service_module : Service::service_module_map) {
|
||||
bool use_lle = sdl2_config->GetBoolean("Debugging", "LLE\\" + service_module.name, false);
|
||||
Settings::values.lle_modules.emplace(service_module.name, use_lle);
|
||||
}
|
||||
|
||||
// Web Service
|
||||
NetSettings::values.web_api_url =
|
||||
sdl2_config->GetString("WebService", "web_api_url", "https://api.citra-emu.org");
|
||||
NetSettings::values.lime3ds_username =
|
||||
sdl2_config->GetString("WebService", "lime3ds_username", "");
|
||||
NetSettings::values.lime3ds_token = sdl2_config->GetString("WebService", "lime3ds_token", "");
|
||||
|
||||
// Video Dumping
|
||||
Settings::values.output_format =
|
||||
sdl2_config->GetString("Video Dumping", "output_format", "webm");
|
||||
Settings::values.format_options = sdl2_config->GetString("Video Dumping", "format_options", "");
|
||||
|
||||
Settings::values.video_encoder =
|
||||
sdl2_config->GetString("Video Dumping", "video_encoder", "libvpx-vp9");
|
||||
|
||||
// Options for variable bit rate live streaming taken from here:
|
||||
// https://developers.google.com/media/vp9/live-encoding
|
||||
std::string default_video_options;
|
||||
if (Settings::values.video_encoder == "libvpx-vp9") {
|
||||
default_video_options =
|
||||
"quality:realtime,speed:6,tile-columns:4,frame-parallel:1,threads:8,row-mt:1";
|
||||
}
|
||||
Settings::values.video_encoder_options =
|
||||
sdl2_config->GetString("Video Dumping", "video_encoder_options", default_video_options);
|
||||
Settings::values.video_bitrate =
|
||||
sdl2_config->GetInteger("Video Dumping", "video_bitrate", 2500000);
|
||||
|
||||
Settings::values.audio_encoder =
|
||||
sdl2_config->GetString("Video Dumping", "audio_encoder", "libvorbis");
|
||||
Settings::values.audio_encoder_options =
|
||||
sdl2_config->GetString("Video Dumping", "audio_encoder_options", "");
|
||||
Settings::values.audio_bitrate =
|
||||
sdl2_config->GetInteger("Video Dumping", "audio_bitrate", 64000);
|
||||
}
|
||||
|
||||
void Config::Reload() {
|
||||
LoadINI(DefaultINI::sdl2_config_file);
|
||||
ReadValues();
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "common/settings.h"
|
||||
|
||||
class INIReader;
|
||||
|
||||
class Config {
|
||||
std::unique_ptr<INIReader> sdl2_config;
|
||||
std::string sdl2_config_loc;
|
||||
|
||||
bool LoadINI(const std::string& default_contents = "", bool retry = true);
|
||||
void ReadValues();
|
||||
|
||||
public:
|
||||
Config();
|
||||
~Config();
|
||||
|
||||
void Reload();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Applies a value read from the sdl2_config to a Setting.
|
||||
*
|
||||
* @param group The name of the INI group
|
||||
* @param setting The yuzu setting to modify
|
||||
*/
|
||||
template <typename Type, bool ranged>
|
||||
void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting);
|
||||
};
|
@ -1,398 +0,0 @@
|
||||
// Copyright Citra Emulator Project / Lime3DS Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace DefaultINI {
|
||||
|
||||
const char* sdl2_config_file = R"(
|
||||
[Controls]
|
||||
# The input devices and parameters for each 3DS native input
|
||||
# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..."
|
||||
# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values
|
||||
|
||||
# for button input, the following devices are available:
|
||||
# - "keyboard" (default) for keyboard input. Required parameters:
|
||||
# - "code": the code of the key to bind
|
||||
# - "sdl" for joystick input using SDL. Required parameters:
|
||||
# - "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"
|
||||
# - "threshold"(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
|
||||
button_a=
|
||||
button_b=
|
||||
button_x=
|
||||
button_y=
|
||||
button_up=
|
||||
button_down=
|
||||
button_left=
|
||||
button_right=
|
||||
button_l=
|
||||
button_r=
|
||||
button_start=
|
||||
button_select=
|
||||
button_debug=
|
||||
button_gpio14=
|
||||
button_zl=
|
||||
button_zr=
|
||||
button_home=
|
||||
|
||||
# for analog input, the following devices are available:
|
||||
# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters:
|
||||
# - "up", "down", "left", "right": sub-devices for each direction.
|
||||
# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00"
|
||||
# - "modifier": sub-devices as a modifier.
|
||||
# - "modifier_scale": a float number representing the applied modifier scale to the analog input.
|
||||
# Must be in range of 0.0-1.0. Defaults to 0.5
|
||||
# - "sdl" for joystick input using SDL. Required parameters:
|
||||
# - "joystick": the index of the joystick to bind
|
||||
# - "axis_x": the index of the axis to bind as x-axis (default to 0)
|
||||
# - "axis_y": the index of the axis to bind as y-axis (default to 1)
|
||||
circle_pad=
|
||||
c_stick=
|
||||
|
||||
# for motion input, the following devices are available:
|
||||
# - "motion_emu" (default) for emulating motion input from mouse input. Required parameters:
|
||||
# - "update_period": update period in milliseconds (default to 100)
|
||||
# - "sensitivity": the coefficient converting mouse movement to tilting angle (default to 0.01)
|
||||
# - "tilt_clamp": the max value of the tilt angle in degrees (default to 90)
|
||||
# - "cemuhookudp" reads motion input from a udp server that uses cemuhook's udp protocol
|
||||
motion_device=
|
||||
|
||||
# for touch input, the following devices are available:
|
||||
# - "emu_window" (default) for emulating touch input from mouse input to the emulation window. No parameters required
|
||||
# - "cemuhookudp" reads touch input from a udp server that uses cemuhook's udp protocol
|
||||
# - "min_x", "min_y", "max_x", "max_y": defines the udp device's touch screen coordinate system
|
||||
touch_device=
|
||||
|
||||
# Most desktop operating systems do not expose a way to poll the motion state of the controllers
|
||||
# so as a way around it, cemuhook created a udp client/server protocol to broadcast the data directly
|
||||
# from a controller device to the client program. Lime3DS has a client that can connect and read
|
||||
# from any cemuhook compatible motion program.
|
||||
|
||||
# IPv4 address of the udp input server (Default "127.0.0.1")
|
||||
udp_input_address=
|
||||
|
||||
# Port of the udp input server. (Default 26760)
|
||||
udp_input_port=
|
||||
|
||||
# The pad to request data on. Should be between 0 (Pad 1) and 3 (Pad 4). (Default 0)
|
||||
udp_pad_index=
|
||||
|
||||
[Core]
|
||||
# Whether to use the Just-In-Time (JIT) compiler for CPU emulation
|
||||
# 0: Interpreter (slow), 1 (default): JIT (fast)
|
||||
use_cpu_jit =
|
||||
|
||||
# Change the Clock Frequency of the emulated 3DS CPU.
|
||||
# Underclocking can increase the performance of the game at the risk of freezing.
|
||||
# Overclocking may fix lag that happens on console, but also comes with the risk of freezing.
|
||||
# Range is any positive integer (but we suspect 25 - 400 is a good idea) Default is 100
|
||||
cpu_clock_percentage =
|
||||
|
||||
[Renderer]
|
||||
# Whether to render using OpenGL or Software
|
||||
# 0: Software, 1: OpenGL (default), 2: Vulkan
|
||||
graphics_api =
|
||||
|
||||
# Whether to render using GLES or OpenGL
|
||||
# 0 (default): OpenGL, 1: GLES
|
||||
use_gles =
|
||||
|
||||
# Whether to use hardware shaders to emulate 3DS shaders
|
||||
# 0: Software, 1 (default): Hardware
|
||||
use_hw_shader =
|
||||
|
||||
# Whether to use accurate multiplication in hardware shaders
|
||||
# 0: Off (Faster, but causes issues in some games) 1: On (Default. Slower, but correct)
|
||||
shaders_accurate_mul =
|
||||
|
||||
# Whether to use the Just-In-Time (JIT) compiler for shader emulation
|
||||
# 0: Interpreter (slow), 1 (default): JIT (fast)
|
||||
use_shader_jit =
|
||||
|
||||
# Forces VSync on the display thread. Usually doesn't impact performance, but on some drivers it can
|
||||
# so only turn this off if you notice a speed difference.
|
||||
# 0: Off, 1 (default): On
|
||||
use_vsync_new =
|
||||
|
||||
# Reduce stuttering by storing and loading generated shaders to disk
|
||||
# 0: Off, 1 (default. On)
|
||||
use_disk_shader_cache =
|
||||
|
||||
# Resolution scale factor
|
||||
# 0: Auto (scales resolution to window size), 1: Native 3DS screen resolution, Otherwise a scale
|
||||
# factor for the 3DS resolution
|
||||
resolution_factor =
|
||||
|
||||
# Texture filter
|
||||
# 0: None, 1: Anime4K, 2: Bicubic, 3: Nearest Neighbor, 4: ScaleForce, 5: xBRZ
|
||||
texture_filter =
|
||||
|
||||
# Limits the speed of the game to run no faster than this value as a percentage of target speed.
|
||||
# Will not have an effect if unthrottled is enabled.
|
||||
# 5 - 995: Speed limit as a percentage of target game speed. 0 for unthrottled. 100 (default)
|
||||
frame_limit =
|
||||
|
||||
# Overrides the frame limiter to use frame_limit_alternate instead of frame_limit.
|
||||
# 0: Off (default), 1: On
|
||||
use_frame_limit_alternate =
|
||||
|
||||
# Alternate speed limit to be used instead of frame_limit if use_frame_limit_alternate is enabled
|
||||
# 5 - 995: Speed limit as a percentage of target game speed. 0 for unthrottled. 200 (default)
|
||||
frame_limit_alternate =
|
||||
|
||||
# The clear color for the renderer. What shows up on the sides of the bottom screen.
|
||||
# Must be in range of 0.0-1.0. Defaults to 0.0 for all.
|
||||
bg_red =
|
||||
bg_blue =
|
||||
bg_green =
|
||||
|
||||
# Whether and how Stereoscopic 3D should be rendered
|
||||
# 0 (default): Off, 1: Side by Side, 2: Reverse Side by Side, 3: Anaglyph, 4: Interlaced, 5: Reverse Interlaced
|
||||
render_3d =
|
||||
|
||||
# Change 3D Intensity
|
||||
# 0 - 100: Intensity. 0 (default)
|
||||
factor_3d =
|
||||
|
||||
# Change Default Eye to Render When in Monoscopic Mode
|
||||
# 0 (default): Left, 1: Right
|
||||
mono_render_option =
|
||||
|
||||
# The name of the post processing shader to apply.
|
||||
# Loaded from shaders if render_3d is off or side by side.
|
||||
pp_shader_name =
|
||||
|
||||
# The name of the shader to apply when render_3d is anaglyph.
|
||||
# Loaded from shaders/anaglyph
|
||||
anaglyph_shader_name =
|
||||
|
||||
# Whether to enable linear filtering or not
|
||||
# This is required for some shaders to work correctly
|
||||
# 0: Nearest, 1 (default): Linear
|
||||
filter_mode =
|
||||
|
||||
[Layout]
|
||||
# Layout for the screen inside the render window.
|
||||
# 0 (default): Default Above/Below Screen
|
||||
# 1: Single Screen Only
|
||||
# 2: Large Screen Small Screen
|
||||
# 3: Side by Side
|
||||
# 4: Separate Windows
|
||||
# 5: Hybrid Screen
|
||||
# 6: Custom Layout
|
||||
layout_option =
|
||||
|
||||
# Screen placement when using Custom layout option
|
||||
# 0x, 0y is the top left corner of the render window.
|
||||
custom_top_x =
|
||||
custom_top_y =
|
||||
custom_top_width =
|
||||
custom_top_height =
|
||||
custom_bottom_x =
|
||||
custom_bottom_y =
|
||||
custom_bottom_width =
|
||||
custom_bottom_height =
|
||||
|
||||
# Opacity of second layer when using custom layout option (bottom screen unless swapped)
|
||||
custom_second_layer_opacity =
|
||||
|
||||
# Swaps the prominent screen with the other screen.
|
||||
# For example, if Single Screen is chosen, setting this to 1 will display the bottom screen instead of the top screen.
|
||||
# 0 (default): Top Screen is prominent, 1: Bottom Screen is prominent
|
||||
swap_screen =
|
||||
|
||||
# Toggle upright orientation, for book style games.
|
||||
# 0 (default): Off, 1: On
|
||||
upright_screen =
|
||||
|
||||
# The proportion between the large and small screens when playing in Large Screen Small Screen layout.
|
||||
# Must be a real value between 1.0 and 16.0. Default is 4
|
||||
large_screen_proportion =
|
||||
|
||||
# Dumps textures as PNG to dump/textures/[Title ID]/.
|
||||
# 0 (default): Off, 1: On
|
||||
dump_textures =
|
||||
|
||||
# Reads PNG files from load/textures/[Title ID]/ and replaces textures.
|
||||
# 0 (default): Off, 1: On
|
||||
custom_textures =
|
||||
|
||||
# Loads all custom textures into memory before booting.
|
||||
# 0 (default): Off, 1: On
|
||||
preload_textures =
|
||||
|
||||
# Loads custom textures asynchronously with background threads.
|
||||
# 0: Off, 1 (default): On
|
||||
async_custom_loading =
|
||||
|
||||
[Audio]
|
||||
# Whether or not to enable DSP LLE
|
||||
# 0 (default): No, 1: Yes
|
||||
enable_dsp_lle =
|
||||
|
||||
# Whether or not to run DSP LLE on a different thread
|
||||
# 0 (default): No, 1: Yes
|
||||
enable_dsp_lle_thread =
|
||||
|
||||
# Whether or not to enable the audio-stretching post-processing effect.
|
||||
# This effect adjusts audio speed to match emulation speed and helps prevent audio stutter,
|
||||
# at the cost of increasing audio latency.
|
||||
# 0: No, 1 (default): Yes
|
||||
enable_audio_stretching =
|
||||
|
||||
# Scales audio playback speed to account for drops in emulation framerate
|
||||
# 0 (default): No, 1: Yes
|
||||
enable_realtime_audio =
|
||||
|
||||
# Output volume.
|
||||
# 1.0 (default): 100%, 0.0; mute
|
||||
volume =
|
||||
|
||||
# Which audio output type to use.
|
||||
# 0 (default): Auto-select, 1: No audio output, 2: Cubeb (if available), 3: OpenAL (if available), 4: SDL2 (if available)
|
||||
output_type =
|
||||
|
||||
# Which audio output device to use.
|
||||
# auto (default): Auto-select
|
||||
output_device =
|
||||
|
||||
# Which audio input type to use.
|
||||
# 0 (default): Auto-select, 1: No audio input, 2: Static noise, 3: Cubeb (if available), 4: OpenAL (if available)
|
||||
input_type =
|
||||
|
||||
# Which audio input device to use.
|
||||
# auto (default): Auto-select
|
||||
input_device =
|
||||
|
||||
[Data Storage]
|
||||
# Whether to create a virtual SD card.
|
||||
# 1 (default): Yes, 0: No
|
||||
use_virtual_sd =
|
||||
|
||||
# Whether to use custom storage locations
|
||||
# 1: Yes, 0 (default): No
|
||||
use_custom_storage =
|
||||
|
||||
# The path of the virtual SD card directory.
|
||||
# empty (default) will use the user_path
|
||||
sdmc_directory =
|
||||
|
||||
# The path of NAND directory.
|
||||
# empty (default) will use the user_path
|
||||
nand_directory =
|
||||
|
||||
[System]
|
||||
# The system model that Lime3DS will try to emulate
|
||||
# 0: Old 3DS, 1: New 3DS (default)
|
||||
is_new_3ds =
|
||||
|
||||
# Whether to use LLE system applets, if installed
|
||||
# 0 (default): No, 1: Yes
|
||||
lle_applets =
|
||||
|
||||
# The system region that Lime3DS will use during emulation
|
||||
# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
|
||||
region_value =
|
||||
|
||||
# The clock to use when lime3ds starts
|
||||
# 0: System clock (default), 1: fixed time
|
||||
init_clock =
|
||||
|
||||
# Time used when init_clock is set to fixed_time in the format %Y-%m-%d %H:%M:%S
|
||||
# set to fixed time. Default 2000-01-01 00:00:01
|
||||
# Note: 3DS can only handle times later then Jan 1 2000
|
||||
init_time =
|
||||
|
||||
# The system ticks count to use when lime3ds starts
|
||||
# 0: Random (default), 1: Fixed
|
||||
init_ticks_type =
|
||||
|
||||
# Tick count to use when init_ticks_type is set to Fixed.
|
||||
# Defaults to 0.
|
||||
init_ticks_override =
|
||||
|
||||
[Camera]
|
||||
# Which camera engine to use for the right outer camera
|
||||
# blank (default): a dummy camera that always returns black image
|
||||
camera_outer_right_name =
|
||||
|
||||
# A config string for the right outer camera. Its meaning is defined by the camera engine
|
||||
camera_outer_right_config =
|
||||
|
||||
# The image flip to apply
|
||||
# 0: None (default), 1: Horizontal, 2: Vertical, 3: Reverse
|
||||
camera_outer_right_flip =
|
||||
|
||||
# ... for the left outer camera
|
||||
camera_outer_left_name =
|
||||
camera_outer_left_config =
|
||||
camera_outer_left_flip =
|
||||
|
||||
# ... for the inner camera
|
||||
camera_inner_name =
|
||||
camera_inner_config =
|
||||
camera_inner_flip =
|
||||
|
||||
[Miscellaneous]
|
||||
# A filter which removes logs below a certain logging level.
|
||||
# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
|
||||
log_filter = *:Info
|
||||
|
||||
[Debugging]
|
||||
# Record frame time data, can be found in the log directory. Boolean value
|
||||
record_frame_times =
|
||||
|
||||
# Port for listening to GDB connections.
|
||||
use_gdbstub=false
|
||||
gdbstub_port=24689
|
||||
|
||||
# Whether to enable additional debugging information during emulation
|
||||
# 0 (default): Off, 1: On
|
||||
renderer_debug =
|
||||
|
||||
# To LLE a service module add "LLE\<module name>=true"
|
||||
|
||||
[WebService]
|
||||
# URL for Web API
|
||||
web_api_url = https://api.citra-emu.org
|
||||
# Username and token for Lime3DS Web Service
|
||||
# See https://profile.citra-emu.org/ for more info
|
||||
lime3ds_username =
|
||||
lime3ds_token =
|
||||
|
||||
[Video Dumping]
|
||||
# Format of the video to output, default: webm
|
||||
output_format =
|
||||
|
||||
# Options passed to the muxer (optional)
|
||||
# This is a param package, format: [key1]:[value1],[key2]:[value2],...
|
||||
format_options =
|
||||
|
||||
# Video encoder used, default: libvpx-vp9
|
||||
video_encoder =
|
||||
|
||||
# Options passed to the video codec (optional)
|
||||
video_encoder_options =
|
||||
|
||||
# Video bitrate, default: 2500000
|
||||
video_bitrate =
|
||||
|
||||
# Audio encoder used, default: libvorbis
|
||||
audio_encoder =
|
||||
|
||||
# Options passed to the audio codec (optional)
|
||||
audio_encoder_options =
|
||||
|
||||
# Audio bitrate, default: 64000
|
||||
audio_bitrate =
|
||||
)";
|
||||
}
|
@ -1,252 +0,0 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#define SDL_MAIN_HANDLED
|
||||
#include <SDL.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "core/core.h"
|
||||
#include "input_common/keyboard.h"
|
||||
#include "input_common/main.h"
|
||||
#include "input_common/motion_emu.h"
|
||||
#include "lime/emu_window/emu_window_sdl2.h"
|
||||
#include "network/network.h"
|
||||
|
||||
void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
|
||||
TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
|
||||
InputCommon::GetMotionEmu()->Tilt(x, y);
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
|
||||
if (button == SDL_BUTTON_LEFT) {
|
||||
if (state == SDL_PRESSED) {
|
||||
TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
|
||||
} else {
|
||||
TouchReleased();
|
||||
}
|
||||
} else if (button == SDL_BUTTON_RIGHT) {
|
||||
if (state == SDL_PRESSED) {
|
||||
InputCommon::GetMotionEmu()->BeginTilt(x, y);
|
||||
} else {
|
||||
InputCommon::GetMotionEmu()->EndTilt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<unsigned, unsigned> EmuWindow_SDL2::TouchToPixelPos(float touch_x, float touch_y) const {
|
||||
int w, h;
|
||||
SDL_GetWindowSize(render_window, &w, &h);
|
||||
|
||||
touch_x *= w;
|
||||
touch_y *= h;
|
||||
|
||||
return {static_cast<unsigned>(std::max(std::round(touch_x), 0.0f)),
|
||||
static_cast<unsigned>(std::max(std::round(touch_y), 0.0f))};
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnFingerDown(float x, float y) {
|
||||
// TODO(NeatNit): keep track of multitouch using the fingerID and a dictionary of some kind
|
||||
// This isn't critical because the best we can do when we have that is to average them, like the
|
||||
// 3DS does
|
||||
|
||||
const auto [px, py] = TouchToPixelPos(x, y);
|
||||
TouchPressed(px, py);
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnFingerMotion(float x, float y) {
|
||||
const auto [px, py] = TouchToPixelPos(x, y);
|
||||
TouchMoved(px, py);
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnFingerUp() {
|
||||
TouchReleased();
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
|
||||
if (state == SDL_PRESSED) {
|
||||
InputCommon::GetKeyboard()->PressKey(key);
|
||||
} else if (state == SDL_RELEASED) {
|
||||
InputCommon::GetKeyboard()->ReleaseKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
bool EmuWindow_SDL2::IsOpen() const {
|
||||
return is_open;
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::RequestClose() {
|
||||
is_open = false;
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnResize() {
|
||||
int width, height;
|
||||
SDL_GL_GetDrawableSize(render_window, &width, &height);
|
||||
UpdateCurrentFramebufferLayout(width, height);
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::Fullscreen() {
|
||||
if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_ERROR(Frontend, "Fullscreening failed: {}", SDL_GetError());
|
||||
|
||||
// Try a different fullscreening method
|
||||
LOG_INFO(Frontend, "Attempting to use borderless fullscreen...");
|
||||
if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_ERROR(Frontend, "Borderless fullscreening failed: {}", SDL_GetError());
|
||||
|
||||
// Fallback algorithm: Maximise window.
|
||||
// Works on all systems (unless something is seriously wrong), so no fallback for this one.
|
||||
LOG_INFO(Frontend, "Falling back on a maximised window...");
|
||||
SDL_MaximizeWindow(render_window);
|
||||
}
|
||||
|
||||
EmuWindow_SDL2::EmuWindow_SDL2(Core::System& system_, bool is_secondary)
|
||||
: EmuWindow(is_secondary), system(system_) {}
|
||||
|
||||
EmuWindow_SDL2::~EmuWindow_SDL2() {
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::InitializeSDL2() {
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) < 0) {
|
||||
LOG_CRITICAL(Frontend, "Failed to initialize SDL2: {}! Exiting...", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
InputCommon::Init();
|
||||
Network::Init();
|
||||
|
||||
SDL_SetMainReady();
|
||||
}
|
||||
|
||||
u32 EmuWindow_SDL2::GetEventWindowId(const SDL_Event& event) const {
|
||||
switch (event.type) {
|
||||
case SDL_WINDOWEVENT:
|
||||
return event.window.windowID;
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
return event.key.windowID;
|
||||
case SDL_MOUSEMOTION:
|
||||
return event.motion.windowID;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
return event.button.windowID;
|
||||
case SDL_MOUSEWHEEL:
|
||||
return event.wheel.windowID;
|
||||
case SDL_FINGERDOWN:
|
||||
case SDL_FINGERMOTION:
|
||||
case SDL_FINGERUP:
|
||||
return event.tfinger.windowID;
|
||||
case SDL_TEXTEDITING:
|
||||
return event.edit.windowID;
|
||||
case SDL_TEXTEDITING_EXT:
|
||||
return event.editExt.windowID;
|
||||
case SDL_TEXTINPUT:
|
||||
return event.text.windowID;
|
||||
case SDL_DROPBEGIN:
|
||||
case SDL_DROPFILE:
|
||||
case SDL_DROPTEXT:
|
||||
case SDL_DROPCOMPLETE:
|
||||
return event.drop.windowID;
|
||||
case SDL_USEREVENT:
|
||||
return event.user.windowID;
|
||||
default:
|
||||
// Event is not for any particular window, so we can just pretend it's for this one.
|
||||
return render_window_id;
|
||||
}
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::PollEvents() {
|
||||
SDL_Event event;
|
||||
std::vector<SDL_Event> other_window_events;
|
||||
|
||||
// SDL_PollEvent returns 0 when there are no more events in the event queue
|
||||
while (SDL_PollEvent(&event)) {
|
||||
if (GetEventWindowId(event) != render_window_id) {
|
||||
other_window_events.push_back(event);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
case SDL_WINDOWEVENT:
|
||||
switch (event.window.event) {
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
case SDL_WINDOWEVENT_MAXIMIZED:
|
||||
case SDL_WINDOWEVENT_RESTORED:
|
||||
case SDL_WINDOWEVENT_MINIMIZED:
|
||||
OnResize();
|
||||
break;
|
||||
case SDL_WINDOWEVENT_CLOSE:
|
||||
RequestClose();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state);
|
||||
break;
|
||||
case SDL_MOUSEMOTION:
|
||||
// ignore if it came from touch
|
||||
if (event.button.which != SDL_TOUCH_MOUSEID)
|
||||
OnMouseMotion(event.motion.x, event.motion.y);
|
||||
break;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
// ignore if it came from touch
|
||||
if (event.button.which != SDL_TOUCH_MOUSEID) {
|
||||
OnMouseButton(event.button.button, event.button.state, event.button.x,
|
||||
event.button.y);
|
||||
}
|
||||
break;
|
||||
case SDL_FINGERDOWN:
|
||||
OnFingerDown(event.tfinger.x, event.tfinger.y);
|
||||
break;
|
||||
case SDL_FINGERMOTION:
|
||||
OnFingerMotion(event.tfinger.x, event.tfinger.y);
|
||||
break;
|
||||
case SDL_FINGERUP:
|
||||
OnFingerUp();
|
||||
break;
|
||||
case SDL_QUIT:
|
||||
RequestClose();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (auto& e : other_window_events) {
|
||||
// This is a somewhat hacky workaround to re-emit window events meant for another window
|
||||
// since SDL_PollEvent() is global but we poll events per window.
|
||||
SDL_PushEvent(&e);
|
||||
}
|
||||
if (!is_secondary) {
|
||||
UpdateFramerateCounter();
|
||||
}
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) {
|
||||
SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second);
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::UpdateFramerateCounter() {
|
||||
const u32 current_time = SDL_GetTicks();
|
||||
if (current_time > last_time + 2000) {
|
||||
const auto results = system.GetAndResetPerfStats();
|
||||
const auto title =
|
||||
fmt::format("Lime3DS {} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname,
|
||||
Common::g_scm_branch, Common::g_scm_desc, results.game_fps,
|
||||
results.emulation_speed * 100.0f);
|
||||
SDL_SetWindowTitle(render_window, title.c_str());
|
||||
last_time = current_time;
|
||||
}
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include "common/common_types.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
|
||||
union SDL_Event;
|
||||
struct SDL_Window;
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
class EmuWindow_SDL2 : public Frontend::EmuWindow {
|
||||
public:
|
||||
explicit EmuWindow_SDL2(Core::System& system_, bool is_secondary);
|
||||
~EmuWindow_SDL2();
|
||||
|
||||
/// Initializes SDL2
|
||||
static void InitializeSDL2();
|
||||
|
||||
/// Presents the most recent frame from the video backend
|
||||
virtual void Present() {}
|
||||
|
||||
/// Polls window events
|
||||
void PollEvents() override;
|
||||
|
||||
/// Whether the window is still open, and a close request hasn't yet been sent
|
||||
bool IsOpen() const;
|
||||
|
||||
/// Close the window.
|
||||
void RequestClose();
|
||||
|
||||
protected:
|
||||
/// Gets the ID of the window an event originated from.
|
||||
u32 GetEventWindowId(const SDL_Event& event) const;
|
||||
|
||||
/// Called by PollEvents when a key is pressed or released.
|
||||
void OnKeyEvent(int key, u8 state);
|
||||
|
||||
/// Called by PollEvents when the mouse moves.
|
||||
void OnMouseMotion(s32 x, s32 y);
|
||||
|
||||
/// Called by PollEvents when a mouse button is pressed or released
|
||||
void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
|
||||
|
||||
/// Translates pixel position (0..1) to pixel positions
|
||||
std::pair<unsigned, unsigned> TouchToPixelPos(float touch_x, float touch_y) const;
|
||||
|
||||
/// Called by PollEvents when a finger starts touching the touchscreen
|
||||
void OnFingerDown(float x, float y);
|
||||
|
||||
/// Called by PollEvents when a finger moves while touching the touchscreen
|
||||
void OnFingerMotion(float x, float y);
|
||||
|
||||
/// Called by PollEvents when a finger stops touching the touchscreen
|
||||
void OnFingerUp();
|
||||
|
||||
/// Called by PollEvents when any event that may cause the window to be resized occurs
|
||||
void OnResize();
|
||||
|
||||
/// Called when user passes the fullscreen parameter flag
|
||||
void Fullscreen();
|
||||
|
||||
/// Called when a configuration change affects the minimal size of the window
|
||||
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
|
||||
|
||||
/// Called when polling to update framerate
|
||||
void UpdateFramerateCounter();
|
||||
|
||||
/// Is the window still open?
|
||||
bool is_open = true;
|
||||
|
||||
/// Internal SDL2 render window
|
||||
SDL_Window* render_window;
|
||||
|
||||
/// Internal SDL2 window ID
|
||||
u32 render_window_id{};
|
||||
|
||||
/// Fake hidden window for the core context
|
||||
SDL_Window* dummy_window;
|
||||
|
||||
/// Keeps track of how often to update the title bar during gameplay
|
||||
u32 last_time = 0;
|
||||
|
||||
Core::System& system;
|
||||
};
|
@ -1,167 +0,0 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#define SDL_MAIN_HANDLED
|
||||
#include <SDL.h>
|
||||
#include <glad/glad.h>
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "lime/emu_window/emu_window_sdl2_gl.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
|
||||
class SDLGLContext : public Frontend::GraphicsContext {
|
||||
public:
|
||||
using SDL_GLContext = void*;
|
||||
|
||||
SDLGLContext() {
|
||||
window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0,
|
||||
SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL);
|
||||
context = SDL_GL_CreateContext(window);
|
||||
}
|
||||
|
||||
~SDLGLContext() override {
|
||||
SDL_GL_DeleteContext(context);
|
||||
SDL_DestroyWindow(window);
|
||||
}
|
||||
|
||||
void MakeCurrent() override {
|
||||
SDL_GL_MakeCurrent(window, context);
|
||||
}
|
||||
|
||||
void DoneCurrent() override {
|
||||
SDL_GL_MakeCurrent(window, nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
SDL_Window* window;
|
||||
SDL_GLContext context;
|
||||
};
|
||||
|
||||
static SDL_Window* CreateGLWindow(const std::string& window_title, bool gles) {
|
||||
if (gles) {
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
||||
} else {
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
}
|
||||
return SDL_CreateWindow(window_title.c_str(),
|
||||
SDL_WINDOWPOS_UNDEFINED, // x position
|
||||
SDL_WINDOWPOS_UNDEFINED, // y position
|
||||
Core::kScreenTopWidth,
|
||||
Core::kScreenTopHeight + Core::kScreenBottomHeight,
|
||||
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
|
||||
}
|
||||
|
||||
EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system_, bool fullscreen, bool is_secondary)
|
||||
: EmuWindow_SDL2{system_, is_secondary} {
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
|
||||
// Enable context sharing for the shared context
|
||||
SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
|
||||
// Enable vsync
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
// Enable debug context
|
||||
if (Settings::values.renderer_debug) {
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
|
||||
}
|
||||
|
||||
std::string window_title = fmt::format("Lime3DS {} | {}-{}", Common::g_build_fullname,
|
||||
Common::g_scm_branch, Common::g_scm_desc);
|
||||
|
||||
// First, try to create a context with the requested type.
|
||||
render_window = CreateGLWindow(window_title, Settings::values.use_gles.GetValue());
|
||||
if (render_window == nullptr) {
|
||||
// On failure, fall back to context with flipped type.
|
||||
render_window = CreateGLWindow(window_title, !Settings::values.use_gles.GetValue());
|
||||
if (render_window == nullptr) {
|
||||
LOG_CRITICAL(Frontend, "Failed to create SDL2 window: {}", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
strict_context_required = std::strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0;
|
||||
|
||||
dummy_window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0,
|
||||
SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL);
|
||||
|
||||
if (fullscreen) {
|
||||
Fullscreen();
|
||||
}
|
||||
|
||||
window_context = SDL_GL_CreateContext(render_window);
|
||||
core_context = CreateSharedContext();
|
||||
last_saved_context = nullptr;
|
||||
|
||||
if (window_context == nullptr) {
|
||||
LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context: {}", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
if (core_context == nullptr) {
|
||||
LOG_CRITICAL(Frontend, "Failed to create shared SDL2 GL context: {}", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
render_window_id = SDL_GetWindowID(render_window);
|
||||
|
||||
int profile_mask = 0;
|
||||
SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profile_mask);
|
||||
auto gl_load_func =
|
||||
profile_mask == SDL_GL_CONTEXT_PROFILE_ES ? gladLoadGLES2Loader : gladLoadGLLoader;
|
||||
|
||||
if (!gl_load_func(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
|
||||
LOG_CRITICAL(Frontend, "Failed to initialize GL functions: {}", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
OnResize();
|
||||
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
|
||||
SDL_PumpEvents();
|
||||
}
|
||||
|
||||
EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() {
|
||||
core_context.reset();
|
||||
SDL_DestroyWindow(render_window);
|
||||
SDL_GL_DeleteContext(window_context);
|
||||
}
|
||||
|
||||
std::unique_ptr<Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const {
|
||||
return std::make_unique<SDLGLContext>();
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2_GL::MakeCurrent() {
|
||||
core_context->MakeCurrent();
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2_GL::DoneCurrent() {
|
||||
core_context->DoneCurrent();
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2_GL::SaveContext() {
|
||||
last_saved_context = SDL_GL_GetCurrentContext();
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2_GL::RestoreContext() {
|
||||
SDL_GL_MakeCurrent(render_window, last_saved_context);
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2_GL::Present() {
|
||||
SDL_GL_MakeCurrent(render_window, window_context);
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
while (IsOpen()) {
|
||||
system.GPU().Renderer().TryPresent(100, is_secondary);
|
||||
SDL_GL_SwapWindow(render_window);
|
||||
}
|
||||
SDL_GL_MakeCurrent(render_window, nullptr);
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "lime/emu_window/emu_window_sdl2.h"
|
||||
|
||||
struct SDL_Window;
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
class EmuWindow_SDL2_GL : public EmuWindow_SDL2 {
|
||||
public:
|
||||
explicit EmuWindow_SDL2_GL(Core::System& system_, bool fullscreen, bool is_secondary);
|
||||
~EmuWindow_SDL2_GL();
|
||||
|
||||
void Present() override;
|
||||
std::unique_ptr<GraphicsContext> CreateSharedContext() const override;
|
||||
void MakeCurrent() override;
|
||||
void DoneCurrent() override;
|
||||
void SaveContext() override;
|
||||
void RestoreContext() override;
|
||||
|
||||
private:
|
||||
using SDL_GLContext = void*;
|
||||
|
||||
/// The OpenGL context associated with the window
|
||||
SDL_GLContext window_context;
|
||||
|
||||
/// Used by SaveContext and RestoreContext
|
||||
SDL_GLContext last_saved_context;
|
||||
|
||||
/// The OpenGL context associated with the core
|
||||
std::unique_ptr<Frontend::GraphicsContext> core_context;
|
||||
};
|
@ -1,108 +0,0 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#define SDL_MAIN_HANDLED
|
||||
#include <SDL.h>
|
||||
#include <SDL_rect.h>
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "lime/emu_window/emu_window_sdl2_sw.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/renderer_software/renderer_software.h"
|
||||
|
||||
class DummyContext : public Frontend::GraphicsContext {};
|
||||
|
||||
EmuWindow_SDL2_SW::EmuWindow_SDL2_SW(Core::System& system_, bool fullscreen, bool is_secondary)
|
||||
: EmuWindow_SDL2{system_, is_secondary}, system{system_} {
|
||||
std::string window_title = fmt::format("Lime3DS {} | {}-{}", Common::g_build_fullname,
|
||||
Common::g_scm_branch, Common::g_scm_desc);
|
||||
render_window =
|
||||
SDL_CreateWindow(window_title.c_str(),
|
||||
SDL_WINDOWPOS_UNDEFINED, // x position
|
||||
SDL_WINDOWPOS_UNDEFINED, // y position
|
||||
Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight,
|
||||
SDL_WINDOW_SHOWN);
|
||||
|
||||
if (render_window == nullptr) {
|
||||
LOG_CRITICAL(Frontend, "Failed to create SDL2 window: {}", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
window_surface = SDL_GetWindowSurface(render_window);
|
||||
renderer = SDL_CreateSoftwareRenderer(window_surface);
|
||||
|
||||
if (renderer == nullptr) {
|
||||
LOG_CRITICAL(Frontend, "Failed to create SDL2 software renderer: {}", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (fullscreen) {
|
||||
Fullscreen();
|
||||
}
|
||||
|
||||
render_window_id = SDL_GetWindowID(render_window);
|
||||
|
||||
OnResize();
|
||||
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
|
||||
SDL_PumpEvents();
|
||||
}
|
||||
|
||||
EmuWindow_SDL2_SW::~EmuWindow_SDL2_SW() {
|
||||
SDL_DestroyRenderer(renderer);
|
||||
SDL_DestroyWindow(render_window);
|
||||
}
|
||||
|
||||
std::unique_ptr<Frontend::GraphicsContext> EmuWindow_SDL2_SW::CreateSharedContext() const {
|
||||
return std::make_unique<DummyContext>();
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2_SW::Present() {
|
||||
const auto layout{Layout::DefaultFrameLayout(
|
||||
Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight, false, false)};
|
||||
|
||||
using VideoCore::ScreenId;
|
||||
|
||||
while (IsOpen()) {
|
||||
SDL_SetRenderDrawColor(renderer,
|
||||
static_cast<Uint8>(Settings::values.bg_red.GetValue() * 255),
|
||||
static_cast<Uint8>(Settings::values.bg_green.GetValue() * 255),
|
||||
static_cast<Uint8>(Settings::values.bg_blue.GetValue() * 255), 0xFF);
|
||||
SDL_RenderClear(renderer);
|
||||
|
||||
const auto draw_screen = [&](ScreenId screen_id) {
|
||||
const auto dst_rect =
|
||||
screen_id == ScreenId::TopLeft ? layout.top_screen : layout.bottom_screen;
|
||||
SDL_Rect sdl_rect{static_cast<int>(dst_rect.left), static_cast<int>(dst_rect.top),
|
||||
static_cast<int>(dst_rect.GetWidth()),
|
||||
static_cast<int>(dst_rect.GetHeight())};
|
||||
SDL_Surface* screen = LoadFramebuffer(screen_id);
|
||||
SDL_BlitSurface(screen, nullptr, window_surface, &sdl_rect);
|
||||
SDL_FreeSurface(screen);
|
||||
};
|
||||
|
||||
draw_screen(ScreenId::TopLeft);
|
||||
draw_screen(ScreenId::Bottom);
|
||||
|
||||
SDL_RenderPresent(renderer);
|
||||
SDL_UpdateWindowSurface(render_window);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Surface* EmuWindow_SDL2_SW::LoadFramebuffer(VideoCore::ScreenId screen_id) {
|
||||
const auto& renderer = static_cast<SwRenderer::RendererSoftware&>(system.GPU().Renderer());
|
||||
const auto& info = renderer.Screen(screen_id);
|
||||
const int width = static_cast<int>(info.width);
|
||||
const int height = static_cast<int>(info.height);
|
||||
SDL_Surface* surface =
|
||||
SDL_CreateRGBSurfaceWithFormat(0, width, height, 0, SDL_PIXELFORMAT_ABGR8888);
|
||||
SDL_LockSurface(surface);
|
||||
std::memcpy(surface->pixels, info.pixels.data(), info.pixels.size());
|
||||
SDL_UnlockSurface(surface);
|
||||
return surface;
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "lime/emu_window/emu_window_sdl2.h"
|
||||
|
||||
struct SDL_Renderer;
|
||||
struct SDL_Surface;
|
||||
|
||||
namespace VideoCore {
|
||||
enum class ScreenId : u32;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
class EmuWindow_SDL2_SW : public EmuWindow_SDL2 {
|
||||
public:
|
||||
explicit EmuWindow_SDL2_SW(Core::System& system, bool fullscreen, bool is_secondary);
|
||||
~EmuWindow_SDL2_SW();
|
||||
|
||||
void Present() override;
|
||||
std::unique_ptr<GraphicsContext> CreateSharedContext() const override;
|
||||
void MakeCurrent() override {}
|
||||
void DoneCurrent() override {}
|
||||
|
||||
private:
|
||||
/// Loads a framebuffer to an SDL surface
|
||||
SDL_Surface* LoadFramebuffer(VideoCore::ScreenId screen_id);
|
||||
|
||||
/// The system class.
|
||||
Core::System& system;
|
||||
|
||||
/// The SDL software renderer
|
||||
SDL_Renderer* renderer;
|
||||
|
||||
/// The window surface
|
||||
SDL_Surface* window_surface;
|
||||
};
|
@ -1,90 +0,0 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <SDL.h>
|
||||
#include <SDL_syswm.h>
|
||||
#include <fmt/format.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "lime/emu_window/emu_window_sdl2_vk.h"
|
||||
|
||||
class DummyContext : public Frontend::GraphicsContext {};
|
||||
|
||||
EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen, bool is_secondary)
|
||||
: EmuWindow_SDL2{system, is_secondary} {
|
||||
const std::string window_title = fmt::format("Lime3DS {} | {}-{}", Common::g_build_fullname,
|
||||
Common::g_scm_branch, Common::g_scm_desc);
|
||||
render_window =
|
||||
SDL_CreateWindow(window_title.c_str(),
|
||||
SDL_WINDOWPOS_UNDEFINED, // x position
|
||||
SDL_WINDOWPOS_UNDEFINED, // y position
|
||||
Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight,
|
||||
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
|
||||
SDL_SysWMinfo wm;
|
||||
SDL_VERSION(&wm.version);
|
||||
if (SDL_GetWindowWMInfo(render_window, &wm) == SDL_FALSE) {
|
||||
LOG_CRITICAL(Frontend, "Failed to get information from the window manager");
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (fullscreen) {
|
||||
Fullscreen();
|
||||
SDL_ShowCursor(false);
|
||||
}
|
||||
|
||||
switch (wm.subsystem) {
|
||||
#ifdef SDL_VIDEO_DRIVER_WINDOWS
|
||||
case SDL_SYSWM_TYPE::SDL_SYSWM_WINDOWS:
|
||||
window_info.type = Frontend::WindowSystemType::Windows;
|
||||
window_info.render_surface = reinterpret_cast<void*>(wm.info.win.window);
|
||||
break;
|
||||
#endif
|
||||
#ifdef SDL_VIDEO_DRIVER_X11
|
||||
case SDL_SYSWM_TYPE::SDL_SYSWM_X11:
|
||||
window_info.type = Frontend::WindowSystemType::X11;
|
||||
window_info.display_connection = wm.info.x11.display;
|
||||
window_info.render_surface = reinterpret_cast<void*>(wm.info.x11.window);
|
||||
break;
|
||||
#endif
|
||||
#ifdef SDL_VIDEO_DRIVER_WAYLAND
|
||||
case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND:
|
||||
window_info.type = Frontend::WindowSystemType::Wayland;
|
||||
window_info.display_connection = wm.info.wl.display;
|
||||
window_info.render_surface = wm.info.wl.surface;
|
||||
break;
|
||||
#endif
|
||||
#ifdef SDL_VIDEO_DRIVER_COCOA
|
||||
case SDL_SYSWM_TYPE::SDL_SYSWM_COCOA:
|
||||
window_info.type = Frontend::WindowSystemType::MacOS;
|
||||
window_info.render_surface = SDL_Metal_GetLayer(SDL_Metal_CreateView(render_window));
|
||||
break;
|
||||
#endif
|
||||
#ifdef SDL_VIDEO_DRIVER_ANDROID
|
||||
case SDL_SYSWM_TYPE::SDL_SYSWM_ANDROID:
|
||||
window_info.type = Frontend::WindowSystemType::Android;
|
||||
window_info.render_surface = reinterpret_cast<void*>(wm.info.android.window);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
LOG_CRITICAL(Frontend, "Window manager subsystem {} not implemented", wm.subsystem);
|
||||
std::exit(EXIT_FAILURE);
|
||||
break;
|
||||
}
|
||||
|
||||
render_window_id = SDL_GetWindowID(render_window);
|
||||
|
||||
OnResize();
|
||||
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
|
||||
SDL_PumpEvents();
|
||||
}
|
||||
|
||||
EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() = default;
|
||||
|
||||
std::unique_ptr<Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const {
|
||||
return std::make_unique<DummyContext>();
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "lime/emu_window/emu_window_sdl2.h"
|
||||
|
||||
namespace Frontend {
|
||||
class GraphicsContext;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
|
||||
public:
|
||||
explicit EmuWindow_SDL2_VK(Core::System& system_, bool fullscreen, bool is_secondary);
|
||||
~EmuWindow_SDL2_VK() override;
|
||||
|
||||
std::unique_ptr<Frontend::GraphicsContext> CreateSharedContext() const override;
|
||||
};
|
@ -1,536 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
// This needs to be included before getopt.h because the latter #defines symbols used by it
|
||||
#include "common/microprofile.h"
|
||||
|
||||
#include "lime/config.h"
|
||||
#include "lime/emu_window/emu_window_sdl2.h"
|
||||
#ifdef ENABLE_OPENGL
|
||||
#include "lime/emu_window/emu_window_sdl2_gl.h"
|
||||
#endif
|
||||
#ifdef ENABLE_SOFTWARE_RENDERER
|
||||
#include "lime/emu_window/emu_window_sdl2_sw.h"
|
||||
#endif
|
||||
#ifdef ENABLE_VULKAN
|
||||
#include "lime/emu_window/emu_window_sdl2_vk.h"
|
||||
#endif
|
||||
#include "common/common_paths.h"
|
||||
#include "common/detached_tasks.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/dumping/backend.h"
|
||||
#include "core/dumping/ffmpeg_backend.h"
|
||||
#include "core/frontend/applets/default_applets.h"
|
||||
#include "core/frontend/framebuffer_layout.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/cfg/cfg.h"
|
||||
#include "core/movie.h"
|
||||
#include "input_common/main.h"
|
||||
#include "network/network.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
|
||||
#ifdef __unix__
|
||||
#include "common/linux/gamemode.h"
|
||||
#endif
|
||||
|
||||
#undef _UNICODE
|
||||
#include <getopt.h>
|
||||
#ifndef _MSC_VER
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
// windows.h needs to be included before shellapi.h
|
||||
#include <windows.h>
|
||||
|
||||
#include <shellapi.h>
|
||||
|
||||
extern "C" {
|
||||
// tells Nvidia drivers to use the dedicated GPU by default on laptops with switchable graphics
|
||||
__declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void PrintHelp(const char* argv0) {
|
||||
std::cout << "Usage: " << argv0
|
||||
<< " [options] <filename>\n"
|
||||
"-g, --gdbport=NUMBER Enable gdb stub on port NUMBER\n"
|
||||
"-i, --install=FILE Installs a specified CIA file\n"
|
||||
"-m, --multiplayer=nick:password@address:port"
|
||||
" Nickname, password, address and port for multiplayer\n"
|
||||
"-r, --movie-record=[file] Record a movie (game inputs) to the given file\n"
|
||||
"-a, --movie-record-author=AUTHOR Sets the author of the movie to be recorded\n"
|
||||
"-p, --movie-play=[file] Playback the movie (game inputs) from the given file\n"
|
||||
"-d, --dump-video=[file] Dumps audio and video to the given video file\n"
|
||||
"-f, --fullscreen Start in fullscreen mode\n"
|
||||
"-h, --help Display this help and exit\n"
|
||||
"-v, --version Output version information and exit\n";
|
||||
}
|
||||
|
||||
static void PrintVersion() {
|
||||
std::cout << "Lime3DS " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl;
|
||||
}
|
||||
|
||||
static void OnStateChanged(const Network::RoomMember::State& state) {
|
||||
switch (state) {
|
||||
case Network::RoomMember::State::Idle:
|
||||
LOG_DEBUG(Network, "Network is idle");
|
||||
break;
|
||||
case Network::RoomMember::State::Joining:
|
||||
LOG_DEBUG(Network, "Connection sequence to room started");
|
||||
break;
|
||||
case Network::RoomMember::State::Joined:
|
||||
LOG_DEBUG(Network, "Successfully joined to the room");
|
||||
break;
|
||||
case Network::RoomMember::State::Moderator:
|
||||
LOG_DEBUG(Network, "Successfully joined the room as a moderator");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void OnNetworkError(const Network::RoomMember::Error& error) {
|
||||
switch (error) {
|
||||
case Network::RoomMember::Error::LostConnection:
|
||||
LOG_DEBUG(Network, "Lost connection to the room");
|
||||
break;
|
||||
case Network::RoomMember::Error::CouldNotConnect:
|
||||
LOG_ERROR(Network, "Error: Could not connect");
|
||||
std::exit(1);
|
||||
break;
|
||||
case Network::RoomMember::Error::NameCollision:
|
||||
LOG_ERROR(
|
||||
Network,
|
||||
"You tried to use the same nickname as another user that is connected to the Room");
|
||||
std::exit(1);
|
||||
break;
|
||||
case Network::RoomMember::Error::MacCollision:
|
||||
LOG_ERROR(Network, "You tried to use the same MAC-Address as another user that is "
|
||||
"connected to the Room");
|
||||
std::exit(1);
|
||||
break;
|
||||
case Network::RoomMember::Error::ConsoleIdCollision:
|
||||
LOG_ERROR(Network, "Your Console ID conflicted with someone else in the Room");
|
||||
std::exit(1);
|
||||
break;
|
||||
case Network::RoomMember::Error::WrongPassword:
|
||||
LOG_ERROR(Network, "Room replied with: Wrong password");
|
||||
std::exit(1);
|
||||
break;
|
||||
case Network::RoomMember::Error::WrongVersion:
|
||||
LOG_ERROR(Network,
|
||||
"You are using a different version than the room you are trying to connect to");
|
||||
std::exit(1);
|
||||
break;
|
||||
case Network::RoomMember::Error::RoomIsFull:
|
||||
LOG_ERROR(Network, "The room is full");
|
||||
std::exit(1);
|
||||
break;
|
||||
case Network::RoomMember::Error::HostKicked:
|
||||
LOG_ERROR(Network, "You have been kicked by the host");
|
||||
break;
|
||||
case Network::RoomMember::Error::HostBanned:
|
||||
LOG_ERROR(Network, "You have been banned by the host");
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Network, "Unknown network error: {}", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void OnMessageReceived(const Network::ChatEntry& msg) {
|
||||
std::cout << std::endl << msg.nickname << ": " << msg.message << std::endl << std::endl;
|
||||
}
|
||||
|
||||
static void OnStatusMessageReceived(const Network::StatusMessageEntry& msg) {
|
||||
std::string message;
|
||||
switch (msg.type) {
|
||||
case Network::IdMemberJoin:
|
||||
message = fmt::format("{} has joined", msg.nickname);
|
||||
break;
|
||||
case Network::IdMemberLeave:
|
||||
message = fmt::format("{} has left", msg.nickname);
|
||||
break;
|
||||
case Network::IdMemberKicked:
|
||||
message = fmt::format("{} has been kicked", msg.nickname);
|
||||
break;
|
||||
case Network::IdMemberBanned:
|
||||
message = fmt::format("{} has been banned", msg.nickname);
|
||||
break;
|
||||
case Network::IdAddressUnbanned:
|
||||
message = fmt::format("{} has been unbanned", msg.nickname);
|
||||
break;
|
||||
}
|
||||
if (!message.empty())
|
||||
std::cout << std::endl << "* " << message << std::endl << std::endl;
|
||||
}
|
||||
|
||||
/// Application entry point
|
||||
int main(int argc, char** argv) {
|
||||
Common::Log::Initialize();
|
||||
Common::Log::SetColorConsoleBackendEnabled(true);
|
||||
Common::Log::Start();
|
||||
Common::DetachedTasks detached_tasks;
|
||||
Config config;
|
||||
int option_index = 0;
|
||||
bool use_gdbstub = Settings::values.use_gdbstub.GetValue();
|
||||
u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port.GetValue());
|
||||
std::string movie_record;
|
||||
std::string movie_record_author;
|
||||
std::string movie_play;
|
||||
std::string dump_video;
|
||||
|
||||
char* endarg;
|
||||
#ifdef _WIN32
|
||||
int argc_w;
|
||||
auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w);
|
||||
|
||||
if (argv_w == nullptr) {
|
||||
LOG_CRITICAL(Frontend, "Failed to get command line arguments");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
std::string filepath;
|
||||
|
||||
bool use_multiplayer = false;
|
||||
bool fullscreen = false;
|
||||
std::string nickname{};
|
||||
std::string password{};
|
||||
std::string address{};
|
||||
u16 port = Network::DefaultRoomPort;
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"gdbport", required_argument, 0, 'g'},
|
||||
{"install", required_argument, 0, 'i'},
|
||||
{"multiplayer", required_argument, 0, 'm'},
|
||||
{"movie-record", required_argument, 0, 'r'},
|
||||
{"movie-record-author", required_argument, 0, 'a'},
|
||||
{"movie-play", required_argument, 0, 'p'},
|
||||
{"dump-video", required_argument, 0, 'd'},
|
||||
{"fullscreen", no_argument, 0, 'f'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"version", no_argument, 0, 'v'},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
while (optind < argc) {
|
||||
int arg = getopt_long(argc, argv, "g:i:m:r:p:fhv", long_options, &option_index);
|
||||
if (arg != -1) {
|
||||
switch (static_cast<char>(arg)) {
|
||||
case 'g':
|
||||
errno = 0;
|
||||
gdb_port = strtoul(optarg, &endarg, 0);
|
||||
use_gdbstub = true;
|
||||
if (endarg == optarg)
|
||||
errno = EINVAL;
|
||||
if (errno != 0) {
|
||||
perror("--gdbport");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'i': {
|
||||
const auto cia_progress = [](std::size_t written, std::size_t total) {
|
||||
LOG_INFO(Frontend, "{:02d}%", (written * 100 / total));
|
||||
};
|
||||
if (Service::AM::InstallCIA(std::string(optarg), cia_progress) !=
|
||||
Service::AM::InstallStatus::Success)
|
||||
errno = EINVAL;
|
||||
if (errno != 0)
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
case 'm': {
|
||||
use_multiplayer = true;
|
||||
const std::string str_arg(optarg);
|
||||
// regex to check if the format is nickname:password@ip:port
|
||||
// with optional :password
|
||||
const std::regex re("^([^:]+)(?::(.+))?@([^:]+)(?::([0-9]+))?$");
|
||||
if (!std::regex_match(str_arg, re)) {
|
||||
std::cout << "Wrong format for option --multiplayer\n";
|
||||
PrintHelp(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::smatch match;
|
||||
std::regex_search(str_arg, match, re);
|
||||
ASSERT(match.size() == 5);
|
||||
nickname = match[1];
|
||||
password = match[2];
|
||||
address = match[3];
|
||||
if (!match[4].str().empty())
|
||||
port = std::stoi(match[4]);
|
||||
std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$");
|
||||
if (!std::regex_match(nickname, nickname_re)) {
|
||||
std::cout
|
||||
<< "Nickname is not valid. Must be 4 to 20 alphanumeric characters.\n";
|
||||
return 0;
|
||||
}
|
||||
if (address.empty()) {
|
||||
std::cout << "Address to room must not be empty.\n";
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'r':
|
||||
movie_record = optarg;
|
||||
break;
|
||||
case 'a':
|
||||
movie_record_author = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
movie_play = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
dump_video = optarg;
|
||||
break;
|
||||
case 'f':
|
||||
fullscreen = true;
|
||||
LOG_INFO(Frontend, "Starting in fullscreen mode...");
|
||||
break;
|
||||
case 'h':
|
||||
PrintHelp(argv[0]);
|
||||
return 0;
|
||||
case 'v':
|
||||
PrintVersion();
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
#ifdef _WIN32
|
||||
filepath = Common::UTF16ToUTF8(argv_w[optind]);
|
||||
#else
|
||||
filepath = argv[optind];
|
||||
#endif
|
||||
optind++;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
LocalFree(argv_w);
|
||||
#endif
|
||||
|
||||
MicroProfileOnThreadCreate("EmuThread");
|
||||
SCOPE_EXIT({ MicroProfileShutdown(); });
|
||||
|
||||
if (filepath.empty()) {
|
||||
LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!movie_record.empty() && !movie_play.empty()) {
|
||||
LOG_CRITICAL(Frontend, "Cannot both play and record a movie");
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
auto& movie = system.Movie();
|
||||
|
||||
if (!movie_record.empty()) {
|
||||
movie.PrepareForRecording();
|
||||
}
|
||||
if (!movie_play.empty()) {
|
||||
movie.PrepareForPlayback(movie_play);
|
||||
}
|
||||
|
||||
// Apply the command line arguments
|
||||
Settings::values.gdbstub_port = gdb_port;
|
||||
Settings::values.use_gdbstub = use_gdbstub;
|
||||
system.ApplySettings();
|
||||
|
||||
// Register frontend applets
|
||||
Frontend::RegisterDefaultApplets(system);
|
||||
|
||||
EmuWindow_SDL2::InitializeSDL2();
|
||||
|
||||
const auto create_emu_window = [&](bool fullscreen,
|
||||
bool is_secondary) -> std::unique_ptr<EmuWindow_SDL2> {
|
||||
const auto graphics_api = Settings::values.graphics_api.GetValue();
|
||||
switch (graphics_api) {
|
||||
#ifdef ENABLE_OPENGL
|
||||
case Settings::GraphicsAPI::OpenGL:
|
||||
return std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen, is_secondary);
|
||||
#endif
|
||||
#ifdef ENABLE_VULKAN
|
||||
case Settings::GraphicsAPI::Vulkan:
|
||||
return std::make_unique<EmuWindow_SDL2_VK>(system, fullscreen, is_secondary);
|
||||
#endif
|
||||
#ifdef ENABLE_SOFTWARE_RENDERER
|
||||
case Settings::GraphicsAPI::Software:
|
||||
return std::make_unique<EmuWindow_SDL2_SW>(system, fullscreen, is_secondary);
|
||||
#endif
|
||||
default:
|
||||
LOG_CRITICAL(
|
||||
Frontend,
|
||||
"Unknown or unsupported graphics API {}, falling back to available default",
|
||||
graphics_api);
|
||||
#ifdef ENABLE_OPENGL
|
||||
return std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen, is_secondary);
|
||||
#elif ENABLE_VULKAN
|
||||
return std::make_unique<EmuWindow_SDL2_VK>(system, fullscreen, is_secondary);
|
||||
#elif ENABLE_SOFTWARE_RENDERER
|
||||
return std::make_unique<EmuWindow_SDL2_SW>(system, fullscreen, is_secondary);
|
||||
#else
|
||||
// TODO: Add a null renderer backend for this, perhaps.
|
||||
#error "At least one renderer must be enabled."
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
const auto emu_window{create_emu_window(fullscreen, false)};
|
||||
const bool use_secondary_window{
|
||||
Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows &&
|
||||
Settings::values.graphics_api.GetValue() != Settings::GraphicsAPI::Software};
|
||||
const auto secondary_window = use_secondary_window ? create_emu_window(false, true) : nullptr;
|
||||
|
||||
const auto scope = emu_window->Acquire();
|
||||
|
||||
LOG_INFO(Frontend, "Lime3DS Version: {} | {}-{}", Common::g_build_fullname,
|
||||
Common::g_scm_branch, Common::g_scm_desc);
|
||||
Settings::LogSettings();
|
||||
|
||||
const Core::System::ResultStatus load_result{
|
||||
system.Load(*emu_window, filepath, secondary_window.get())};
|
||||
|
||||
switch (load_result) {
|
||||
case Core::System::ResultStatus::ErrorGetLoader:
|
||||
LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", filepath);
|
||||
return -1;
|
||||
case Core::System::ResultStatus::ErrorLoader:
|
||||
LOG_CRITICAL(Frontend, "Failed to load ROM!");
|
||||
return -1;
|
||||
case Core::System::ResultStatus::ErrorLoader_ErrorEncrypted:
|
||||
LOG_CRITICAL(Frontend, "The game that you are trying to load must be decrypted before "
|
||||
"being used with Lime3DS. \n\n For more information on dumping and "
|
||||
"decrypting games, please refer to: "
|
||||
"https://web.archive.org/web/20240304210021/https://citra-emu.org/"
|
||||
"wiki/dumping-game-cartridges/");
|
||||
return -1;
|
||||
case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat:
|
||||
LOG_CRITICAL(Frontend, "Error while loading ROM: The ROM format is not supported.");
|
||||
return -1;
|
||||
case Core::System::ResultStatus::ErrorNotInitialized:
|
||||
LOG_CRITICAL(Frontend, "CPUCore not initialized");
|
||||
return -1;
|
||||
case Core::System::ResultStatus::ErrorSystemMode:
|
||||
LOG_CRITICAL(Frontend, "Failed to determine system mode!");
|
||||
return -1;
|
||||
case Core::System::ResultStatus::Success:
|
||||
break; // Expected case
|
||||
default:
|
||||
LOG_ERROR(Frontend, "Error while loading ROM: {}", system.GetStatusDetails());
|
||||
break;
|
||||
}
|
||||
|
||||
if (use_multiplayer) {
|
||||
if (auto member = Network::GetRoomMember().lock()) {
|
||||
member->BindOnChatMessageRecieved(OnMessageReceived);
|
||||
member->BindOnStatusMessageReceived(OnStatusMessageReceived);
|
||||
member->BindOnStateChanged(OnStateChanged);
|
||||
member->BindOnError(OnNetworkError);
|
||||
LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port,
|
||||
nickname);
|
||||
member->Join(nickname, Service::CFG::GetConsoleIdHash(system), address.c_str(), port, 0,
|
||||
Network::NoPreferredMac, password);
|
||||
} else {
|
||||
LOG_ERROR(Network, "Could not access RoomMember");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!movie_play.empty()) {
|
||||
auto metadata = movie.GetMovieMetadata(movie_play);
|
||||
LOG_INFO(Movie, "Author: {}", metadata.author);
|
||||
LOG_INFO(Movie, "Rerecord count: {}", metadata.rerecord_count);
|
||||
LOG_INFO(Movie, "Input count: {}", metadata.input_count);
|
||||
movie.StartPlayback(movie_play);
|
||||
}
|
||||
if (!movie_record.empty()) {
|
||||
movie.StartRecording(movie_record, movie_record_author);
|
||||
}
|
||||
if (!dump_video.empty() && DynamicLibrary::FFmpeg::LoadFFmpeg()) {
|
||||
auto& renderer = system.GPU().Renderer();
|
||||
const auto layout{
|
||||
Layout::FrameLayoutFromResolutionScale(renderer.GetResolutionScaleFactor())};
|
||||
auto dumper = std::make_shared<VideoDumper::FFmpegBackend>(renderer);
|
||||
if (dumper->StartDumping(dump_video, layout)) {
|
||||
system.RegisterVideoDumper(dumper);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __unix__
|
||||
Common::Linux::StartGamemode();
|
||||
#endif
|
||||
|
||||
std::thread main_render_thread([&emu_window] { emu_window->Present(); });
|
||||
std::thread secondary_render_thread([&secondary_window] {
|
||||
if (secondary_window) {
|
||||
secondary_window->Present();
|
||||
}
|
||||
});
|
||||
|
||||
std::atomic_bool stop_run;
|
||||
system.GPU().Renderer().Rasterizer()->LoadDiskResources(
|
||||
stop_run, [](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
|
||||
LOG_DEBUG(Frontend, "Loading stage {} progress {} {}", static_cast<u32>(stage), value,
|
||||
total);
|
||||
});
|
||||
|
||||
const auto secondary_is_open = [&secondary_window] {
|
||||
// if the secondary window isn't created, it shouldn't affect the main loop
|
||||
return secondary_window ? secondary_window->IsOpen() : true;
|
||||
};
|
||||
while (emu_window->IsOpen() && secondary_is_open()) {
|
||||
const auto result = system.RunLoop();
|
||||
|
||||
switch (result) {
|
||||
case Core::System::ResultStatus::ShutdownRequested:
|
||||
emu_window->RequestClose();
|
||||
break;
|
||||
case Core::System::ResultStatus::Success:
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Frontend, "Error in main run loop: {}", result, system.GetStatusDetails());
|
||||
break;
|
||||
}
|
||||
}
|
||||
emu_window->RequestClose();
|
||||
if (secondary_window) {
|
||||
secondary_window->RequestClose();
|
||||
}
|
||||
main_render_thread.join();
|
||||
secondary_render_thread.join();
|
||||
|
||||
movie.Shutdown();
|
||||
|
||||
auto video_dumper = system.GetVideoDumper();
|
||||
if (video_dumper && video_dumper->IsDumping()) {
|
||||
video_dumper->StopDumping();
|
||||
}
|
||||
|
||||
Network::Shutdown();
|
||||
InputCommon::Shutdown();
|
||||
|
||||
system.Shutdown();
|
||||
|
||||
#ifdef __unix__
|
||||
Common::Linux::StopGamemode();
|
||||
#endif
|
||||
|
||||
detached_tasks.WaitForAllTasks();
|
||||
return 0;
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
#include "winresrc.h"
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Icon
|
||||
//
|
||||
|
||||
// Icon with lowest ID value placed first to ensure application icon
|
||||
// remains consistent on all systems.
|
||||
LIME3DS_ICON ICON "../../dist/lime.ico"
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// RT_MANIFEST
|
||||
//
|
||||
|
||||
0 RT_MANIFEST "../../dist/lime.manifest"
|
@ -1,7 +0,0 @@
|
||||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_precompiled_headers.h"
|
@ -1,16 +0,0 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by pcafe.rc
|
||||
//
|
||||
#define IDI_ICON3 103
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 105
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
@ -285,11 +285,6 @@ elseif(WIN32)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_SDL2)
|
||||
target_link_libraries(lime-qt PRIVATE SDL2::SDL2)
|
||||
target_compile_definitions(lime-qt PRIVATE HAVE_SDL2)
|
||||
endif()
|
||||
|
||||
create_target_directory_groups(lime-qt)
|
||||
|
||||
target_link_libraries(lime-qt PRIVATE audio_core lime_common lime_core input_common network video_core)
|
||||
|
@ -119,10 +119,6 @@ __declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
#include <SDL.h>
|
||||
#endif
|
||||
|
||||
constexpr int default_mouse_timeout = 2500;
|
||||
|
||||
/**
|
||||
@ -1133,58 +1129,9 @@ void GMainWindow::MigrateUserData() {
|
||||
QMessageBox::Ok);
|
||||
}
|
||||
|
||||
#if defined(HAVE_SDL2) && defined(__unix__) && !defined(__APPLE__)
|
||||
static std::optional<QDBusObjectPath> HoldWakeLockLinux(u32 window_id = 0) {
|
||||
if (!QDBusConnection::sessionBus().isConnected()) {
|
||||
return {};
|
||||
}
|
||||
// reference: https://flatpak.github.io/xdg-desktop-portal/#gdbus-org.freedesktop.portal.Inhibit
|
||||
QDBusInterface xdp(QStringLiteral("org.freedesktop.portal.Desktop"),
|
||||
QStringLiteral("/org/freedesktop/portal/desktop"),
|
||||
QStringLiteral("org.freedesktop.portal.Inhibit"));
|
||||
if (!xdp.isValid()) {
|
||||
LOG_WARNING(Frontend, "Couldn't connect to XDP D-Bus endpoint");
|
||||
return {};
|
||||
}
|
||||
QVariantMap options = {};
|
||||
//: TRANSLATORS: This string is shown to the user to explain why Lime3DS needs to prevent the
|
||||
//: computer from sleeping
|
||||
options.insert(QString::fromLatin1("reason"),
|
||||
QCoreApplication::translate("GMainWindow", "Lime3DS is running a game"));
|
||||
// 0x4: Suspend lock; 0x8: Idle lock
|
||||
QDBusReply<QDBusObjectPath> reply =
|
||||
xdp.call(QString::fromLatin1("Inhibit"),
|
||||
QString::fromLatin1("x11:") + QString::number(window_id, 16), 12U, options);
|
||||
|
||||
if (reply.isValid()) {
|
||||
return reply.value();
|
||||
}
|
||||
LOG_WARNING(Frontend, "Couldn't read Inhibit reply from XDP: {}",
|
||||
reply.error().message().toStdString());
|
||||
return {};
|
||||
}
|
||||
|
||||
static void ReleaseWakeLockLinux(const QDBusObjectPath& lock) {
|
||||
if (!QDBusConnection::sessionBus().isConnected()) {
|
||||
return;
|
||||
}
|
||||
QDBusInterface unlocker(QString::fromLatin1("org.freedesktop.portal.Desktop"), lock.path(),
|
||||
QString::fromLatin1("org.freedesktop.portal.Request"));
|
||||
unlocker.call(QString::fromLatin1("Close"));
|
||||
}
|
||||
#endif // __unix__
|
||||
|
||||
void GMainWindow::PreventOSSleep() {
|
||||
#ifdef _WIN32
|
||||
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
|
||||
#elif defined(HAVE_SDL2)
|
||||
SDL_DisableScreenSaver();
|
||||
#if defined(__unix__) && !defined(__APPLE__)
|
||||
auto reply = HoldWakeLockLinux(winId());
|
||||
if (reply) {
|
||||
wake_lock = std::move(reply.value());
|
||||
}
|
||||
#endif // defined(__unix__) && !defined(__APPLE__)
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user