Compare commits

..

2 Commits

Author SHA1 Message Date
Morph
d7d09355e7 qt_software_keyboard: Fix infinite loop when moving between buttons
There was a bug where, when using the numeric keyboard, moving between buttons resulted in an infinite loop, resulting in a stuck state.
This was due to prev_button being the only one enabled in that row or column, causing the condition in the while loop to always be true.
To fix this, detect whether we have returned to that initial row/column and break out of the loop.
2022-07-24 07:27:41 -04:00
Morph
bee823db3a applet/swkbd: Implement optional symbol keys
These are only used in the numeric keyboard, and correspond to the keys to the left and right of the "0" key on the numeric keyboard.
2022-07-24 07:21:02 -04:00
169 changed files with 1214 additions and 10761 deletions

6
.gitmodules vendored
View File

@@ -1,6 +1,3 @@
[submodule "enet"]
path = externals/enet
url = https://github.com/lsalzman/enet.git
[submodule "inih"]
path = externals/inih/inih
url = https://github.com/benhoyt/inih.git
@@ -46,6 +43,3 @@
[submodule "vcpkg"]
path = externals/vcpkg
url = https://github.com/Microsoft/vcpkg.git
[submodule "cpp-jwt"]
path = externals/cpp-jwt
url = https://github.com/arun11299/cpp-jwt.git

View File

@@ -196,7 +196,7 @@ if(ENABLE_QT)
# Check for system Qt on Linux, fallback to bundled Qt
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
if (NOT YUZU_USE_BUNDLED_QT)
find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus Multimedia)
find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus)
endif()
if (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT)
# Check for dependencies, then enable bundled Qt download
@@ -300,9 +300,9 @@ if(ENABLE_QT)
set(YUZU_QT_NO_CMAKE_SYSTEM_PATH "NO_CMAKE_SYSTEM_PATH")
endif()
if ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND YUZU_USE_BUNDLED_QT)
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia DBus ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent DBus ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
else()
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
endif()
if (YUZU_USE_QT_WEB_ENGINE)
find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets)

View File

@@ -10,13 +10,11 @@ function(copy_yuzu_Qt5_deps target_dir)
set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/")
set(Qt5_PLATFORMTHEMES_DIR "${Qt5_DIR}/../../../plugins/platformthemes/")
set(Qt5_PLATFORMINPUTCONTEXTS_DIR "${Qt5_DIR}/../../../plugins/platforminputcontexts/")
set(Qt5_MEDIASERVICE_DIR "${Qt5_DIR}/../../../plugins/mediaservice/")
set(Qt5_XCBGLINTEGRATIONS_DIR "${Qt5_DIR}/../../../plugins/xcbglintegrations/")
set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/")
set(Qt5_IMAGEFORMATS_DIR "${Qt5_DIR}/../../../plugins/imageformats/")
set(Qt5_RESOURCES_DIR "${Qt5_DIR}/../../../resources/")
set(PLATFORMS ${DLL_DEST}plugins/platforms/)
set(MEDIASERVICE ${DLL_DEST}mediaservice/)
set(STYLES ${DLL_DEST}plugins/styles/)
set(IMAGEFORMATS ${DLL_DEST}plugins/imageformats/)
if (MSVC)
@@ -24,8 +22,6 @@ function(copy_yuzu_Qt5_deps target_dir)
Qt5Core$<$<CONFIG:Debug>:d>.*
Qt5Gui$<$<CONFIG:Debug>:d>.*
Qt5Widgets$<$<CONFIG:Debug>:d>.*
Qt5Multimedia$<$<CONFIG:Debug>:d>.*
Qt5Network$<$<CONFIG:Debug>:d>.*
)
if (YUZU_USE_QT_WEB_ENGINE)
@@ -57,10 +53,6 @@ function(copy_yuzu_Qt5_deps target_dir)
qjpeg$<$<CONFIG:Debug>:d>.*
qgif$<$<CONFIG:Debug>:d>.*
)
windows_copy_files(yuzu ${Qt5_MEDIASERVICE_DIR} ${MEDIASERVICE}
dsengine$<$<CONFIG:Debug>:d>.*
wmfengine$<$<CONFIG:Debug>:d>.*
)
else()
set(Qt5_DLLS
"${Qt5_DLL_DIR}libQt5Core.so.5"

9
dist/license.md vendored
View File

@@ -3,9 +3,6 @@ The icons in this folder and its subfolders have the following licenses:
Icon Name | License | Origin/Author
--- | --- | ---
qt_themes/default/icons/16x16/checked.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/16x16/connected.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/16x16/connected_notification.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/16x16/disconnected.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/16x16/failed.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/16x16/view-refresh.png | Apache 2.0 | https://material.io
@@ -13,24 +10,18 @@ qt_themes/default/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.
qt_themes/default/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/48x48/no_avatar.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team
qt_themes/default/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/48x48/star.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/16x16/connected.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/16x16/connected_notification.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/16x16/view-refresh.png | Apache 2.0 | https://material.io
qt_themes/qdarkstyle/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/48x48/no_avatar.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team
qt_themes/qdarkstyle/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/48x48/star.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/16x16/connected.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/16x16/connected_notification.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/16x16/view-refresh.png | Apache 2.0 | https://material.io
qt_themes/colorful/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 607 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 784 B

View File

@@ -1,9 +1,6 @@
<RCC>
<qresource prefix="icons/colorful">
<file alias="index.theme">icons/index.theme</file>
<file alias="16x16/connected.png">icons/16x16/connected.png</file>
<file alias="16x16/connected_notification.png">icons/16x16/connected_notification.png</file>
<file alias="16x16/disconnected.png">icons/16x16/disconnected.png</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">icons/48x48/chip.png</file>

View File

@@ -1,15 +1,11 @@
<RCC>
<qresource prefix="icons/colorful_dark">
<file alias="16x16/connected.png">../colorful/icons/16x16/connected.png</file>
<file alias="16x16/connected_notification.png">../colorful/icons/16x16/connected_notification.png</file>
<file alias="16x16/disconnected.png">../colorful/icons/16x16/disconnected.png</file>
<file alias="index.theme">icons/index.theme</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
<file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
<file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
<file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
<file alias="48x48/no_avatar.png">../qdarkstyle/icons/48x48/no_avatar.png</file>
<file alias="48x48/plus.png">../colorful/icons/48x48/plus.png</file>
<file alias="48x48/sd_card.png">../colorful/icons/48x48/sd_card.png</file>
<file alias="256x256/plus_folder.png">../colorful/icons/256x256/plus_folder.png</file>

View File

@@ -4,14 +4,10 @@
<file alias="16x16/checked.png">icons/16x16/checked.png</file>
<file alias="16x16/failed.png">icons/16x16/failed.png</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
<file alias="16x16/connected.png">icons/16x16/connected.png</file>
<file alias="16x16/disconnected.png">icons/16x16/disconnected.png</file>
<file alias="16x16/connected_notification.png">icons/16x16/connected_notification.png</file>
<file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
<file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file>
<file alias="48x48/plus.png">icons/48x48/plus.png</file>
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
<file alias="48x48/star.png">icons/48x48/star.png</file>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 269 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 397 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 708 B

View File

@@ -1,15 +1,11 @@
<RCC>
<qresource prefix="icons/qdarkstyle">
<file alias="index.theme">icons/index.theme</file>
<file alias="16x16/connected.png">icons/16x16/connected.png</file>
<file alias="16x16/disconnected.png">icons/16x16/disconnected.png</file>
<file alias="16x16/connected_notification.png">icons/16x16/connected_notification.png</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
<file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
<file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file>
<file alias="48x48/plus.png">icons/48x48/plus.png</file>
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
<file alias="48x48/star.png">icons/48x48/star.png</file>

View File

@@ -73,10 +73,6 @@ if (YUZU_USE_EXTERNAL_SDL2)
add_library(SDL2 ALIAS SDL2-static)
endif()
# ENet
add_subdirectory(enet)
target_include_directories(enet INTERFACE ./enet/include)
# Cubeb
if(ENABLE_CUBEB)
set(BUILD_TESTS OFF CACHE BOOL "")
@@ -116,11 +112,6 @@ if (ENABLE_WEB_SERVICE)
if (WIN32)
target_link_libraries(httplib INTERFACE crypt32 cryptui ws2_32)
endif()
# cpp-jwt
add_library(cpp-jwt INTERFACE)
target_include_directories(cpp-jwt INTERFACE ./cpp-jwt/include)
target_compile_definitions(cpp-jwt INTERFACE CPP_JWT_USE_VENDORED_NLOHMANN_JSON)
endif()
# Opus

1
externals/cpp-jwt vendored

Submodule externals/cpp-jwt deleted from e12ef06218

1
externals/enet vendored

Submodule externals/enet deleted from 39a72ab199

View File

@@ -156,7 +156,6 @@ add_subdirectory(common)
add_subdirectory(core)
add_subdirectory(audio_core)
add_subdirectory(video_core)
add_subdirectory(network)
add_subdirectory(input_common)
add_subdirectory(shader_recompiler)

View File

@@ -41,7 +41,6 @@ add_custom_command(OUTPUT scm_rev.cpp
add_library(common STATIC
algorithm.h
alignment.h
announce_multiplayer_room.h
assert.cpp
assert.h
atomic_helpers.h

View File

@@ -1,143 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <functional>
#include <string>
#include <vector>
#include "common/common_types.h"
#include "web_service/web_result.h"
namespace AnnounceMultiplayerRoom {
using MacAddress = std::array<u8, 6>;
struct GameInfo {
std::string name{""};
u64 id{0};
};
struct Member {
std::string username;
std::string nickname;
std::string display_name;
std::string avatar_url;
MacAddress mac_address;
GameInfo game;
};
struct RoomInformation {
std::string name; ///< Name of the server
std::string description; ///< Server description
u32 member_slots; ///< Maximum number of members in this room
u16 port; ///< The port of this room
GameInfo preferred_game; ///< Game to advertise that you want to play
std::string host_username; ///< Forum username of the host
bool enable_yuzu_mods; ///< Allow yuzu Moderators to moderate on this room
};
struct Room {
RoomInformation information;
std::string id;
std::string verify_uid; ///< UID used for verification
std::string ip;
u32 net_version;
bool has_password;
std::vector<Member> members;
};
using RoomList = std::vector<Room>;
/**
* A AnnounceMultiplayerRoom interface class. A backend to submit/get to/from a web service should
* implement this interface.
*/
class Backend {
public:
virtual ~Backend() = default;
/**
* Sets the Information that gets used for the announce
* @param uid The Id of the room
* @param name The name of the room
* @param description The room description
* @param port The port of the room
* @param net_version The version of the libNetwork that gets used
* @param has_password True if the room is passowrd protected
* @param preferred_game The preferred game of the room
* @param preferred_game_id The title id of the preferred game
*/
virtual void SetRoomInformation(const std::string& name, const std::string& description,
const u16 port, const u32 max_player, const u32 net_version,
const bool has_password, const GameInfo& preferred_game) = 0;
/**
* Adds a player information to the data that gets announced
* @param nickname The nickname of the player
* @param mac_address The MAC Address of the player
* @param game_id The title id of the game the player plays
* @param game_name The name of the game the player plays
*/
virtual void AddPlayer(const Member& member) = 0;
/**
* Updates the data in the announce service. Re-register the room when required.
* @result The result of the update attempt
*/
virtual WebService::WebResult Update() = 0;
/**
* Registers the data in the announce service
* @result The result of the register attempt. When the result code is Success, A global Guid of
* the room which may be used for verification will be in the result's returned_data.
*/
virtual WebService::WebResult Register() = 0;
/**
* Empties the stored players
*/
virtual void ClearPlayers() = 0;
/**
* Get the room information from the announce service
* @result A list of all rooms the announce service has
*/
virtual RoomList GetRoomList() = 0;
/**
* Sends a delete message to the announce service
*/
virtual void Delete() = 0;
};
/**
* Empty implementation of AnnounceMultiplayerRoom interface that drops all data. Used when a
* functional backend implementation is not available.
*/
class NullBackend : public Backend {
public:
~NullBackend() = default;
void SetRoomInformation(const std::string& /*name*/, const std::string& /*description*/,
const u16 /*port*/, const u32 /*max_player*/, const u32 /*net_version*/,
const bool /*has_password*/,
const GameInfo& /*preferred_game*/) override {}
void AddPlayer(const Member& /*member*/) override {}
WebService::WebResult Update() override {
return WebService::WebResult{WebService::WebResult::Code::NoWebservice,
"WebService is missing", ""};
}
WebService::WebResult Register() override {
return WebService::WebResult{WebService::WebResult::Code::NoWebservice,
"WebService is missing", ""};
}
void ClearPlayers() override {}
RoomList GetRoomList() override {
return RoomList{};
}
void Delete() override {}
};
} // namespace AnnounceMultiplayerRoom

View File

@@ -28,7 +28,7 @@ enum class InputType {
Color,
Vibration,
Nfc,
IrSensor,
Ir,
};
// Internal battery charge level
@@ -53,15 +53,6 @@ enum class PollingMode {
IR,
};
enum class CameraFormat {
Size320x240,
Size160x120,
Size80x60,
Size40x30,
Size20x15,
None,
};
// Vibration reply from the controller
enum class VibrationError {
None,
@@ -77,13 +68,6 @@ enum class PollingError {
Unknown,
};
// Ir camera reply from the controller
enum class CameraError {
None,
NotSupported,
Unknown,
};
// Hint for amplification curve to be used
enum class VibrationAmplificationType {
Linear,
@@ -192,12 +176,6 @@ struct LedStatus {
bool led_4{};
};
// Raw data fom camera
struct CameraStatus {
CameraFormat format{CameraFormat::None};
std::vector<u8> data{};
};
// List of buttons to be passed to Qt that can be translated
enum class ButtonNames {
Undefined,
@@ -255,7 +233,6 @@ struct CallbackStatus {
BodyColorStatus color_status{};
BatteryStatus battery_status{};
VibrationStatus vibration_status{};
CameraStatus camera_status{};
};
// Triggered once every input change
@@ -304,10 +281,6 @@ public:
virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) {
return PollingError::NotSupported;
}
virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) {
return CameraError::NotSupported;
}
};
/// An abstract class template for a factory that can create input devices.

View File

@@ -503,9 +503,6 @@ struct Values {
Setting<bool> enable_ring_controller{true, "enable_ring_controller"};
RingconRaw ringcon_analogs;
Setting<bool> enable_ir_sensor{false, "enable_ir_sensor"};
Setting<std::string> ir_sensor_device{"auto", "ir_sensor_device"};
// Data Storage
Setting<bool> use_virtual_sd{true, "use_virtual_sd"};
Setting<bool> gamecard_inserted{false, "gamecard_inserted"};

View File

@@ -1,6 +1,4 @@
add_library(core STATIC
announce_multiplayer_session.cpp
announce_multiplayer_session.h
arm/arm_interface.h
arm/arm_interface.cpp
arm/cpu_interrupt_handler.cpp
@@ -160,7 +158,6 @@ add_library(core STATIC
hid/input_converter.h
hid/input_interpreter.cpp
hid/input_interpreter.h
hid/irs_types.h
hid/motion_input.cpp
hid/motion_input.h
hle/api_version.h
@@ -480,20 +477,6 @@ add_library(core STATIC
hle/service/hid/hidbus/starlink.h
hle/service/hid/hidbus/stubbed.cpp
hle/service/hid/hidbus/stubbed.h
hle/service/hid/irsensor/clustering_processor.cpp
hle/service/hid/irsensor/clustering_processor.h
hle/service/hid/irsensor/image_transfer_processor.cpp
hle/service/hid/irsensor/image_transfer_processor.h
hle/service/hid/irsensor/ir_led_processor.cpp
hle/service/hid/irsensor/ir_led_processor.h
hle/service/hid/irsensor/moment_processor.cpp
hle/service/hid/irsensor/moment_processor.h
hle/service/hid/irsensor/pointing_processor.cpp
hle/service/hid/irsensor/pointing_processor.h
hle/service/hid/irsensor/processor_base.cpp
hle/service/hid/irsensor/processor_base.h
hle/service/hid/irsensor/tera_plugin_processor.cpp
hle/service/hid/irsensor/tera_plugin_processor.h
hle/service/jit/jit_context.cpp
hle/service/jit/jit_context.h
hle/service/jit/jit.cpp
@@ -716,11 +699,6 @@ add_library(core STATIC
hle/service/vi/vi_u.h
hle/service/wlan/wlan.cpp
hle/service/wlan/wlan.h
internal_network/network.cpp
internal_network/network.h
internal_network/network_interface.cpp
internal_network/network_interface.h
internal_network/sockets.h
loader/deconstructed_rom_directory.cpp
loader/deconstructed_rom_directory.h
loader/elf.cpp
@@ -748,6 +726,11 @@ add_library(core STATIC
memory/dmnt_cheat_vm.h
memory.cpp
memory.h
network/network.cpp
network/network.h
network/network_interface.cpp
network/network_interface.h
network/sockets.h
perf_stats.cpp
perf_stats.h
reporter.cpp
@@ -782,7 +765,7 @@ endif()
create_target_directory_groups(core)
target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core)
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus)
if (MINGW)
target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})

View File

@@ -1,164 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <chrono>
#include <future>
#include <vector>
#include "announce_multiplayer_session.h"
#include "common/announce_multiplayer_room.h"
#include "common/assert.h"
#include "common/settings.h"
#include "network/network.h"
#ifdef ENABLE_WEB_SERVICE
#include "web_service/announce_room_json.h"
#endif
namespace Core {
// Time between room is announced to web_service
static constexpr std::chrono::seconds announce_time_interval(15);
AnnounceMultiplayerSession::AnnounceMultiplayerSession(Network::RoomNetwork& room_network_)
: room_network{room_network_} {
#ifdef ENABLE_WEB_SERVICE
backend = std::make_unique<WebService::RoomJson>(Settings::values.web_api_url.GetValue(),
Settings::values.yuzu_username.GetValue(),
Settings::values.yuzu_token.GetValue());
#else
backend = std::make_unique<AnnounceMultiplayerRoom::NullBackend>();
#endif
}
WebService::WebResult AnnounceMultiplayerSession::Register() {
std::shared_ptr<Network::Room> room = room_network.GetRoom().lock();
if (!room) {
return WebService::WebResult{WebService::WebResult::Code::LibError,
"Network is not initialized", ""};
}
if (room->GetState() != Network::Room::State::Open) {
return WebService::WebResult{WebService::WebResult::Code::LibError, "Room is not open", ""};
}
UpdateBackendData(room);
WebService::WebResult result = backend->Register();
if (result.result_code != WebService::WebResult::Code::Success) {
return result;
}
LOG_INFO(WebService, "Room has been registered");
room->SetVerifyUID(result.returned_data);
registered = true;
return WebService::WebResult{WebService::WebResult::Code::Success, "", ""};
}
void AnnounceMultiplayerSession::Start() {
if (announce_multiplayer_thread) {
Stop();
}
shutdown_event.Reset();
announce_multiplayer_thread =
std::make_unique<std::thread>(&AnnounceMultiplayerSession::AnnounceMultiplayerLoop, this);
}
void AnnounceMultiplayerSession::Stop() {
if (announce_multiplayer_thread) {
shutdown_event.Set();
announce_multiplayer_thread->join();
announce_multiplayer_thread.reset();
backend->Delete();
registered = false;
}
}
AnnounceMultiplayerSession::CallbackHandle AnnounceMultiplayerSession::BindErrorCallback(
std::function<void(const WebService::WebResult&)> function) {
std::lock_guard lock(callback_mutex);
auto handle = std::make_shared<std::function<void(const WebService::WebResult&)>>(function);
error_callbacks.insert(handle);
return handle;
}
void AnnounceMultiplayerSession::UnbindErrorCallback(CallbackHandle handle) {
std::lock_guard lock(callback_mutex);
error_callbacks.erase(handle);
}
AnnounceMultiplayerSession::~AnnounceMultiplayerSession() {
Stop();
}
void AnnounceMultiplayerSession::UpdateBackendData(std::shared_ptr<Network::Room> room) {
Network::RoomInformation room_information = room->GetRoomInformation();
std::vector<AnnounceMultiplayerRoom::Member> memberlist = room->GetRoomMemberList();
backend->SetRoomInformation(room_information.name, room_information.description,
room_information.port, room_information.member_slots,
Network::network_version, room->HasPassword(),
room_information.preferred_game);
backend->ClearPlayers();
for (const auto& member : memberlist) {
backend->AddPlayer(member);
}
}
void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() {
// Invokes all current bound error callbacks.
const auto ErrorCallback = [this](WebService::WebResult result) {
std::lock_guard<std::mutex> lock(callback_mutex);
for (auto callback : error_callbacks) {
(*callback)(result);
}
};
if (!registered) {
WebService::WebResult result = Register();
if (result.result_code != WebService::WebResult::Code::Success) {
ErrorCallback(result);
return;
}
}
auto update_time = std::chrono::steady_clock::now();
std::future<WebService::WebResult> future;
while (!shutdown_event.WaitUntil(update_time)) {
update_time += announce_time_interval;
std::shared_ptr<Network::Room> room = room_network.GetRoom().lock();
if (!room) {
break;
}
if (room->GetState() != Network::Room::State::Open) {
break;
}
UpdateBackendData(room);
WebService::WebResult result = backend->Update();
if (result.result_code != WebService::WebResult::Code::Success) {
ErrorCallback(result);
}
if (result.result_string == "404") {
registered = false;
// Needs to register the room again
WebService::WebResult register_result = Register();
if (register_result.result_code != WebService::WebResult::Code::Success) {
ErrorCallback(register_result);
}
}
}
}
AnnounceMultiplayerRoom::RoomList AnnounceMultiplayerSession::GetRoomList() {
return backend->GetRoomList();
}
bool AnnounceMultiplayerSession::IsRunning() const {
return announce_multiplayer_thread != nullptr;
}
void AnnounceMultiplayerSession::UpdateCredentials() {
ASSERT_MSG(!IsRunning(), "Credentials can only be updated when session is not running");
#ifdef ENABLE_WEB_SERVICE
backend = std::make_unique<WebService::RoomJson>(Settings::values.web_api_url.GetValue(),
Settings::values.yuzu_username.GetValue(),
Settings::values.yuzu_token.GetValue());
#endif
}
} // namespace Core

View File

@@ -1,98 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <functional>
#include <memory>
#include <mutex>
#include <set>
#include <thread>
#include "common/announce_multiplayer_room.h"
#include "common/common_types.h"
#include "common/thread.h"
namespace Network {
class Room;
class RoomNetwork;
} // namespace Network
namespace Core {
/**
* Instruments AnnounceMultiplayerRoom::Backend.
* Creates a thread that regularly updates the room information and submits them
* An async get of room information is also possible
*/
class AnnounceMultiplayerSession {
public:
using CallbackHandle = std::shared_ptr<std::function<void(const WebService::WebResult&)>>;
AnnounceMultiplayerSession(Network::RoomNetwork& room_network_);
~AnnounceMultiplayerSession();
/**
* Allows to bind a function that will get called if the announce encounters an error
* @param function The function that gets called
* @return A handle that can be used the unbind the function
*/
CallbackHandle BindErrorCallback(std::function<void(const WebService::WebResult&)> function);
/**
* Unbind a function from the error callbacks
* @param handle The handle for the function that should get unbind
*/
void UnbindErrorCallback(CallbackHandle handle);
/**
* Registers a room to web services
* @return The result of the registration attempt.
*/
WebService::WebResult Register();
/**
* Starts the announce of a room to web services
*/
void Start();
/**
* Stops the announce to web services
*/
void Stop();
/**
* Returns a list of all room information the backend got
* @param func A function that gets executed when the async get finished, e.g. a signal
* @return a list of rooms received from the web service
*/
AnnounceMultiplayerRoom::RoomList GetRoomList();
/**
* Whether the announce session is still running
*/
bool IsRunning() const;
/**
* Recreates the backend, updating the credentials.
* This can only be used when the announce session is not running.
*/
void UpdateCredentials();
private:
void UpdateBackendData(std::shared_ptr<Network::Room> room);
void AnnounceMultiplayerLoop();
Common::Event shutdown_event;
std::mutex callback_mutex;
std::set<CallbackHandle> error_callbacks;
std::unique_ptr<std::thread> announce_multiplayer_thread;
/// Backend interface that logs fields
std::unique_ptr<AnnounceMultiplayerRoom::Backend> backend;
std::atomic_bool registered = false; ///< Whether the room has been registered
Network::RoomNetwork& room_network;
};
} // namespace Core

View File

@@ -154,10 +154,9 @@ void ARM_Interface::Run() {
break;
}
// Handle syscalls and scheduling (this may change the current thread/core)
// Handle syscalls and scheduling (this may change the current thread)
if (Has(hr, svc_call)) {
Kernel::Svc::Call(system, GetSvcNumber());
break;
}
if (Has(hr, break_loop) || !uses_wall_clock) {
break;

View File

@@ -43,15 +43,14 @@
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/time/time_manager.h"
#include "core/internal_network/network.h"
#include "core/loader/loader.h"
#include "core/memory.h"
#include "core/memory/cheat_engine.h"
#include "core/network/network.h"
#include "core/perf_stats.h"
#include "core/reporter.h"
#include "core/telemetry_session.h"
#include "core/tools/freezer.h"
#include "network/network.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
@@ -131,7 +130,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
struct System::Impl {
explicit Impl(System& system)
: kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{},
: kernel{system}, fs_controller{system}, memory{system}, hid_core{},
cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {}
SystemResultStatus Run() {
@@ -316,17 +315,6 @@ struct System::Impl {
GetAndResetPerfStats();
perf_stats->BeginSystemFrame();
std::string name = "Unknown Game";
if (app_loader->ReadTitle(name) != Loader::ResultStatus::Success) {
LOG_ERROR(Core, "Failed to read title for ROM (Error {})", load_result);
}
if (auto room_member = room_network.GetRoomMember().lock()) {
Network::GameInfo game_info;
game_info.name = name;
game_info.id = program_id;
room_member->SendGameInfo(game_info);
}
status = SystemResultStatus::Success;
return status;
}
@@ -374,11 +362,6 @@ struct System::Impl {
memory.Reset();
applet_manager.ClearAll();
if (auto room_member = room_network.GetRoomMember().lock()) {
Network::GameInfo game_info{};
room_member->SendGameInfo(game_info);
}
LOG_DEBUG(Core, "Shutdown OK");
}
@@ -451,8 +434,6 @@ struct System::Impl {
std::unique_ptr<AudioCore::AudioCore> audio_core;
Core::Memory::Memory memory;
Core::HID::HIDCore hid_core;
Network::RoomNetwork room_network;
CpuManager cpu_manager;
std::atomic_bool is_powered_on{};
bool exit_lock = false;
@@ -898,14 +879,6 @@ const Core::Debugger& System::GetDebugger() const {
return *impl->debugger;
}
Network::RoomNetwork& System::GetRoomNetwork() {
return impl->room_network;
}
const Network::RoomNetwork& System::GetRoomNetwork() const {
return impl->room_network;
}
void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) {
impl->execute_program_callback = std::move(callback);
}

View File

@@ -97,10 +97,6 @@ namespace Core::HID {
class HIDCore;
}
namespace Network {
class RoomNetwork;
}
namespace Core {
class ARM_Interface;
@@ -383,12 +379,6 @@ public:
[[nodiscard]] Core::Debugger& GetDebugger();
[[nodiscard]] const Core::Debugger& GetDebugger() const;
/// Gets a mutable reference to the Room Network.
[[nodiscard]] Network::RoomNetwork& GetRoomNetwork();
/// Gets an immutable reference to the Room Network.
[[nodiscard]] const Network::RoomNetwork& GetRoomNetwork() const;
void SetExitLock(bool locked);
[[nodiscard]] bool GetExitLock() const;

View File

@@ -8,7 +8,6 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/cpu_manager.h"
#include "core/hle/kernel/k_interrupt_manager.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
@@ -50,6 +49,14 @@ void CpuManager::GuestThreadFunction() {
}
}
void CpuManager::GuestRewindFunction() {
if (is_multicore) {
MultiCoreRunGuestLoop();
} else {
SingleCoreRunGuestLoop();
}
}
void CpuManager::IdleThreadFunction() {
if (is_multicore) {
MultiCoreRunIdleThread();
@@ -62,21 +69,21 @@ void CpuManager::ShutdownThreadFunction() {
ShutdownThread();
}
void CpuManager::HandleInterrupt() {
auto& kernel = system.Kernel();
auto core_index = kernel.CurrentPhysicalCoreIndex();
Kernel::KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_index));
}
///////////////////////////////////////////////////////////////////////////////
/// MultiCore ///
///////////////////////////////////////////////////////////////////////////////
void CpuManager::MultiCoreRunGuestThread() {
// Similar to UserModeThreadStarter in HOS
auto& kernel = system.Kernel();
kernel.CurrentScheduler()->OnThreadStart();
auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread();
auto& host_context = thread->GetHostContext();
host_context->SetRewindPoint([this] { GuestRewindFunction(); });
MultiCoreRunGuestLoop();
}
void CpuManager::MultiCoreRunGuestLoop() {
auto& kernel = system.Kernel();
while (true) {
auto* physical_core = &kernel.CurrentPhysicalCore();
@@ -84,26 +91,18 @@ void CpuManager::MultiCoreRunGuestThread() {
physical_core->Run();
physical_core = &kernel.CurrentPhysicalCore();
}
HandleInterrupt();
{
Kernel::KScopedDisableDispatch dd(kernel);
physical_core->ArmInterface().ClearExclusiveState();
}
}
}
void CpuManager::MultiCoreRunIdleThread() {
// Not accurate to HOS. Remove this entire method when singlecore is removed.
// See notes in KScheduler::ScheduleImpl for more information about why this
// is inaccurate.
auto& kernel = system.Kernel();
kernel.CurrentScheduler()->OnThreadStart();
while (true) {
auto& physical_core = kernel.CurrentPhysicalCore();
if (!physical_core.IsInterrupted()) {
physical_core.Idle();
}
HandleInterrupt();
Kernel::KScopedDisableDispatch dd(kernel);
kernel.CurrentPhysicalCore().Idle();
}
}
@@ -114,73 +113,80 @@ void CpuManager::MultiCoreRunIdleThread() {
void CpuManager::SingleCoreRunGuestThread() {
auto& kernel = system.Kernel();
kernel.CurrentScheduler()->OnThreadStart();
auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread();
auto& host_context = thread->GetHostContext();
host_context->SetRewindPoint([this] { GuestRewindFunction(); });
SingleCoreRunGuestLoop();
}
void CpuManager::SingleCoreRunGuestLoop() {
auto& kernel = system.Kernel();
while (true) {
auto* physical_core = &kernel.CurrentPhysicalCore();
if (!physical_core->IsInterrupted()) {
physical_core->Run();
physical_core = &kernel.CurrentPhysicalCore();
}
kernel.SetIsPhantomModeForSingleCore(true);
system.CoreTiming().Advance();
kernel.SetIsPhantomModeForSingleCore(false);
physical_core->ArmInterface().ClearExclusiveState();
PreemptSingleCore();
HandleInterrupt();
auto& scheduler = kernel.Scheduler(current_core);
scheduler.RescheduleCurrentCore();
}
}
void CpuManager::SingleCoreRunIdleThread() {
auto& kernel = system.Kernel();
kernel.CurrentScheduler()->OnThreadStart();
while (true) {
auto& physical_core = kernel.CurrentPhysicalCore();
PreemptSingleCore(false);
system.CoreTiming().AddTicks(1000U);
idle_count++;
HandleInterrupt();
auto& scheduler = physical_core.Scheduler();
scheduler.RescheduleCurrentCore();
}
}
void CpuManager::PreemptSingleCore(bool from_running_environment) {
auto& kernel = system.Kernel();
void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
{
auto& kernel = system.Kernel();
auto& scheduler = kernel.Scheduler(current_core);
Kernel::KThread* current_thread = scheduler.GetSchedulerCurrentThread();
if (idle_count >= 4 || from_running_enviroment) {
if (!from_running_enviroment) {
system.CoreTiming().Idle();
idle_count = 0;
}
kernel.SetIsPhantomModeForSingleCore(true);
system.CoreTiming().Advance();
kernel.SetIsPhantomModeForSingleCore(false);
}
current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES);
system.CoreTiming().ResetTicks();
scheduler.Unload(scheduler.GetSchedulerCurrentThread());
if (idle_count >= 4 || from_running_environment) {
if (!from_running_environment) {
system.CoreTiming().Idle();
auto& next_scheduler = kernel.Scheduler(current_core);
Common::Fiber::YieldTo(current_thread->GetHostContext(), *next_scheduler.ControlContext());
}
// May have changed scheduler
{
auto& scheduler = system.Kernel().Scheduler(current_core);
scheduler.Reload(scheduler.GetSchedulerCurrentThread());
if (!scheduler.IsIdle()) {
idle_count = 0;
}
kernel.SetIsPhantomModeForSingleCore(true);
system.CoreTiming().Advance();
kernel.SetIsPhantomModeForSingleCore(false);
}
current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES);
system.CoreTiming().ResetTicks();
kernel.Scheduler(current_core).PreemptSingleCore();
// We've now been scheduled again, and we may have exchanged schedulers.
// Reload the scheduler in case it's different.
if (!kernel.Scheduler(current_core).IsIdle()) {
idle_count = 0;
}
}
void CpuManager::GuestActivate() {
// Similar to the HorizonKernelMain callback in HOS
auto& kernel = system.Kernel();
auto* scheduler = kernel.CurrentScheduler();
scheduler->Activate();
UNREACHABLE();
}
void CpuManager::ShutdownThread() {
auto& kernel = system.Kernel();
auto* thread = kernel.GetCurrentEmuThread();
auto core = is_multicore ? kernel.CurrentPhysicalCoreIndex() : 0;
auto* current_thread = kernel.GetCurrentEmuThread();
Common::Fiber::YieldTo(thread->GetHostContext(), *core_data[core].host_context);
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
UNREACHABLE();
}
@@ -212,12 +218,9 @@ void CpuManager::RunThread(std::size_t core) {
system.GPU().ObtainContext();
}
auto& kernel = system.Kernel();
auto& scheduler = *kernel.CurrentScheduler();
auto* thread = scheduler.GetSchedulerCurrentThread();
Kernel::SetCurrentThread(kernel, thread);
Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext());
auto* current_thread = system.Kernel().CurrentScheduler()->GetIdleThread();
Kernel::SetCurrentThread(system.Kernel(), current_thread);
Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
}
} // namespace Core

View File

@@ -50,10 +50,7 @@ public:
void Initialize();
void Shutdown();
std::function<void()> GetGuestActivateFunc() {
return [this] { GuestActivate(); };
}
std::function<void()> GetGuestThreadFunc() {
std::function<void()> GetGuestThreadStartFunc() {
return [this] { GuestThreadFunction(); };
}
std::function<void()> GetIdleThreadStartFunc() {
@@ -71,19 +68,20 @@ public:
private:
void GuestThreadFunction();
void GuestRewindFunction();
void IdleThreadFunction();
void ShutdownThreadFunction();
void MultiCoreRunGuestThread();
void MultiCoreRunGuestLoop();
void MultiCoreRunIdleThread();
void SingleCoreRunGuestThread();
void SingleCoreRunGuestLoop();
void SingleCoreRunIdleThread();
static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core);
void GuestActivate();
void HandleInterrupt();
void ShutdownThread();
void RunThread(std::size_t core);

View File

@@ -17,6 +17,8 @@ struct KeyboardInitializeParameters {
std::u16string sub_text;
std::u16string guide_text;
std::u16string initial_text;
char16_t left_optional_symbol_key;
char16_t right_optional_symbol_key;
u32 max_text_length;
u32 min_text_length;
s32 initial_cursor_position;

View File

@@ -126,14 +126,10 @@ void EmulatedController::LoadDevices() {
battery_params[LeftIndex].Set("battery", true);
battery_params[RightIndex].Set("battery", true);
camera_params = Common::ParamPackage{"engine:camera,camera:1"};
output_params[LeftIndex] = left_joycon;
output_params[RightIndex] = right_joycon;
output_params[2] = camera_params;
output_params[LeftIndex].Set("output", true);
output_params[RightIndex].Set("output", true);
output_params[2].Set("output", true);
LoadTASParams();
@@ -150,7 +146,6 @@ void EmulatedController::LoadDevices() {
Common::Input::CreateDevice<Common::Input::InputDevice>);
std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(),
Common::Input::CreateDevice<Common::Input::InputDevice>);
camera_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(camera_params);
std::transform(output_params.begin(), output_params.end(), output_devices.begin(),
Common::Input::CreateDevice<Common::Input::OutputDevice>);
@@ -272,14 +267,6 @@ void EmulatedController::ReloadInput() {
motion_devices[index]->ForceUpdate();
}
if (camera_devices) {
camera_devices->SetCallback({
.on_change =
[this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); },
});
camera_devices->ForceUpdate();
}
// Use a common UUID for TAS
static constexpr Common::UUID TAS_UUID = Common::UUID{
{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
@@ -864,25 +851,6 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
TriggerOnChange(ControllerTriggerType::Battery, true);
}
void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) {
std::unique_lock lock{mutex};
controller.camera_values = TransformToCamera(callback);
if (is_configuring) {
lock.unlock();
TriggerOnChange(ControllerTriggerType::IrSensor, false);
return;
}
controller.camera_state.sample++;
controller.camera_state.format =
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(controller.camera_values.format);
controller.camera_state.data = controller.camera_values.data;
lock.unlock();
TriggerOnChange(ControllerTriggerType::IrSensor, true);
}
bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
if (device_index >= output_devices.size()) {
return false;
@@ -960,23 +928,6 @@ bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode)
return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None;
}
bool EmulatedController::SetCameraFormat(
Core::IrSensor::ImageTransferProcessorFormat camera_format) {
LOG_INFO(Service_HID, "Set camera format {}", camera_format);
auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
auto& camera_output_device = output_devices[2];
if (right_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
camera_format)) == Common::Input::CameraError::None) {
return true;
}
// Fallback to Qt camera if native device doesn't have support
return camera_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
camera_format)) == Common::Input::CameraError::None;
}
void EmulatedController::SetLedPattern() {
for (auto& device : output_devices) {
if (!device) {
@@ -1212,11 +1163,6 @@ BatteryValues EmulatedController::GetBatteryValues() const {
return controller.battery_values;
}
CameraValues EmulatedController::GetCameraValues() const {
std::scoped_lock lock{mutex};
return controller.camera_values;
}
HomeButtonState EmulatedController::GetHomeButtons() const {
std::scoped_lock lock{mutex};
if (is_configuring) {
@@ -1305,11 +1251,6 @@ BatteryLevelState EmulatedController::GetBattery() const {
return controller.battery_state;
}
const CameraState& EmulatedController::GetCamera() const {
std::scoped_lock lock{mutex};
return controller.camera_state;
}
void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
std::scoped_lock lock{callback_mutex};
for (const auto& poller_pair : callback_list) {

View File

@@ -15,12 +15,10 @@
#include "common/settings.h"
#include "common/vector_math.h"
#include "core/hid/hid_types.h"
#include "core/hid/irs_types.h"
#include "core/hid/motion_input.h"
namespace Core::HID {
const std::size_t max_emulated_controllers = 2;
const std::size_t output_devices = 3;
struct ControllerMotionInfo {
Common::Input::MotionStatus raw_status{};
MotionInput emulated{};
@@ -36,16 +34,15 @@ using TriggerDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>;
using BatteryDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
using CameraDevices = std::unique_ptr<Common::Input::InputDevice>;
using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices>;
using OutputDevices =
std::array<std::unique_ptr<Common::Input::OutputDevice>, max_emulated_controllers>;
using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>;
using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>;
using CameraParams = Common::ParamPackage;
using OutputParams = std::array<Common::ParamPackage, output_devices>;
using OutputParams = std::array<Common::ParamPackage, max_emulated_controllers>;
using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
@@ -54,7 +51,6 @@ using TriggerValues =
using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>;
using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>;
using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>;
using CameraValues = Common::Input::CameraStatus;
using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>;
struct AnalogSticks {
@@ -74,12 +70,6 @@ struct BatteryLevelState {
NpadPowerInfo right{};
};
struct CameraState {
Core::IrSensor::ImageTransferProcessorFormat format{};
std::vector<u8> data{};
std::size_t sample{};
};
struct ControllerMotion {
Common::Vec3f accel{};
Common::Vec3f gyro{};
@@ -106,7 +96,6 @@ struct ControllerStatus {
ColorValues color_values{};
BatteryValues battery_values{};
VibrationValues vibration_values{};
CameraValues camera_values{};
// Data for HID serices
HomeButtonState home_button_state{};
@@ -118,7 +107,6 @@ struct ControllerStatus {
NpadGcTriggerState gc_trigger_state{};
ControllerColors colors_state{};
BatteryLevelState battery_state{};
CameraState camera_state{};
};
enum class ControllerTriggerType {
@@ -129,7 +117,6 @@ enum class ControllerTriggerType {
Color,
Battery,
Vibration,
IrSensor,
Connected,
Disconnected,
Type,
@@ -282,9 +269,6 @@ public:
/// Returns the latest battery status from the controller with parameters
BatteryValues GetBatteryValues() const;
/// Returns the latest camera status from the controller with parameters
CameraValues GetCameraValues() const;
/// Returns the latest status of button input for the hid::HomeButton service
HomeButtonState GetHomeButtons() const;
@@ -312,9 +296,6 @@ public:
/// Returns the latest battery status from the controller
BatteryLevelState GetBattery() const;
/// Returns the latest camera status from the controller
const CameraState& GetCamera() const;
/**
* Sends a specific vibration to the output device
* @return true if vibration had no errors
@@ -334,13 +315,6 @@ public:
*/
bool SetPollingMode(Common::Input::PollingMode polling_mode);
/**
* Sets the desired camera format to be polled from a controller
* @param camera_format size of each frame
* @return true if SetCameraFormat was successfull
*/
bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format);
/// Returns the led pattern corresponding to this emulated controller
LedPattern GetLedPattern() const;
@@ -418,12 +392,6 @@ private:
*/
void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index);
/**
* Updates the camera status of the controller
* @param callback A CallbackStatus containing the camera status
*/
void SetCamera(const Common::Input::CallbackStatus& callback);
/**
* Triggers a callback that something has changed on the controller status
* @param type Input type of the event to trigger
@@ -449,7 +417,6 @@ private:
ControllerMotionParams motion_params;
TriggerParams trigger_params;
BatteryParams battery_params;
CameraParams camera_params;
OutputParams output_params;
ButtonDevices button_devices;
@@ -457,7 +424,6 @@ private:
ControllerMotionDevices motion_devices;
TriggerDevices trigger_devices;
BatteryDevices battery_devices;
CameraDevices camera_devices;
OutputDevices output_devices;
// TAS related variables

View File

@@ -270,20 +270,6 @@ Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatu
return status;
}
Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback) {
Common::Input::CameraStatus camera{};
switch (callback.type) {
case Common::Input::InputType::IrSensor:
camera = callback.camera_status;
break;
default:
LOG_ERROR(Input, "Conversion from type {} to camera not implemented", callback.type);
break;
}
return camera;
}
void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) {
const auto& properties = analog.properties;
float& raw_value = analog.raw_value;

View File

@@ -76,14 +76,6 @@ Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackSta
*/
Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback);
/**
* Converts raw input data into a valid camera status.
*
* @param callback Supported callbacks: Camera.
* @return A valid CameraObject object.
*/
Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback);
/**
* Converts raw analog data into a valid analog value
* @param analog An analog object containing raw data and properties

View File

@@ -1,301 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hid/hid_types.h"
namespace Core::IrSensor {
// This is nn::irsensor::CameraAmbientNoiseLevel
enum class CameraAmbientNoiseLevel : u32 {
Low,
Medium,
High,
Unkown3, // This level can't be reached
};
// This is nn::irsensor::CameraLightTarget
enum class CameraLightTarget : u32 {
AllLeds,
BrightLeds,
DimLeds,
None,
};
// This is nn::irsensor::PackedCameraLightTarget
enum class PackedCameraLightTarget : u8 {
AllLeds,
BrightLeds,
DimLeds,
None,
};
// This is nn::irsensor::AdaptiveClusteringMode
enum class AdaptiveClusteringMode : u32 {
StaticFov,
DynamicFov,
};
// This is nn::irsensor::AdaptiveClusteringTargetDistance
enum class AdaptiveClusteringTargetDistance : u32 {
Near,
Middle,
Far,
};
// This is nn::irsensor::ImageTransferProcessorFormat
enum class ImageTransferProcessorFormat : u32 {
Size320x240,
Size160x120,
Size80x60,
Size40x30,
Size20x15,
};
// This is nn::irsensor::PackedImageTransferProcessorFormat
enum class PackedImageTransferProcessorFormat : u8 {
Size320x240,
Size160x120,
Size80x60,
Size40x30,
Size20x15,
};
// This is nn::irsensor::IrCameraStatus
enum class IrCameraStatus : u32 {
Available,
Unsupported,
Unconnected,
};
// This is nn::irsensor::IrCameraInternalStatus
enum class IrCameraInternalStatus : u32 {
Stopped,
FirmwareUpdateNeeded,
Unkown2,
Unkown3,
Unkown4,
FirmwareVersionRequested,
FirmwareVersionIsInvalid,
Ready,
Setting,
};
// This is nn::irsensor::detail::StatusManager::IrSensorMode
enum class IrSensorMode : u64 {
None,
MomentProcessor,
ClusteringProcessor,
ImageTransferProcessor,
PointingProcessorMarker,
TeraPluginProcessor,
IrLedProcessor,
};
// This is nn::irsensor::ImageProcessorStatus
enum ImageProcessorStatus : u32 {
Stopped,
Running,
};
// This is nn::irsensor::HandAnalysisMode
enum class HandAnalysisMode : u32 {
None,
Silhouette,
Image,
SilhoueteAndImage,
SilhuetteOnly,
};
// This is nn::irsensor::IrSensorFunctionLevel
enum class IrSensorFunctionLevel : u8 {
unknown0,
unknown1,
unknown2,
unknown3,
unknown4,
};
// This is nn::irsensor::MomentProcessorPreprocess
enum class MomentProcessorPreprocess : u32 {
Unkown0,
Unkown1,
};
// This is nn::irsensor::PackedMomentProcessorPreprocess
enum class PackedMomentProcessorPreprocess : u8 {
Unkown0,
Unkown1,
};
// This is nn::irsensor::PointingStatus
enum class PointingStatus : u32 {
Unkown0,
Unkown1,
};
struct IrsRect {
s16 x;
s16 y;
s16 width;
s16 height;
};
struct IrsCentroid {
f32 x;
f32 y;
};
struct CameraConfig {
u64 exposure_time;
CameraLightTarget light_target;
u32 gain;
bool is_negative_used;
INSERT_PADDING_BYTES(7);
};
static_assert(sizeof(CameraConfig) == 0x18, "CameraConfig is an invalid size");
struct PackedCameraConfig {
u64 exposure_time;
PackedCameraLightTarget light_target;
u8 gain;
bool is_negative_used;
INSERT_PADDING_BYTES(5);
};
static_assert(sizeof(PackedCameraConfig) == 0x10, "PackedCameraConfig is an invalid size");
// This is nn::irsensor::IrCameraHandle
struct IrCameraHandle {
u8 npad_id{};
Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
INSERT_PADDING_BYTES(2);
};
static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size");
// This is nn::irsensor::PackedMcuVersion
struct PackedMcuVersion {
u16 major;
u16 minor;
};
static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size");
// This is nn::irsensor::PackedMomentProcessorConfig
struct PackedMomentProcessorConfig {
PackedCameraConfig camera_config;
IrsRect window_of_interest;
PackedMcuVersion required_mcu_version;
PackedMomentProcessorPreprocess preprocess;
u8 preprocess_intensity_threshold;
INSERT_PADDING_BYTES(2);
};
static_assert(sizeof(PackedMomentProcessorConfig) == 0x20,
"PackedMomentProcessorConfig is an invalid size");
// This is nn::irsensor::PackedClusteringProcessorConfig
struct PackedClusteringProcessorConfig {
PackedCameraConfig camera_config;
IrsRect window_of_interest;
PackedMcuVersion required_mcu_version;
u32 pixel_count_min;
u32 pixel_count_max;
u8 object_intensity_min;
bool is_external_light_filter_enabled;
INSERT_PADDING_BYTES(2);
};
static_assert(sizeof(PackedClusteringProcessorConfig) == 0x28,
"PackedClusteringProcessorConfig is an invalid size");
// This is nn::irsensor::PackedImageTransferProcessorConfig
struct PackedImageTransferProcessorConfig {
PackedCameraConfig camera_config;
PackedMcuVersion required_mcu_version;
PackedImageTransferProcessorFormat format;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18,
"PackedImageTransferProcessorConfig is an invalid size");
// This is nn::irsensor::PackedTeraPluginProcessorConfig
struct PackedTeraPluginProcessorConfig {
PackedMcuVersion required_mcu_version;
u8 mode;
u8 unknown_1;
u8 unknown_2;
u8 unknown_3;
};
static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8,
"PackedTeraPluginProcessorConfig is an invalid size");
// This is nn::irsensor::PackedPointingProcessorConfig
struct PackedPointingProcessorConfig {
IrsRect window_of_interest;
PackedMcuVersion required_mcu_version;
};
static_assert(sizeof(PackedPointingProcessorConfig) == 0xC,
"PackedPointingProcessorConfig is an invalid size");
// This is nn::irsensor::PackedFunctionLevel
struct PackedFunctionLevel {
IrSensorFunctionLevel function_level;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size");
// This is nn::irsensor::PackedImageTransferProcessorExConfig
struct PackedImageTransferProcessorExConfig {
PackedCameraConfig camera_config;
PackedMcuVersion required_mcu_version;
PackedImageTransferProcessorFormat origin_format;
PackedImageTransferProcessorFormat trimming_format;
u16 trimming_start_x;
u16 trimming_start_y;
bool is_external_light_filter_enabled;
INSERT_PADDING_BYTES(5);
};
static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20,
"PackedImageTransferProcessorExConfig is an invalid size");
// This is nn::irsensor::PackedIrLedProcessorConfig
struct PackedIrLedProcessorConfig {
PackedMcuVersion required_mcu_version;
u8 light_target;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8,
"PackedIrLedProcessorConfig is an invalid size");
// This is nn::irsensor::HandAnalysisConfig
struct HandAnalysisConfig {
HandAnalysisMode mode;
};
static_assert(sizeof(HandAnalysisConfig) == 0x4, "HandAnalysisConfig is an invalid size");
// This is nn::irsensor::detail::ProcessorState contents are different for each processor
struct ProcessorState {
std::array<u8, 0xE20> processor_raw_data{};
};
static_assert(sizeof(ProcessorState) == 0xE20, "ProcessorState is an invalid size");
// This is nn::irsensor::detail::DeviceFormat
struct DeviceFormat {
Core::IrSensor::IrCameraStatus camera_status{Core::IrSensor::IrCameraStatus::Unconnected};
Core::IrSensor::IrCameraInternalStatus camera_internal_status{
Core::IrSensor::IrCameraInternalStatus::Ready};
Core::IrSensor::IrSensorMode mode{Core::IrSensor::IrSensorMode::None};
ProcessorState state{};
};
static_assert(sizeof(DeviceFormat) == 0xE30, "DeviceFormat is an invalid size");
// This is nn::irsensor::ImageTransferProcessorState
struct ImageTransferProcessorState {
u64 sampling_number;
Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
INSERT_PADDING_BYTES(4);
};
static_assert(sizeof(ImageTransferProcessorState) == 0x10,
"ImageTransferProcessorState is an invalid size");
} // namespace Core::IrSensor

View File

@@ -41,7 +41,12 @@ void GlobalSchedulerContext::PreemptThreads() {
ASSERT(IsLocked());
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
const u32 priority = preemption_priorities[core_id];
KScheduler::RotateScheduledQueue(kernel, core_id, priority);
kernel.Scheduler(core_id).RotateScheduledQueue(core_id, priority);
// Signal an interrupt occurred. For core 3, this is a certainty, as preemption will result
// in the rotator thread being scheduled. For cores 0-2, this is to simulate or system
// interrupts that may have occurred.
kernel.PhysicalCore(core_id).Interrupt();
}
}

View File

@@ -6,7 +6,6 @@
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
namespace Kernel::KInterruptManager {
@@ -16,9 +15,6 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) {
return;
}
// Acknowledge the interrupt.
kernel.PhysicalCore(core_id).ClearInterrupt();
auto& current_thread = GetCurrentThread(kernel);
// If the user disable count is set, we may need to pin the current thread.
@@ -31,9 +27,6 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) {
// Set the interrupt flag for the thread.
GetCurrentThread(kernel).SetInterruptFlag();
}
// Request interrupt scheduling.
kernel.CurrentScheduler()->RequestScheduleOnInterrupt();
}
} // namespace Kernel::KInterruptManager

View File

@@ -27,185 +27,69 @@ static void IncrementScheduledCount(Kernel::KThread* thread) {
}
}
KScheduler::KScheduler(KernelCore& kernel_) : kernel{kernel_} {
m_switch_fiber = std::make_shared<Common::Fiber>([this] {
while (true) {
ScheduleImplFiber();
void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule) {
auto scheduler = kernel.CurrentScheduler();
u32 current_core{0xF};
bool must_context_switch{};
if (scheduler) {
current_core = scheduler->core_id;
// TODO(bunnei): Should be set to true when we deprecate single core
must_context_switch = !kernel.IsPhantomModeForSingleCore();
}
while (cores_pending_reschedule != 0) {
const auto core = static_cast<u32>(std::countr_zero(cores_pending_reschedule));
ASSERT(core < Core::Hardware::NUM_CPU_CORES);
if (!must_context_switch || core != current_core) {
auto& phys_core = kernel.PhysicalCore(core);
phys_core.Interrupt();
}
});
m_state.needs_scheduling = true;
}
KScheduler::~KScheduler() = default;
void KScheduler::SetInterruptTaskRunnable() {
m_state.interrupt_task_runnable = true;
m_state.needs_scheduling = true;
}
void KScheduler::RequestScheduleOnInterrupt() {
m_state.needs_scheduling = true;
if (CanSchedule(kernel)) {
ScheduleOnInterrupt();
}
}
void KScheduler::DisableScheduling(KernelCore& kernel) {
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
GetCurrentThread(kernel).DisableDispatch();
}
void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 1);
auto* scheduler{kernel.CurrentScheduler()};
if (!scheduler || kernel.IsPhantomModeForSingleCore()) {
KScheduler::RescheduleCores(kernel, cores_needing_scheduling);
KScheduler::RescheduleCurrentHLEThread(kernel);
return;
cores_pending_reschedule &= ~(1ULL << core);
}
scheduler->RescheduleOtherCores(cores_needing_scheduling);
if (GetCurrentThread(kernel).GetDisableDispatchCount() > 1) {
GetCurrentThread(kernel).EnableDispatch();
} else {
scheduler->RescheduleCurrentCore();
for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; ++core_id) {
if (kernel.PhysicalCore(core_id).IsInterrupted()) {
KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_id));
}
}
}
void KScheduler::RescheduleCurrentHLEThread(KernelCore& kernel) {
// HACK: we cannot schedule from this thread, it is not a core thread
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1);
// Special case to ensure dummy threads that are waiting block
GetCurrentThread(kernel).IfDummyThreadTryWait();
ASSERT(GetCurrentThread(kernel).GetState() != ThreadState::Waiting);
GetCurrentThread(kernel).EnableDispatch();
}
u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
if (IsSchedulerUpdateNeeded(kernel)) {
return UpdateHighestPriorityThreadsImpl(kernel);
} else {
return 0;
if (must_context_switch) {
auto core_scheduler = kernel.CurrentScheduler();
kernel.ExitSVCProfile();
core_scheduler->RescheduleCurrentCore();
kernel.EnterSVCProfile();
}
}
void KScheduler::Schedule() {
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1);
ASSERT(m_core_id == GetCurrentCoreId(kernel));
ScheduleImpl();
}
void KScheduler::ScheduleOnInterrupt() {
GetCurrentThread(kernel).DisableDispatch();
Schedule();
GetCurrentThread(kernel).EnableDispatch();
}
void KScheduler::PreemptSingleCore() {
GetCurrentThread(kernel).DisableDispatch();
auto* thread = GetCurrentThreadPointer(kernel);
auto& previous_scheduler = kernel.Scheduler(thread->GetCurrentCore());
previous_scheduler.Unload(thread);
Common::Fiber::YieldTo(thread->GetHostContext(), *m_switch_fiber);
GetCurrentThread(kernel).EnableDispatch();
}
void KScheduler::RescheduleCurrentCore() {
ASSERT(!kernel.IsPhantomModeForSingleCore());
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1);
GetCurrentThread(kernel).EnableDispatch();
if (m_state.needs_scheduling.load()) {
// Disable interrupts, and then check again if rescheduling is needed.
// KScopedInterruptDisable intr_disable;
kernel.CurrentScheduler()->RescheduleCurrentCoreImpl();
}
}
void KScheduler::RescheduleCurrentCoreImpl() {
// Check that scheduling is needed.
if (m_state.needs_scheduling.load()) [[likely]] {
GetCurrentThread(kernel).DisableDispatch();
Schedule();
GetCurrentThread(kernel).EnableDispatch();
}
}
void KScheduler::Initialize(KThread* main_thread, KThread* idle_thread, s32 core_id) {
// Set core ID/idle thread/interrupt task manager.
m_core_id = core_id;
m_idle_thread = idle_thread;
// m_state.idle_thread_stack = m_idle_thread->GetStackTop();
// m_state.interrupt_task_manager = &kernel.GetInterruptTaskManager();
// Insert the main thread into the priority queue.
// {
// KScopedSchedulerLock lk{kernel};
// GetPriorityQueue(kernel).PushBack(GetCurrentThreadPointer(kernel));
// SetSchedulerUpdateNeeded(kernel);
// }
// Bind interrupt handler.
// kernel.GetInterruptManager().BindHandler(
// GetSchedulerInterruptHandler(kernel), KInterruptName::Scheduler, m_core_id,
// KInterruptController::PriorityLevel::Scheduler, false, false);
// Set the current thread.
m_current_thread = main_thread;
}
void KScheduler::Activate() {
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1);
// m_state.should_count_idle = KTargetSystem::IsDebugMode();
m_is_active = true;
RescheduleCurrentCore();
}
void KScheduler::OnThreadStart() {
GetCurrentThread(kernel).EnableDispatch();
}
u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) {
if (KThread* prev_highest_thread = m_state.highest_priority_thread;
prev_highest_thread != highest_thread) [[likely]] {
if (prev_highest_thread != nullptr) [[likely]] {
KScopedSpinLock lk{guard};
if (KThread* prev_highest_thread = state.highest_priority_thread;
prev_highest_thread != highest_thread) {
if (prev_highest_thread != nullptr) {
IncrementScheduledCount(prev_highest_thread);
prev_highest_thread->SetLastScheduledTick(kernel.System().CoreTiming().GetCPUTicks());
prev_highest_thread->SetLastScheduledTick(system.CoreTiming().GetCPUTicks());
}
if (m_state.should_count_idle) {
if (highest_thread != nullptr) [[likely]] {
if (state.should_count_idle) {
if (highest_thread != nullptr) {
if (KProcess* process = highest_thread->GetOwnerProcess(); process != nullptr) {
process->SetRunningThread(m_core_id, highest_thread, m_state.idle_count);
process->SetRunningThread(core_id, highest_thread, state.idle_count);
}
} else {
m_state.idle_count++;
state.idle_count++;
}
}
m_state.highest_priority_thread = highest_thread;
m_state.needs_scheduling = true;
return (1ULL << m_core_id);
state.highest_priority_thread = highest_thread;
state.needs_scheduling.store(true);
return (1ULL << core_id);
} else {
return 0;
}
}
u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
ASSERT(IsSchedulerLockedByCurrentThread(kernel));
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Clear that we need to update.
ClearSchedulerUpdateNeeded(kernel);
@@ -214,20 +98,18 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
KThread* top_threads[Core::Hardware::NUM_CPU_CORES];
auto& priority_queue = GetPriorityQueue(kernel);
// We want to go over all cores, finding the highest priority thread and determining if
// scheduling is needed for that core.
/// We want to go over all cores, finding the highest priority thread and determining if
/// scheduling is needed for that core.
for (size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
KThread* top_thread = priority_queue.GetScheduledFront(static_cast<s32>(core_id));
if (top_thread != nullptr) {
// We need to check if the thread's process has a pinned thread.
if (KProcess* parent = top_thread->GetOwnerProcess()) {
// Check that there's a pinned thread other than the current top thread.
if (KThread* pinned = parent->GetPinnedThread(static_cast<s32>(core_id));
pinned != nullptr && pinned != top_thread) {
// We need to prefer threads with kernel waiters to the pinned thread.
if (top_thread->GetNumKernelWaiters() ==
0 /* && top_thread != parent->GetExceptionThread() */) {
// If the pinned thread is runnable, use it.
// If the thread has no waiters, we need to check if the process has a thread pinned.
if (top_thread->GetNumKernelWaiters() == 0) {
if (KProcess* parent = top_thread->GetOwnerProcess(); parent != nullptr) {
if (KThread* pinned = parent->GetPinnedThread(static_cast<s32>(core_id));
pinned != nullptr && pinned != top_thread) {
// We prefer our parent's pinned thread if possible. However, we also don't
// want to schedule un-runnable threads.
if (pinned->GetRawState() == ThreadState::Runnable) {
top_thread = pinned;
} else {
@@ -247,8 +129,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
// Idle cores are bad. We're going to try to migrate threads to each idle core in turn.
while (idle_cores != 0) {
const s32 core_id = static_cast<s32>(std::countr_zero(idle_cores));
const auto core_id = static_cast<u32>(std::countr_zero(idle_cores));
if (KThread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) {
s32 migration_candidates[Core::Hardware::NUM_CPU_CORES];
size_t num_candidates = 0;
@@ -269,6 +150,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
// The suggested thread isn't bound to its core, so we can migrate it!
suggested->SetActiveCore(core_id);
priority_queue.ChangeCore(suggested_core, suggested);
top_threads[core_id] = suggested;
cores_needing_scheduling |=
kernel.Scheduler(core_id).UpdateHighestPriorityThread(top_threads[core_id]);
@@ -301,6 +183,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
// Perform the migration.
suggested->SetActiveCore(core_id);
priority_queue.ChangeCore(candidate_core, suggested);
top_threads[core_id] = suggested;
cores_needing_scheduling |=
kernel.Scheduler(core_id).UpdateHighestPriorityThread(
@@ -317,210 +200,24 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
return cores_needing_scheduling;
}
void KScheduler::SwitchThread(KThread* next_thread) {
KProcess* const cur_process = kernel.CurrentProcess();
KThread* const cur_thread = GetCurrentThreadPointer(kernel);
// We never want to schedule a null thread, so use the idle thread if we don't have a next.
if (next_thread == nullptr) {
next_thread = m_idle_thread;
}
if (next_thread->GetCurrentCore() != m_core_id) {
next_thread->SetCurrentCore(m_core_id);
}
// If we're not actually switching thread, there's nothing to do.
if (next_thread == cur_thread) {
return;
}
// Next thread is now known not to be nullptr, and must not be dispatchable.
ASSERT(next_thread->GetDisableDispatchCount() == 1);
ASSERT(!next_thread->IsDummyThread());
// Update the CPU time tracking variables.
const s64 prev_tick = m_last_context_switch_time;
const s64 cur_tick = kernel.System().CoreTiming().GetCPUTicks();
const s64 tick_diff = cur_tick - prev_tick;
cur_thread->AddCpuTime(m_core_id, tick_diff);
if (cur_process != nullptr) {
cur_process->UpdateCPUTimeTicks(tick_diff);
}
m_last_context_switch_time = cur_tick;
// Update our previous thread.
if (cur_process != nullptr) {
if (!cur_thread->IsTerminationRequested() && cur_thread->GetActiveCore() == m_core_id)
[[likely]] {
m_state.prev_thread = cur_thread;
} else {
m_state.prev_thread = nullptr;
}
}
// Switch the current process, if we're switching processes.
// if (KProcess *next_process = next_thread->GetOwnerProcess(); next_process != cur_process) {
// KProcess::Switch(cur_process, next_process);
// }
// Set the new thread.
SetCurrentThread(kernel, next_thread);
m_current_thread = next_thread;
// Set the new Thread Local region.
// cpu::SwitchThreadLocalRegion(GetInteger(next_thread->GetThreadLocalRegionAddress()));
}
void KScheduler::ScheduleImpl() {
// First, clear the needs scheduling bool.
m_state.needs_scheduling.store(false, std::memory_order_seq_cst);
// Load the appropriate thread pointers for scheduling.
KThread* const cur_thread{GetCurrentThreadPointer(kernel)};
KThread* highest_priority_thread{m_state.highest_priority_thread};
// Check whether there are runnable interrupt tasks.
if (m_state.interrupt_task_runnable) {
// The interrupt task is runnable.
// We want to switch to the interrupt task/idle thread.
highest_priority_thread = nullptr;
}
// If there aren't, we want to check if the highest priority thread is the same as the current
// thread.
if (highest_priority_thread == cur_thread) {
// If they're the same, then we can just return.
return;
}
// The highest priority thread is not the same as the current thread.
// Jump to the switcher and continue executing from there.
m_switch_cur_thread = cur_thread;
m_switch_highest_priority_thread = highest_priority_thread;
m_switch_from_schedule = true;
Common::Fiber::YieldTo(cur_thread->host_context, *m_switch_fiber);
// Returning from ScheduleImpl occurs after this thread has been scheduled again.
}
void KScheduler::ScheduleImplFiber() {
KThread* const cur_thread{m_switch_cur_thread};
KThread* highest_priority_thread{m_switch_highest_priority_thread};
// If we're not coming from scheduling (i.e., we came from SC preemption),
// we should restart the scheduling loop directly. Not accurate to HOS.
if (!m_switch_from_schedule) {
goto retry;
}
// Mark that we are not coming from scheduling anymore.
m_switch_from_schedule = false;
// Save the original thread context.
Unload(cur_thread);
// The current thread's context has been entirely taken care of.
// Now we want to loop until we successfully switch the thread context.
while (true) {
// We're starting to try to do the context switch.
// Check if the highest priority thread is null.
if (!highest_priority_thread) {
// The next thread is nullptr!
// Switch to the idle thread. Note: HOS treats idling as a special case for
// performance. This is not *required* for yuzu's purposes, and for singlecore
// compatibility, we can just move the logic that would go here into the execution
// of the idle thread. If we ever remove singlecore, we should implement this
// accurately to HOS.
highest_priority_thread = m_idle_thread;
}
// We want to try to lock the highest priority thread's context.
// Try to take it.
while (!highest_priority_thread->context_guard.try_lock()) {
// The highest priority thread's context is already locked.
// Check if we need scheduling. If we don't, we can retry directly.
if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) {
// If we do, another core is interfering, and we must start again.
goto retry;
}
}
// It's time to switch the thread.
// Switch to the highest priority thread.
SwitchThread(highest_priority_thread);
// Check if we need scheduling. If we do, then we can't complete the switch and should
// retry.
if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) {
// Our switch failed.
// We should unlock the thread context, and then retry.
highest_priority_thread->context_guard.unlock();
goto retry;
} else {
break;
}
retry:
// We failed to successfully do the context switch, and need to retry.
// Clear needs_scheduling.
m_state.needs_scheduling.store(false, std::memory_order_seq_cst);
// Refresh the highest priority thread.
highest_priority_thread = m_state.highest_priority_thread;
}
// Reload the guest thread context.
Reload(highest_priority_thread);
// Reload the host thread.
Common::Fiber::YieldTo(m_switch_fiber, *highest_priority_thread->host_context);
}
void KScheduler::Unload(KThread* thread) {
auto& cpu_core = kernel.System().ArmInterface(m_core_id);
cpu_core.SaveContext(thread->GetContext32());
cpu_core.SaveContext(thread->GetContext64());
// Save the TPIDR_EL0 system register in case it was modified.
thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
cpu_core.ClearExclusiveState();
// Check if the thread is terminated by checking the DPC flags.
if ((thread->GetStackParameters().dpc_flags & static_cast<u32>(DpcFlag::Terminated)) == 0) {
// The thread isn't terminated, so we want to unlock it.
thread->context_guard.unlock();
}
}
void KScheduler::Reload(KThread* thread) {
auto& cpu_core = kernel.System().ArmInterface(m_core_id);
cpu_core.LoadContext(thread->GetContext32());
cpu_core.LoadContext(thread->GetContext64());
cpu_core.SetTlsAddress(thread->GetTLSAddress());
cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
cpu_core.LoadWatchpointArray(thread->GetOwnerProcess()->GetWatchpoints());
cpu_core.ClearExclusiveState();
}
void KScheduler::ClearPreviousThread(KernelCore& kernel, KThread* thread) {
ASSERT(IsSchedulerLockedByCurrentThread(kernel));
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; ++i) {
// Get an atomic reference to the core scheduler's previous thread.
auto& prev_thread{kernel.Scheduler(i).m_state.prev_thread};
std::atomic_ref<KThread*> prev_thread(kernel.Scheduler(static_cast<s32>(i)).prev_thread);
static_assert(std::atomic_ref<KThread*>::is_always_lock_free);
// Atomically clear the previous thread if it's our target.
KThread* compare = thread;
prev_thread.compare_exchange_strong(compare, nullptr, std::memory_order_seq_cst);
prev_thread.compare_exchange_strong(compare, nullptr);
}
}
void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state) {
ASSERT(IsSchedulerLockedByCurrentThread(kernel));
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Check if the state has changed, because if it hasn't there's nothing to do.
const ThreadState cur_state = thread->GetRawState();
const auto cur_state = thread->GetRawState();
if (cur_state == old_state) {
return;
}
@@ -540,12 +237,12 @@ void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, Threa
}
void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority) {
ASSERT(IsSchedulerLockedByCurrentThread(kernel));
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// If the thread is runnable, we want to change its priority in the queue.
if (thread->GetRawState() == ThreadState::Runnable) {
GetPriorityQueue(kernel).ChangePriority(old_priority,
thread == GetCurrentThreadPointer(kernel), thread);
thread == kernel.GetCurrentEmuThread(), thread);
IncrementScheduledCount(thread);
SetSchedulerUpdateNeeded(kernel);
}
@@ -553,7 +250,7 @@ void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s3
void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread,
const KAffinityMask& old_affinity, s32 old_core) {
ASSERT(IsSchedulerLockedByCurrentThread(kernel));
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// If the thread is runnable, we want to change its affinity in the queue.
if (thread->GetRawState() == ThreadState::Runnable) {
@@ -563,14 +260,15 @@ void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread
}
}
void KScheduler::RotateScheduledQueue(KernelCore& kernel, s32 core_id, s32 priority) {
ASSERT(IsSchedulerLockedByCurrentThread(kernel));
void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
ASSERT(system.GlobalSchedulerContext().IsLocked());
// Get a reference to the priority queue.
auto& kernel = system.Kernel();
auto& priority_queue = GetPriorityQueue(kernel);
// Rotate the front of the queue to the end.
KThread* top_thread = priority_queue.GetScheduledFront(core_id, priority);
KThread* top_thread = priority_queue.GetScheduledFront(cpu_core_id, priority);
KThread* next_thread = nullptr;
if (top_thread != nullptr) {
next_thread = priority_queue.MoveToScheduledBack(top_thread);
@@ -582,7 +280,7 @@ void KScheduler::RotateScheduledQueue(KernelCore& kernel, s32 core_id, s32 prior
// While we have a suggested thread, try to migrate it!
{
KThread* suggested = priority_queue.GetSuggestedFront(core_id, priority);
KThread* suggested = priority_queue.GetSuggestedFront(cpu_core_id, priority);
while (suggested != nullptr) {
// Check if the suggested thread is the top thread on its core.
const s32 suggested_core = suggested->GetActiveCore();
@@ -603,7 +301,7 @@ void KScheduler::RotateScheduledQueue(KernelCore& kernel, s32 core_id, s32 prior
// to the front of the queue.
if (top_on_suggested_core == nullptr ||
top_on_suggested_core->GetPriority() >= HighestCoreMigrationAllowedPriority) {
suggested->SetActiveCore(core_id);
suggested->SetActiveCore(cpu_core_id);
priority_queue.ChangeCore(suggested_core, suggested, true);
IncrementScheduledCount(suggested);
break;
@@ -611,21 +309,22 @@ void KScheduler::RotateScheduledQueue(KernelCore& kernel, s32 core_id, s32 prior
}
// Get the next suggestion.
suggested = priority_queue.GetSamePriorityNext(core_id, suggested);
suggested = priority_queue.GetSamePriorityNext(cpu_core_id, suggested);
}
}
// Now that we might have migrated a thread with the same priority, check if we can do better.
{
KThread* best_thread = priority_queue.GetScheduledFront(core_id);
KThread* best_thread = priority_queue.GetScheduledFront(cpu_core_id);
if (best_thread == GetCurrentThreadPointer(kernel)) {
best_thread = priority_queue.GetScheduledNext(core_id, best_thread);
best_thread = priority_queue.GetScheduledNext(cpu_core_id, best_thread);
}
// If the best thread we can choose has a priority the same or worse than ours, try to
// migrate a higher priority thread.
if (best_thread != nullptr && best_thread->GetPriority() >= priority) {
KThread* suggested = priority_queue.GetSuggestedFront(core_id);
KThread* suggested = priority_queue.GetSuggestedFront(cpu_core_id);
while (suggested != nullptr) {
// If the suggestion's priority is the same as ours, don't bother.
if (suggested->GetPriority() >= best_thread->GetPriority()) {
@@ -644,7 +343,7 @@ void KScheduler::RotateScheduledQueue(KernelCore& kernel, s32 core_id, s32 prior
if (top_on_suggested_core == nullptr ||
top_on_suggested_core->GetPriority() >=
HighestCoreMigrationAllowedPriority) {
suggested->SetActiveCore(core_id);
suggested->SetActiveCore(cpu_core_id);
priority_queue.ChangeCore(suggested_core, suggested, true);
IncrementScheduledCount(suggested);
break;
@@ -652,7 +351,7 @@ void KScheduler::RotateScheduledQueue(KernelCore& kernel, s32 core_id, s32 prior
}
// Get the next suggestion.
suggested = priority_queue.GetSuggestedNext(core_id, suggested);
suggested = priority_queue.GetSuggestedNext(cpu_core_id, suggested);
}
}
}
@@ -661,6 +360,64 @@ void KScheduler::RotateScheduledQueue(KernelCore& kernel, s32 core_id, s32 prior
SetSchedulerUpdateNeeded(kernel);
}
bool KScheduler::CanSchedule(KernelCore& kernel) {
return kernel.GetCurrentEmuThread()->GetDisableDispatchCount() <= 1;
}
bool KScheduler::IsSchedulerUpdateNeeded(const KernelCore& kernel) {
return kernel.GlobalSchedulerContext().scheduler_update_needed.load(std::memory_order_acquire);
}
void KScheduler::SetSchedulerUpdateNeeded(KernelCore& kernel) {
kernel.GlobalSchedulerContext().scheduler_update_needed.store(true, std::memory_order_release);
}
void KScheduler::ClearSchedulerUpdateNeeded(KernelCore& kernel) {
kernel.GlobalSchedulerContext().scheduler_update_needed.store(false, std::memory_order_release);
}
void KScheduler::DisableScheduling(KernelCore& kernel) {
// If we are shutting down the kernel, none of this is relevant anymore.
if (kernel.IsShuttingDown()) {
return;
}
ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 0);
GetCurrentThreadPointer(kernel)->DisableDispatch();
}
void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
// If we are shutting down the kernel, none of this is relevant anymore.
if (kernel.IsShuttingDown()) {
return;
}
auto* current_thread = GetCurrentThreadPointer(kernel);
ASSERT(current_thread->GetDisableDispatchCount() >= 1);
if (current_thread->GetDisableDispatchCount() > 1) {
current_thread->EnableDispatch();
} else {
RescheduleCores(kernel, cores_needing_scheduling);
}
// Special case to ensure dummy threads that are waiting block.
current_thread->IfDummyThreadTryWait();
}
u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
if (IsSchedulerUpdateNeeded(kernel)) {
return UpdateHighestPriorityThreadsImpl(kernel);
} else {
return 0;
}
}
KSchedulerPriorityQueue& KScheduler::GetPriorityQueue(KernelCore& kernel) {
return kernel.GlobalSchedulerContext().priority_queue;
}
void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) {
// Validate preconditions.
ASSERT(CanSchedule(kernel));
@@ -680,7 +437,7 @@ void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) {
// Perform the yield.
{
KScopedSchedulerLock sl{kernel};
KScopedSchedulerLock lock(kernel);
const auto cur_state = cur_thread.GetRawState();
if (cur_state == ThreadState::Runnable) {
@@ -719,7 +476,7 @@ void KScheduler::YieldWithCoreMigration(KernelCore& kernel) {
// Perform the yield.
{
KScopedSchedulerLock sl{kernel};
KScopedSchedulerLock lock(kernel);
const auto cur_state = cur_thread.GetRawState();
if (cur_state == ThreadState::Runnable) {
@@ -739,7 +496,7 @@ void KScheduler::YieldWithCoreMigration(KernelCore& kernel) {
if (KThread* running_on_suggested_core =
(suggested_core >= 0)
? kernel.Scheduler(suggested_core).m_state.highest_priority_thread
? kernel.Scheduler(suggested_core).state.highest_priority_thread
: nullptr;
running_on_suggested_core != suggested) {
// If the current thread's priority is higher than our suggestion's we prefer
@@ -807,7 +564,7 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) {
// Perform the yield.
{
KScopedSchedulerLock sl{kernel};
KScopedSchedulerLock lock(kernel);
const auto cur_state = cur_thread.GetRawState();
if (cur_state == ThreadState::Runnable) {
@@ -864,19 +621,223 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) {
}
}
void KScheduler::RescheduleOtherCores(u64 cores_needing_scheduling) {
if (const u64 core_mask = cores_needing_scheduling & ~(1ULL << m_core_id); core_mask != 0) {
RescheduleCores(kernel, core_mask);
KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, core_id{core_id_} {
switch_fiber = std::make_shared<Common::Fiber>([this] { SwitchToCurrent(); });
state.needs_scheduling.store(true);
state.interrupt_task_thread_runnable = false;
state.should_count_idle = false;
state.idle_count = 0;
state.idle_thread_stack = nullptr;
state.highest_priority_thread = nullptr;
}
void KScheduler::Finalize() {
if (idle_thread) {
idle_thread->Close();
idle_thread = nullptr;
}
}
void KScheduler::RescheduleCores(KernelCore& kernel, u64 core_mask) {
// Send IPI
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
if (core_mask & (1ULL << i)) {
kernel.PhysicalCore(i).Interrupt();
}
KScheduler::~KScheduler() {
ASSERT(!idle_thread);
}
KThread* KScheduler::GetSchedulerCurrentThread() const {
if (auto result = current_thread.load(); result) {
return result;
}
return idle_thread;
}
u64 KScheduler::GetLastContextSwitchTicks() const {
return last_context_switch_time;
}
void KScheduler::RescheduleCurrentCore() {
ASSERT(GetCurrentThread(system.Kernel()).GetDisableDispatchCount() == 1);
auto& phys_core = system.Kernel().PhysicalCore(core_id);
if (phys_core.IsInterrupted()) {
phys_core.ClearInterrupt();
}
guard.Lock();
if (state.needs_scheduling.load()) {
Schedule();
} else {
GetCurrentThread(system.Kernel()).EnableDispatch();
guard.Unlock();
}
}
void KScheduler::OnThreadStart() {
SwitchContextStep2();
}
void KScheduler::Unload(KThread* thread) {
ASSERT(thread);
LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr");
if (thread->IsCallingSvc()) {
thread->ClearIsCallingSvc();
}
auto& physical_core = system.Kernel().PhysicalCore(core_id);
if (!physical_core.IsInitialized()) {
return;
}
Core::ARM_Interface& cpu_core = physical_core.ArmInterface();
cpu_core.SaveContext(thread->GetContext32());
cpu_core.SaveContext(thread->GetContext64());
// Save the TPIDR_EL0 system register in case it was modified.
thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
cpu_core.ClearExclusiveState();
if (!thread->IsTerminationRequested() && thread->GetActiveCore() == core_id) {
prev_thread = thread;
} else {
prev_thread = nullptr;
}
thread->context_guard.unlock();
}
void KScheduler::Reload(KThread* thread) {
LOG_TRACE(Kernel, "core {}, reload thread {}", core_id, thread->GetName());
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
cpu_core.LoadContext(thread->GetContext32());
cpu_core.LoadContext(thread->GetContext64());
cpu_core.LoadWatchpointArray(thread->GetOwnerProcess()->GetWatchpoints());
cpu_core.SetTlsAddress(thread->GetTLSAddress());
cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
cpu_core.ClearExclusiveState();
}
void KScheduler::SwitchContextStep2() {
// Load context of new thread
Reload(GetCurrentThreadPointer(system.Kernel()));
RescheduleCurrentCore();
}
void KScheduler::Schedule() {
ASSERT(GetCurrentThread(system.Kernel()).GetDisableDispatchCount() == 1);
this->ScheduleImpl();
}
void KScheduler::ScheduleImpl() {
KThread* previous_thread = GetCurrentThreadPointer(system.Kernel());
KThread* next_thread = state.highest_priority_thread;
state.needs_scheduling.store(false);
// We never want to schedule a null thread, so use the idle thread if we don't have a next.
if (next_thread == nullptr) {
next_thread = idle_thread;
}
if (next_thread->GetCurrentCore() != core_id) {
next_thread->SetCurrentCore(core_id);
}
// We never want to schedule a dummy thread, as these are only used by host threads for locking.
if (next_thread->GetThreadType() == ThreadType::Dummy) {
ASSERT_MSG(false, "Dummy threads should never be scheduled!");
next_thread = idle_thread;
}
// If we're not actually switching thread, there's nothing to do.
if (next_thread == current_thread.load()) {
previous_thread->EnableDispatch();
guard.Unlock();
return;
}
// Update the CPU time tracking variables.
KProcess* const previous_process = system.Kernel().CurrentProcess();
UpdateLastContextSwitchTime(previous_thread, previous_process);
// Save context for previous thread
Unload(previous_thread);
std::shared_ptr<Common::Fiber>* old_context;
old_context = &previous_thread->GetHostContext();
// Set the new thread.
SetCurrentThread(system.Kernel(), next_thread);
current_thread.store(next_thread);
guard.Unlock();
Common::Fiber::YieldTo(*old_context, *switch_fiber);
/// When a thread wakes up, the scheduler may have changed to other in another core.
auto& next_scheduler = *system.Kernel().CurrentScheduler();
next_scheduler.SwitchContextStep2();
}
void KScheduler::SwitchToCurrent() {
while (true) {
{
KScopedSpinLock lk{guard};
current_thread.store(state.highest_priority_thread);
state.needs_scheduling.store(false);
}
const auto is_switch_pending = [this] {
KScopedSpinLock lk{guard};
return state.needs_scheduling.load();
};
do {
auto next_thread = current_thread.load();
if (next_thread != nullptr) {
const auto locked = next_thread->context_guard.try_lock();
if (state.needs_scheduling.load()) {
next_thread->context_guard.unlock();
break;
}
if (next_thread->GetActiveCore() != core_id) {
next_thread->context_guard.unlock();
break;
}
if (!locked) {
continue;
}
}
auto thread = next_thread ? next_thread : idle_thread;
SetCurrentThread(system.Kernel(), thread);
Common::Fiber::YieldTo(switch_fiber, *thread->GetHostContext());
} while (!is_switch_pending());
}
}
void KScheduler::UpdateLastContextSwitchTime(KThread* thread, KProcess* process) {
const u64 prev_switch_ticks = last_context_switch_time;
const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks();
const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
if (thread != nullptr) {
thread->AddCpuTime(core_id, update_ticks);
}
if (process != nullptr) {
process->UpdateCPUTimeTicks(update_ticks);
}
last_context_switch_time = most_recent_switch_ticks;
}
void KScheduler::Initialize() {
idle_thread = KThread::Create(system.Kernel());
ASSERT(KThread::InitializeIdleThread(system, idle_thread, core_id).IsSuccess());
idle_thread->SetName(fmt::format("IdleThread:{}", core_id));
idle_thread->EnableDispatch();
}
KScopedSchedulerLock::KScopedSchedulerLock(KernelCore& kernel)
: KScopedLock(kernel.GlobalSchedulerContext().SchedulerLock()) {}
KScopedSchedulerLock::~KScopedSchedulerLock() = default;
} // namespace Kernel

View File

@@ -11,7 +11,6 @@
#include "core/hle/kernel/k_scheduler_lock.h"
#include "core/hle/kernel/k_scoped_lock.h"
#include "core/hle/kernel/k_spin_lock.h"
#include "core/hle/kernel/k_thread.h"
namespace Common {
class Fiber;
@@ -24,150 +23,184 @@ class System;
namespace Kernel {
class KernelCore;
class KInterruptTaskManager;
class KProcess;
class SchedulerLock;
class KThread;
class KScopedDisableDispatch;
class KScopedSchedulerLock;
class KScopedSchedulerLockAndSleep;
class KScheduler final {
public:
YUZU_NON_COPYABLE(KScheduler);
YUZU_NON_MOVEABLE(KScheduler);
using LockType = KAbstractSchedulerLock<KScheduler>;
explicit KScheduler(KernelCore& kernel);
explicit KScheduler(Core::System& system_, s32 core_id_);
~KScheduler();
void Initialize(KThread* main_thread, KThread* idle_thread, s32 core_id);
void Activate();
void OnThreadStart();
void Finalize();
/// Reschedules to the next available thread (call after current thread is suspended)
void RescheduleCurrentCore();
/// Reschedules cores pending reschedule, to be called on EnableScheduling.
static void RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule);
/// The next two are for SingleCore Only.
/// Unload current thread before preempting core.
void Unload(KThread* thread);
/// Reload current thread after core preemption.
void Reload(KThread* thread);
void SetInterruptTaskRunnable();
void RequestScheduleOnInterrupt();
void PreemptSingleCore();
/// Gets the current running thread
[[nodiscard]] KThread* GetSchedulerCurrentThread() const;
u64 GetIdleCount() {
return m_state.idle_count;
/// Gets the idle thread
[[nodiscard]] KThread* GetIdleThread() const {
return idle_thread;
}
KThread* GetIdleThread() const {
return m_idle_thread;
/// Returns true if the scheduler is idle
[[nodiscard]] bool IsIdle() const {
return GetSchedulerCurrentThread() == idle_thread;
}
bool IsIdle() const {
return m_current_thread.load() == m_idle_thread;
/// Gets the timestamp for the last context switch in ticks.
[[nodiscard]] u64 GetLastContextSwitchTicks() const;
[[nodiscard]] bool ContextSwitchPending() const {
return state.needs_scheduling.load(std::memory_order_relaxed);
}
KThread* GetPreviousThread() const {
return m_state.prev_thread;
void Initialize();
void OnThreadStart();
[[nodiscard]] std::shared_ptr<Common::Fiber>& ControlContext() {
return switch_fiber;
}
KThread* GetSchedulerCurrentThread() const {
return m_current_thread.load();
[[nodiscard]] const std::shared_ptr<Common::Fiber>& ControlContext() const {
return switch_fiber;
}
s64 GetLastContextSwitchTime() const {
return m_last_context_switch_time;
}
[[nodiscard]] u64 UpdateHighestPriorityThread(KThread* highest_thread);
// Static public API.
static bool CanSchedule(KernelCore& kernel) {
return GetCurrentThread(kernel).GetDisableDispatchCount() == 0;
}
static bool IsSchedulerLockedByCurrentThread(KernelCore& kernel) {
return kernel.GlobalSchedulerContext().scheduler_lock.IsLockedByCurrentThread();
}
/**
* Takes a thread and moves it to the back of the it's priority list.
*
* @note This operation can be redundant and no scheduling is changed if marked as so.
*/
static void YieldWithoutCoreMigration(KernelCore& kernel);
static bool IsSchedulerUpdateNeeded(KernelCore& kernel) {
return kernel.GlobalSchedulerContext().scheduler_update_needed;
}
static void SetSchedulerUpdateNeeded(KernelCore& kernel) {
kernel.GlobalSchedulerContext().scheduler_update_needed = true;
}
static void ClearSchedulerUpdateNeeded(KernelCore& kernel) {
kernel.GlobalSchedulerContext().scheduler_update_needed = false;
}
/**
* Takes a thread and moves it to the back of the it's priority list.
* Afterwards, tries to pick a suggested thread from the suggested queue that has worse time or
* a better priority than the next thread in the core.
*
* @note This operation can be redundant and no scheduling is changed if marked as so.
*/
static void YieldWithCoreMigration(KernelCore& kernel);
static void DisableScheduling(KernelCore& kernel);
static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling);
static u64 UpdateHighestPriorityThreads(KernelCore& kernel);
/**
* Takes a thread and moves it out of the scheduling queue.
* and into the suggested queue. If no thread can be scheduled afterwards in that core,
* a suggested thread is obtained instead.
*
* @note This operation can be redundant and no scheduling is changed if marked as so.
*/
static void YieldToAnyThread(KernelCore& kernel);
static void ClearPreviousThread(KernelCore& kernel, KThread* thread);
/// Notify the scheduler a thread's status has changed.
static void OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state);
/// Notify the scheduler a thread's priority has changed.
static void OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority);
/// Notify the scheduler a thread's core and/or affinity mask has changed.
static void OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread,
const KAffinityMask& old_affinity, s32 old_core);
static void RotateScheduledQueue(KernelCore& kernel, s32 core_id, s32 priority);
static void RescheduleCores(KernelCore& kernel, u64 cores_needing_scheduling);
static void YieldWithoutCoreMigration(KernelCore& kernel);
static void YieldWithCoreMigration(KernelCore& kernel);
static void YieldToAnyThread(KernelCore& kernel);
static bool CanSchedule(KernelCore& kernel);
static bool IsSchedulerUpdateNeeded(const KernelCore& kernel);
static void SetSchedulerUpdateNeeded(KernelCore& kernel);
static void ClearSchedulerUpdateNeeded(KernelCore& kernel);
static void DisableScheduling(KernelCore& kernel);
static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling);
[[nodiscard]] static u64 UpdateHighestPriorityThreads(KernelCore& kernel);
private:
// Static private API.
static KSchedulerPriorityQueue& GetPriorityQueue(KernelCore& kernel) {
return kernel.GlobalSchedulerContext().priority_queue;
}
static u64 UpdateHighestPriorityThreadsImpl(KernelCore& kernel);
friend class GlobalSchedulerContext;
static void RescheduleCurrentHLEThread(KernelCore& kernel);
/**
* Takes care of selecting the new scheduled threads in three steps:
*
* 1. First a thread is selected from the top of the priority queue. If no thread
* is obtained then we move to step two, else we are done.
*
* 2. Second we try to get a suggested thread that's not assigned to any core or
* that is not the top thread in that core.
*
* 3. Third is no suggested thread is found, we do a second pass and pick a running
* thread in another core and swap it with its current thread.
*
* returns the cores needing scheduling.
*/
[[nodiscard]] static u64 UpdateHighestPriorityThreadsImpl(KernelCore& kernel);
// Instanced private API.
void ScheduleImpl();
void ScheduleImplFiber();
void SwitchThread(KThread* next_thread);
[[nodiscard]] static KSchedulerPriorityQueue& GetPriorityQueue(KernelCore& kernel);
void RotateScheduledQueue(s32 cpu_core_id, s32 priority);
void Schedule();
void ScheduleOnInterrupt();
void RescheduleOtherCores(u64 cores_needing_scheduling);
void RescheduleCurrentCore();
void RescheduleCurrentCoreImpl();
/// Switches the CPU's active thread context to that of the specified thread
void ScheduleImpl();
u64 UpdateHighestPriorityThread(KThread* thread);
/// When a thread wakes up, it must run this through it's new scheduler
void SwitchContextStep2();
private:
friend class KScopedDisableDispatch;
/**
* Called on every context switch to update the internal timestamp
* This also updates the running time ticks for the given thread and
* process using the following difference:
*
* ticks += most_recent_ticks - last_context_switch_ticks
*
* The internal tick timestamp for the scheduler is simply the
* most recent tick count retrieved. No special arithmetic is
* applied to it.
*/
void UpdateLastContextSwitchTime(KThread* thread, KProcess* process);
void SwitchToCurrent();
KThread* prev_thread{};
std::atomic<KThread*> current_thread{};
KThread* idle_thread{};
std::shared_ptr<Common::Fiber> switch_fiber{};
struct SchedulingState {
std::atomic<bool> needs_scheduling{false};
bool interrupt_task_runnable{false};
bool should_count_idle{false};
u64 idle_count{0};
KThread* highest_priority_thread{nullptr};
void* idle_thread_stack{nullptr};
std::atomic<KThread*> prev_thread{nullptr};
KInterruptTaskManager* interrupt_task_manager{nullptr};
std::atomic<bool> needs_scheduling{};
bool interrupt_task_thread_runnable{};
bool should_count_idle{};
u64 idle_count{};
KThread* highest_priority_thread{};
void* idle_thread_stack{};
};
KernelCore& kernel;
SchedulingState m_state;
bool m_is_active{false};
s32 m_core_id{0};
s64 m_last_context_switch_time{0};
KThread* m_idle_thread{nullptr};
std::atomic<KThread*> m_current_thread{nullptr};
SchedulingState state;
std::shared_ptr<Common::Fiber> m_switch_fiber{};
KThread* m_switch_cur_thread{};
KThread* m_switch_highest_priority_thread{};
bool m_switch_from_schedule{};
Core::System& system;
u64 last_context_switch_time{};
const s32 core_id;
KSpinLock guard{};
};
class KScopedSchedulerLock : public KScopedLock<KScheduler::LockType> {
class [[nodiscard]] KScopedSchedulerLock : KScopedLock<GlobalSchedulerContext::LockType> {
public:
explicit KScopedSchedulerLock(KernelCore& kernel)
: KScopedLock(kernel.GlobalSchedulerContext().scheduler_lock) {}
~KScopedSchedulerLock() = default;
explicit KScopedSchedulerLock(KernelCore& kernel);
~KScopedSchedulerLock();
};
} // namespace Kernel

View File

@@ -5,11 +5,9 @@
#include <atomic>
#include "common/assert.h"
#include "core/hle/kernel/k_interrupt_manager.h"
#include "core/hle/kernel/k_spin_lock.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
namespace Kernel {

View File

@@ -258,18 +258,7 @@ Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_
}
Result KThread::InitializeDummyThread(KThread* thread) {
// Initialize the thread.
R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy));
// Initialize emulation parameters.
thread->stack_parameters.disable_count = 0;
return ResultSuccess;
}
Result KThread::InitializeMainThread(Core::System& system, KThread* thread, s32 virt_core) {
return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main,
system.GetCpuManager().GetGuestActivateFunc());
return thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy);
}
Result KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) {
@@ -288,7 +277,7 @@ Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThr
KProcess* owner) {
system.Kernel().GlobalSchedulerContext().AddThread(thread);
return InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner,
ThreadType::User, system.GetCpuManager().GetGuestThreadFunc());
ThreadType::User, system.GetCpuManager().GetGuestThreadStartFunc());
}
void KThread::PostDestroy(uintptr_t arg) {
@@ -1069,8 +1058,6 @@ void KThread::Exit() {
// Register the thread as a work task.
KWorkerTaskManager::AddTask(kernel, KWorkerTaskManager::WorkerType::Exit, this);
}
UNREACHABLE_MSG("KThread::Exit() would return");
}
Result KThread::Sleep(s64 timeout) {
@@ -1106,8 +1093,6 @@ void KThread::IfDummyThreadTryWait() {
return;
}
ASSERT(!kernel.IsPhantomModeForSingleCore());
// Block until we are no longer waiting.
std::unique_lock lk(dummy_wait_lock);
dummy_wait_cv.wait(
@@ -1212,13 +1197,16 @@ KScopedDisableDispatch::~KScopedDisableDispatch() {
return;
}
if (GetCurrentThread(kernel).GetDisableDispatchCount() <= 1) {
auto* scheduler = kernel.CurrentScheduler();
// Skip the reschedule if single-core, as dispatch tracking is disabled here.
if (!Settings::values.use_multi_core.GetValue()) {
return;
}
if (scheduler && !kernel.IsPhantomModeForSingleCore()) {
if (GetCurrentThread(kernel).GetDisableDispatchCount() <= 1) {
auto scheduler = kernel.CurrentScheduler();
if (scheduler) {
scheduler->RescheduleCurrentCore();
} else {
KScheduler::RescheduleCurrentHLEThread(kernel);
}
} else {
GetCurrentThread(kernel).EnableDispatch();

View File

@@ -413,9 +413,6 @@ public:
[[nodiscard]] static Result InitializeDummyThread(KThread* thread);
[[nodiscard]] static Result InitializeMainThread(Core::System& system, KThread* thread,
s32 virt_core);
[[nodiscard]] static Result InitializeIdleThread(Core::System& system, KThread* thread,
s32 virt_core);
@@ -483,16 +480,39 @@ public:
return per_core_priority_queue_entry[core];
}
[[nodiscard]] bool IsKernelThread() const {
return GetActiveCore() == 3;
}
[[nodiscard]] bool IsDispatchTrackingDisabled() const {
return is_single_core || IsKernelThread();
}
[[nodiscard]] s32 GetDisableDispatchCount() const {
if (IsDispatchTrackingDisabled()) {
// TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
return 1;
}
return this->GetStackParameters().disable_count;
}
void DisableDispatch() {
if (IsDispatchTrackingDisabled()) {
// TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
return;
}
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
this->GetStackParameters().disable_count++;
}
void EnableDispatch() {
if (IsDispatchTrackingDisabled()) {
// TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
return;
}
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0);
this->GetStackParameters().disable_count--;
}

View File

@@ -64,6 +64,8 @@ struct KernelCore::Impl {
is_phantom_mode_for_singlecore = false;
InitializePhysicalCores();
// Derive the initial memory layout from the emulated board
Init::InitializeSlabResourceCounts(kernel);
DeriveInitialMemoryLayout();
@@ -73,9 +75,9 @@ struct KernelCore::Impl {
InitializeSystemResourceLimit(kernel, system.CoreTiming());
InitializeMemoryLayout();
Init::InitializeKPageBufferSlabHeap(system);
InitializeSchedulers();
InitializeShutdownThreads();
InitializePreemption(kernel);
InitializePhysicalCores();
RegisterHostThread();
}
@@ -134,6 +136,7 @@ struct KernelCore::Impl {
shutdown_threads[core_id] = nullptr;
}
schedulers[core_id]->Finalize();
schedulers[core_id].reset();
}
@@ -196,21 +199,14 @@ struct KernelCore::Impl {
exclusive_monitor =
Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES);
for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
const s32 core{static_cast<s32>(i)};
schedulers[i] = std::make_unique<Kernel::KScheduler>(system.Kernel());
schedulers[i] = std::make_unique<Kernel::KScheduler>(system, i);
cores.emplace_back(i, system, *schedulers[i], interrupts);
}
}
auto* main_thread{Kernel::KThread::Create(system.Kernel())};
main_thread->SetName(fmt::format("MainThread:{}", core));
main_thread->SetCurrentCore(core);
ASSERT(Kernel::KThread::InitializeMainThread(system, main_thread, core).IsSuccess());
auto* idle_thread{Kernel::KThread::Create(system.Kernel())};
idle_thread->SetCurrentCore(core);
ASSERT(Kernel::KThread::InitializeIdleThread(system, idle_thread, core).IsSuccess());
schedulers[i]->Initialize(main_thread, idle_thread, core);
void InitializeSchedulers() {
for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
cores[i].Scheduler().Initialize();
}
}
@@ -1113,11 +1109,10 @@ void KernelCore::Suspend(bool suspended) {
}
void KernelCore::ShutdownCores() {
KScopedSchedulerLock lk{*this};
for (auto* thread : impl->shutdown_threads) {
void(thread->Run());
}
InterruptAllPhysicalCores();
}
bool KernelCore::IsMulticore() const {

View File

@@ -43,7 +43,6 @@ void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) {
void PhysicalCore::Run() {
arm_interface->Run();
arm_interface->ClearExclusiveState();
}
void PhysicalCore::Idle() {

View File

@@ -887,7 +887,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han
const auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
const bool same_thread = current_thread == thread.GetPointerUnsafe();
const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTime();
const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks();
u64 out_ticks = 0;
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
const u64 thread_ticks = current_thread->GetCpuTime();
@@ -3026,6 +3026,11 @@ void Call(Core::System& system, u32 immediate) {
}
kernel.ExitSVCProfile();
if (!thread->IsCallingSvc()) {
auto* host_context = thread->GetHostContext().get();
host_context->Rewind();
}
}
} // namespace Kernel::Svc

View File

@@ -536,6 +536,8 @@ void SoftwareKeyboard::InitializeFrontendNormalKeyboard() {
.sub_text{std::move(sub_text)},
.guide_text{std::move(guide_text)},
.initial_text{initial_text},
.left_optional_symbol_key{swkbd_config_common.left_optional_symbol_key},
.right_optional_symbol_key{swkbd_config_common.right_optional_symbol_key},
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.initial_cursor_position{initial_cursor_position},
@@ -591,6 +593,8 @@ void SoftwareKeyboard::InitializeFrontendInlineKeyboardOld() {
.sub_text{},
.guide_text{},
.initial_text{current_text},
.left_optional_symbol_key{appear_arg.left_optional_symbol_key},
.right_optional_symbol_key{appear_arg.right_optional_symbol_key},
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.initial_cursor_position{initial_cursor_position},
@@ -632,6 +636,8 @@ void SoftwareKeyboard::InitializeFrontendInlineKeyboardNew() {
.sub_text{},
.guide_text{},
.initial_text{current_text},
.left_optional_symbol_key{appear_arg.left_optional_symbol_key},
.right_optional_symbol_key{appear_arg.right_optional_symbol_key},
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.initial_cursor_position{initial_cursor_position},

View File

@@ -19,10 +19,3 @@ constexpr Result InvalidNpadId{ErrorModule::HID, 709};
constexpr Result NpadNotConnected{ErrorModule::HID, 710};
} // namespace Service::HID
namespace Service::IRS {
constexpr Result InvalidProcessorState{ErrorModule::Irsensor, 78};
constexpr Result InvalidIrCameraHandle{ErrorModule::Irsensor, 204};
} // namespace Service::IRS

View File

@@ -2345,8 +2345,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system
std::make_shared<HidSys>(system)->InstallAsService(service_manager);
std::make_shared<HidTmp>(system)->InstallAsService(service_manager);
std::make_shared<Service::IRS::IRS>(system)->InstallAsService(service_manager);
std::make_shared<Service::IRS::IRS_SYS>(system)->InstallAsService(service_manager);
std::make_shared<IRS>(system)->InstallAsService(service_manager);
std::make_shared<IRS_SYS>(system)->InstallAsService(service_manager);
std::make_shared<XCD_SYS>(system)->InstallAsService(service_manager);
}

View File

@@ -1,28 +1,16 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <random>
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/hid/errors.h"
#include "core/hle/service/hid/irs.h"
#include "core/hle/service/hid/irsensor/clustering_processor.h"
#include "core/hle/service/hid/irsensor/image_transfer_processor.h"
#include "core/hle/service/hid/irsensor/ir_led_processor.h"
#include "core/hle/service/hid/irsensor/moment_processor.h"
#include "core/hle/service/hid/irsensor/pointing_processor.h"
#include "core/hle/service/hid/irsensor/tera_plugin_processor.h"
#include "core/memory.h"
namespace Service::IRS {
namespace Service::HID {
IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
// clang-format off
@@ -48,19 +36,14 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
};
// clang-format on
u8* raw_shared_memory = system.Kernel().GetIrsSharedMem().GetPointer();
RegisterHandlers(functions);
shared_memory = std::construct_at(reinterpret_cast<StatusManager*>(raw_shared_memory));
npad_device = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
}
IRS::~IRS() = default;
void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
@@ -71,7 +54,7 @@ void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
@@ -92,7 +75,7 @@ void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
@@ -105,23 +88,17 @@ void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
auto result = IsIrCameraHandleValid(parameters.camera_handle);
if (result.IsSuccess()) {
// TODO: Stop Image processor
result = ResultSuccess;
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
rb.Push(ResultSuccess);
}
void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::IrSensor::PackedMomentProcessorConfig processor_config;
PackedMomentProcessorConfig processor_config;
};
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
@@ -132,28 +109,19 @@ void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
const auto result = IsIrCameraHandleValid(parameters.camera_handle);
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
MakeProcessor<MomentProcessor>(parameters.camera_handle, device);
auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
rb.Push(ResultSuccess);
}
void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::IrSensor::PackedClusteringProcessorConfig processor_config;
PackedClusteringProcessorConfig processor_config;
};
static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
static_assert(sizeof(Parameters) == 0x40, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
@@ -162,27 +130,17 @@ void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
auto result = IsIrCameraHandleValid(parameters.camera_handle);
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
MakeProcessor<ClusteringProcessor>(parameters.camera_handle, device);
auto& image_transfer_processor =
GetProcessor<ClusteringProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
rb.Push(ResultSuccess);
}
void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::IrSensor::PackedImageTransferProcessorConfig processor_config;
PackedImageTransferProcessorConfig processor_config;
u32 transfer_memory_size;
};
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
@@ -193,166 +151,20 @@ void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
auto t_mem =
system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
if (t_mem.IsNull()) {
LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
ASSERT_MSG(t_mem->GetSize() == parameters.transfer_memory_size, "t_mem has incorrect size");
u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress());
LOG_INFO(Service_IRS,
"called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, "
"applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.transfer_memory_size, t_mem->GetSize(), parameters.applet_resource_user_id);
const auto result = IsIrCameraHandleValid(parameters.camera_handle);
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device);
auto& image_transfer_processor =
GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
image_transfer_processor.SetTransferMemoryPointer(transfer_memory);
}
LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, "
"applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.transfer_memory_size, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
rb.Push(ResultSuccess);
}
void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
const auto result = IsIrCameraHandleValid(parameters.camera_handle);
if (result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
const auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
if (device.mode != Core::IrSensor::IrSensorMode::ImageTransferProcessor) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(InvalidProcessorState);
return;
}
std::vector<u8> data{};
const auto& image_transfer_processor =
GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
const auto& state = image_transfer_processor.GetState(data);
ctx.WriteBuffer(data);
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(state);
}
void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
Core::IrSensor::PackedTeraPluginProcessorConfig processor_config;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(
Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
"applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.processor_config.mode, parameters.processor_config.required_mcu_version.major,
parameters.processor_config.required_mcu_version.minor, parameters.applet_resource_user_id);
const auto result = IsIrCameraHandleValid(parameters.camera_handle);
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
MakeProcessor<TeraPluginProcessor>(parameters.camera_handle, device);
auto& image_transfer_processor =
GetProcessor<TeraPluginProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid &&
npad_id != Core::HID::NpadIdType::Handheld) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(Service::HID::InvalidNpadId);
return;
}
Core::IrSensor::IrCameraHandle camera_handle{
.npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)),
.npad_type = Core::HID::NpadStyleIndex::None,
};
LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id,
camera_handle.npad_id, camera_handle.npad_type);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushRaw(camera_handle);
}
void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
const auto processor_config{rp.PopRaw<Core::IrSensor::PackedPointingProcessorConfig>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(
Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, mcu_version={}.{}, applet_resource_user_id={}",
camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major,
processor_config.required_mcu_version.minor, applet_resource_user_id);
auto result = IsIrCameraHandleValid(camera_handle);
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
MakeProcessor<PointingProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
@@ -365,20 +177,93 @@ void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
auto result = IsIrCameraHandleValid(parameters.camera_handle);
if (result.IsSuccess()) {
// TODO: Suspend image processor
result = ResultSuccess;
}
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(ResultSuccess);
rb.PushRaw<u64>(system.CoreTiming().GetCPUTicks());
rb.PushRaw<u32>(0);
}
void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
const auto processor_config{rp.PopRaw<PackedTeraPluginProcessorConfig>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
"applet_resource_user_id={}",
camera_handle.npad_type, camera_handle.npad_id, processor_config.mode,
processor_config.required_mcu_version.major,
processor_config.required_mcu_version.minor, applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
rb.Push(ResultSuccess);
}
void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid &&
npad_id != Core::HID::NpadIdType::Handheld) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(InvalidNpadId);
return;
}
IrCameraHandle camera_handle{
.npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)),
.npad_type = Core::HID::NpadStyleIndex::None,
};
LOG_WARNING(Service_IRS, "(STUBBED) called, npad_id={}, camera_npad_id={}, camera_npad_type={}",
npad_id, camera_handle.npad_id, camera_handle.npad_type);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushRaw(camera_handle);
}
void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
const auto processor_config{rp.PopRaw<PackedPointingProcessorConfig>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(
Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, mcu_version={}.{}, applet_resource_user_id={}",
camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major,
processor_config.required_mcu_version.minor, applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
const auto mcu_version{rp.PopRaw<Core::IrSensor::PackedMcuVersion>()};
const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
const auto mcu_version{rp.PopRaw<PackedMcuVersion>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(
@@ -387,45 +272,37 @@ void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major,
mcu_version.minor);
auto result = IsIrCameraHandleValid(camera_handle);
if (result.IsSuccess()) {
// TODO: Check firmware version
result = ResultSuccess;
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
rb.Push(ResultSuccess);
}
void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
const auto function_level{rp.PopRaw<Core::IrSensor::PackedFunctionLevel>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
struct Parameters {
IrCameraHandle camera_handle;
PackedFunctionLevel function_level;
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
LOG_WARNING(
Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}",
camera_handle.npad_type, camera_handle.npad_id, function_level.function_level,
applet_resource_user_id);
const auto parameters{rp.PopRaw<Parameters>()};
auto result = IsIrCameraHandleValid(camera_handle);
if (result.IsSuccess()) {
// TODO: Set Function level
result = ResultSuccess;
}
LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
rb.Push(ResultSuccess);
}
void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::IrSensor::PackedImageTransferProcessorExConfig processor_config;
PackedImageTransferProcessorExConfig processor_config;
u64 transfer_memory_size;
};
static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
@@ -436,33 +313,20 @@ void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
auto t_mem =
system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress());
LOG_INFO(Service_IRS,
"called, npad_type={}, npad_id={}, transfer_memory_size={}, "
"applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.transfer_memory_size, parameters.applet_resource_user_id);
auto result = IsIrCameraHandleValid(parameters.camera_handle);
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device);
auto& image_transfer_processor =
GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
image_transfer_processor.SetTransferMemoryPointer(transfer_memory);
}
LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, "
"applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.transfer_memory_size, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
rb.Push(ResultSuccess);
}
void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
const auto processor_config{rp.PopRaw<Core::IrSensor::PackedIrLedProcessorConfig>()};
const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
const auto processor_config{rp.PopRaw<PackedIrLedProcessorConfig>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(Service_IRS,
@@ -472,23 +336,14 @@ void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
processor_config.required_mcu_version.major,
processor_config.required_mcu_version.minor, applet_resource_user_id);
auto result = IsIrCameraHandleValid(camera_handle);
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
MakeProcessor<IrLedProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
rb.Push(ResultSuccess);
}
void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
@@ -501,20 +356,14 @@ void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
auto result = IsIrCameraHandleValid(parameters.camera_handle);
if (result.IsSuccess()) {
// TODO: Stop image processor async
result = ResultSuccess;
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
rb.Push(ResultSuccess);
}
void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::PackedFunctionLevel function_level;
PackedFunctionLevel function_level;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
@@ -529,22 +378,7 @@ void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const {
if (camera_handle.npad_id >
static_cast<u8>(NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld))) {
return InvalidIrCameraHandle;
}
if (camera_handle.npad_type != Core::HID::NpadStyleIndex::None) {
return InvalidIrCameraHandle;
}
return ResultSuccess;
}
Core::IrSensor::DeviceFormat& IRS::GetIrCameraSharedMemoryDeviceEntry(
const Core::IrSensor::IrCameraHandle& camera_handle) {
ASSERT_MSG(sizeof(StatusManager::device) > camera_handle.npad_id, "invalid npad_id");
return shared_memory->device[camera_handle.npad_id];
}
IRS::~IRS() = default;
IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} {
// clang-format off
@@ -561,4 +395,4 @@ IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} {
IRS_SYS::~IRS_SYS() = default;
} // namespace Service::IRS
} // namespace Service::HID

View File

@@ -4,19 +4,13 @@
#pragma once
#include "core/hid/hid_types.h"
#include "core/hid/irs_types.h"
#include "core/hle/service/hid/irsensor/processor_base.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Core::HID {
class EmulatedController;
} // namespace Core::HID
namespace Service::IRS {
namespace Service::HID {
class IRS final : public ServiceFramework<IRS> {
public:
@@ -24,19 +18,234 @@ public:
~IRS() override;
private:
// This is nn::irsensor::detail::AruidFormat
struct AruidFormat {
u64 sensor_aruid;
u64 sensor_aruid_status;
// This is nn::irsensor::IrCameraStatus
enum IrCameraStatus : u32 {
Available,
Unsupported,
Unconnected,
};
static_assert(sizeof(AruidFormat) == 0x10, "AruidFormat is an invalid size");
// This is nn::irsensor::detail::StatusManager
struct StatusManager {
std::array<Core::IrSensor::DeviceFormat, 9> device;
std::array<AruidFormat, 5> aruid;
// This is nn::irsensor::IrCameraInternalStatus
enum IrCameraInternalStatus : u32 {
Stopped,
FirmwareUpdateNeeded,
Unkown2,
Unkown3,
Unkown4,
FirmwareVersionRequested,
FirmwareVersionIsInvalid,
Ready,
Setting,
};
static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size");
// This is nn::irsensor::detail::StatusManager::IrSensorMode
enum IrSensorMode : u64 {
None,
MomentProcessor,
ClusteringProcessor,
ImageTransferProcessor,
PointingProcessorMarker,
TeraPluginProcessor,
IrLedProcessor,
};
// This is nn::irsensor::ImageProcessorStatus
enum ImageProcessorStatus : u8 {
stopped,
running,
};
// This is nn::irsensor::ImageTransferProcessorFormat
enum ImageTransferProcessorFormat : u8 {
Size320x240,
Size160x120,
Size80x60,
Size40x30,
Size20x15,
};
// This is nn::irsensor::AdaptiveClusteringMode
enum AdaptiveClusteringMode : u8 {
StaticFov,
DynamicFov,
};
// This is nn::irsensor::AdaptiveClusteringTargetDistance
enum AdaptiveClusteringTargetDistance : u8 {
Near,
Middle,
Far,
};
// This is nn::irsensor::IrsHandAnalysisMode
enum IrsHandAnalysisMode : u8 {
Silhouette,
Image,
SilhoueteAndImage,
SilhuetteOnly,
};
// This is nn::irsensor::IrSensorFunctionLevel
enum IrSensorFunctionLevel : u8 {
unknown0,
unknown1,
unknown2,
unknown3,
unknown4,
};
// This is nn::irsensor::IrCameraHandle
struct IrCameraHandle {
u8 npad_id{};
Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
INSERT_PADDING_BYTES(2);
};
static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size");
struct IrsRect {
s16 x;
s16 y;
s16 width;
s16 height;
};
// This is nn::irsensor::PackedMcuVersion
struct PackedMcuVersion {
u16 major;
u16 minor;
};
static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size");
// This is nn::irsensor::MomentProcessorConfig
struct MomentProcessorConfig {
u64 exposire_time;
u8 light_target;
u8 gain;
u8 is_negative_used;
INSERT_PADDING_BYTES(7);
IrsRect window_of_interest;
u8 preprocess;
u8 preprocess_intensity_threshold;
INSERT_PADDING_BYTES(5);
};
static_assert(sizeof(MomentProcessorConfig) == 0x28,
"MomentProcessorConfig is an invalid size");
// This is nn::irsensor::PackedMomentProcessorConfig
struct PackedMomentProcessorConfig {
u64 exposire_time;
u8 light_target;
u8 gain;
u8 is_negative_used;
INSERT_PADDING_BYTES(5);
IrsRect window_of_interest;
PackedMcuVersion required_mcu_version;
u8 preprocess;
u8 preprocess_intensity_threshold;
INSERT_PADDING_BYTES(2);
};
static_assert(sizeof(PackedMomentProcessorConfig) == 0x20,
"PackedMomentProcessorConfig is an invalid size");
// This is nn::irsensor::ClusteringProcessorConfig
struct ClusteringProcessorConfig {
u64 exposire_time;
u32 light_target;
u32 gain;
u8 is_negative_used;
INSERT_PADDING_BYTES(7);
IrsRect window_of_interest;
u32 pixel_count_min;
u32 pixel_count_max;
u32 object_intensity_min;
u8 is_external_light_filter_enabled;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(ClusteringProcessorConfig) == 0x30,
"ClusteringProcessorConfig is an invalid size");
// This is nn::irsensor::PackedClusteringProcessorConfig
struct PackedClusteringProcessorConfig {
u64 exposire_time;
u8 light_target;
u8 gain;
u8 is_negative_used;
INSERT_PADDING_BYTES(5);
IrsRect window_of_interest;
PackedMcuVersion required_mcu_version;
u32 pixel_count_min;
u32 pixel_count_max;
u32 object_intensity_min;
u8 is_external_light_filter_enabled;
INSERT_PADDING_BYTES(2);
};
static_assert(sizeof(PackedClusteringProcessorConfig) == 0x30,
"PackedClusteringProcessorConfig is an invalid size");
// This is nn::irsensor::PackedImageTransferProcessorConfig
struct PackedImageTransferProcessorConfig {
u64 exposire_time;
u8 light_target;
u8 gain;
u8 is_negative_used;
INSERT_PADDING_BYTES(5);
PackedMcuVersion required_mcu_version;
u8 format;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18,
"PackedImageTransferProcessorConfig is an invalid size");
// This is nn::irsensor::PackedTeraPluginProcessorConfig
struct PackedTeraPluginProcessorConfig {
PackedMcuVersion required_mcu_version;
u8 mode;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8,
"PackedTeraPluginProcessorConfig is an invalid size");
// This is nn::irsensor::PackedPointingProcessorConfig
struct PackedPointingProcessorConfig {
IrsRect window_of_interest;
PackedMcuVersion required_mcu_version;
};
static_assert(sizeof(PackedPointingProcessorConfig) == 0xC,
"PackedPointingProcessorConfig is an invalid size");
// This is nn::irsensor::PackedFunctionLevel
struct PackedFunctionLevel {
IrSensorFunctionLevel function_level;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size");
// This is nn::irsensor::PackedImageTransferProcessorExConfig
struct PackedImageTransferProcessorExConfig {
u64 exposire_time;
u8 light_target;
u8 gain;
u8 is_negative_used;
INSERT_PADDING_BYTES(5);
PackedMcuVersion required_mcu_version;
ImageTransferProcessorFormat origin_format;
ImageTransferProcessorFormat trimming_format;
u16 trimming_start_x;
u16 trimming_start_y;
u8 is_external_light_filter_enabled;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20,
"PackedImageTransferProcessorExConfig is an invalid size");
// This is nn::irsensor::PackedIrLedProcessorConfig
struct PackedIrLedProcessorConfig {
PackedMcuVersion required_mcu_version;
u8 light_target;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8,
"PackedIrLedProcessorConfig is an invalid size");
void ActivateIrsensor(Kernel::HLERequestContext& ctx);
void DeactivateIrsensor(Kernel::HLERequestContext& ctx);
@@ -56,56 +265,6 @@ private:
void RunIrLedProcessor(Kernel::HLERequestContext& ctx);
void StopImageProcessorAsync(Kernel::HLERequestContext& ctx);
void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const;
Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry(
const Core::IrSensor::IrCameraHandle& camera_handle);
template <typename T>
void MakeProcessor(const Core::IrSensor::IrCameraHandle& handle,
Core::IrSensor::DeviceFormat& device_state) {
const auto index = static_cast<std::size_t>(handle.npad_id);
if (index > sizeof(processors)) {
LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
return;
}
processors[index] = std::make_unique<T>(device_state);
}
template <typename T>
void MakeProcessorWithCoreContext(const Core::IrSensor::IrCameraHandle& handle,
Core::IrSensor::DeviceFormat& device_state) {
const auto index = static_cast<std::size_t>(handle.npad_id);
if (index > sizeof(processors)) {
LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
return;
}
processors[index] = std::make_unique<T>(system.HIDCore(), device_state, index);
}
template <typename T>
T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) {
const auto index = static_cast<std::size_t>(handle.npad_id);
if (index > sizeof(processors)) {
LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
return static_cast<T&>(*processors[0]);
}
return static_cast<T&>(*processors[index]);
}
template <typename T>
const T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) const {
const auto index = static_cast<std::size_t>(handle.npad_id);
if (index > sizeof(processors)) {
LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
return static_cast<T&>(*processors[0]);
}
return static_cast<T&>(*processors[index]);
}
Core::HID::EmulatedController* npad_device = nullptr;
StatusManager* shared_memory = nullptr;
std::array<std::unique_ptr<ProcessorBase>, 9> processors{};
};
class IRS_SYS final : public ServiceFramework<IRS_SYS> {
@@ -114,4 +273,4 @@ public:
~IRS_SYS() override;
};
} // namespace Service::IRS
} // namespace Service::HID

View File

@@ -1,34 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/hid/irsensor/clustering_processor.h"
namespace Service::IRS {
ClusteringProcessor::ClusteringProcessor(Core::IrSensor::DeviceFormat& device_format)
: device(device_format) {
device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor;
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
}
ClusteringProcessor::~ClusteringProcessor() = default;
void ClusteringProcessor::StartProcessor() {}
void ClusteringProcessor::SuspendProcessor() {}
void ClusteringProcessor::StopProcessor() {}
void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) {
current_config.camera_config.exposure_time = config.camera_config.exposure_time;
current_config.camera_config.gain = config.camera_config.gain;
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
current_config.camera_config.light_target =
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
current_config.pixel_count_min = config.pixel_count_min;
current_config.pixel_count_max = config.pixel_count_max;
current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled;
current_config.object_intensity_min = config.object_intensity_min;
}
} // namespace Service::IRS

View File

@@ -1,74 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hid/irs_types.h"
#include "core/hle/service/hid/irsensor/processor_base.h"
namespace Service::IRS {
class ClusteringProcessor final : public ProcessorBase {
public:
explicit ClusteringProcessor(Core::IrSensor::DeviceFormat& device_format);
~ClusteringProcessor() override;
// Called when the processor is initialized
void StartProcessor() override;
// Called when the processor is suspended
void SuspendProcessor() override;
// Called when the processor is stopped
void StopProcessor() override;
// Sets config parameters of the camera
void SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config);
private:
// This is nn::irsensor::ClusteringProcessorConfig
struct ClusteringProcessorConfig {
Core::IrSensor::CameraConfig camera_config;
Core::IrSensor::IrsRect window_of_interest;
u32 pixel_count_min;
u32 pixel_count_max;
u32 object_intensity_min;
bool is_external_light_filter_enabled;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(ClusteringProcessorConfig) == 0x30,
"ClusteringProcessorConfig is an invalid size");
// This is nn::irsensor::AdaptiveClusteringProcessorConfig
struct AdaptiveClusteringProcessorConfig {
Core::IrSensor::AdaptiveClusteringMode mode;
Core::IrSensor::AdaptiveClusteringTargetDistance target_distance;
};
static_assert(sizeof(AdaptiveClusteringProcessorConfig) == 0x8,
"AdaptiveClusteringProcessorConfig is an invalid size");
// This is nn::irsensor::ClusteringData
struct ClusteringData {
f32 average_intensity;
Core::IrSensor::IrsCentroid centroid;
u32 pixel_count;
Core::IrSensor::IrsRect bound;
};
static_assert(sizeof(ClusteringData) == 0x18, "ClusteringData is an invalid size");
// This is nn::irsensor::ClusteringProcessorState
struct ClusteringProcessorState {
s64 sampling_number;
u64 timestamp;
u8 object_count;
INSERT_PADDING_BYTES(3);
Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
std::array<ClusteringData, 0x10> data;
};
static_assert(sizeof(ClusteringProcessorState) == 0x198,
"ClusteringProcessorState is an invalid size");
ClusteringProcessorConfig current_config{};
Core::IrSensor::DeviceFormat& device;
};
} // namespace Service::IRS

View File

@@ -1,150 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/irsensor/image_transfer_processor.h"
namespace Service::IRS {
ImageTransferProcessor::ImageTransferProcessor(Core::HID::HIDCore& hid_core_,
Core::IrSensor::DeviceFormat& device_format,
std::size_t npad_index)
: device{device_format} {
npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index);
Core::HID::ControllerUpdateCallback engine_callback{
.on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); },
.is_npad_service = true,
};
callback_key = npad_device->SetCallback(engine_callback);
device.mode = Core::IrSensor::IrSensorMode::ImageTransferProcessor;
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
}
ImageTransferProcessor::~ImageTransferProcessor() {
npad_device->DeleteCallback(callback_key);
};
void ImageTransferProcessor::StartProcessor() {
is_active = true;
device.camera_status = Core::IrSensor::IrCameraStatus::Available;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready;
processor_state.sampling_number = 0;
processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
}
void ImageTransferProcessor::SuspendProcessor() {}
void ImageTransferProcessor::StopProcessor() {}
void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) {
if (type != Core::HID::ControllerTriggerType::IrSensor) {
return;
}
if (!is_transfer_memory_set) {
return;
}
const auto camera_data = npad_device->GetCamera();
// This indicates how much ambient light is precent
processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
processor_state.sampling_number = camera_data.sample;
if (camera_data.format != current_config.origin_format) {
LOG_WARNING(Service_IRS, "Wrong Input format {} expected {}", camera_data.format,
current_config.origin_format);
memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
return;
}
if (current_config.origin_format > current_config.trimming_format) {
LOG_WARNING(Service_IRS, "Origin format {} is smaller than trimming format {}",
current_config.origin_format, current_config.trimming_format);
memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
return;
}
std::vector<u8> window_data{};
const auto origin_width = GetDataWidth(current_config.origin_format);
const auto origin_height = GetDataHeight(current_config.origin_format);
const auto trimming_width = GetDataWidth(current_config.trimming_format);
const auto trimming_height = GetDataHeight(current_config.trimming_format);
window_data.resize(GetDataSize(current_config.trimming_format));
if (trimming_width + current_config.trimming_start_x > origin_width ||
trimming_height + current_config.trimming_start_y > origin_height) {
LOG_WARNING(Service_IRS,
"Trimming area ({}, {}, {}, {}) is outside of origin area ({}, {})",
current_config.trimming_start_x, current_config.trimming_start_y,
trimming_width, trimming_height, origin_width, origin_height);
memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
return;
}
for (std::size_t y = 0; y < trimming_height; y++) {
for (std::size_t x = 0; x < trimming_width; x++) {
const std::size_t window_index = (y * trimming_width) + x;
const std::size_t origin_index =
((y + current_config.trimming_start_y) * origin_width) + x +
current_config.trimming_start_x;
window_data[window_index] = camera_data.data[origin_index];
}
}
memcpy(transfer_memory, window_data.data(), GetDataSize(current_config.trimming_format));
if (!IsProcessorActive()) {
StartProcessor();
}
}
void ImageTransferProcessor::SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config) {
current_config.camera_config.exposure_time = config.camera_config.exposure_time;
current_config.camera_config.gain = config.camera_config.gain;
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
current_config.camera_config.light_target =
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
current_config.origin_format =
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format);
current_config.trimming_format =
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format);
current_config.trimming_start_x = 0;
current_config.trimming_start_y = 0;
npad_device->SetCameraFormat(current_config.origin_format);
}
void ImageTransferProcessor::SetConfig(
Core::IrSensor::PackedImageTransferProcessorExConfig config) {
current_config.camera_config.exposure_time = config.camera_config.exposure_time;
current_config.camera_config.gain = config.camera_config.gain;
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
current_config.camera_config.light_target =
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
current_config.origin_format =
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.origin_format);
current_config.trimming_format =
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.trimming_format);
current_config.trimming_start_x = config.trimming_start_x;
current_config.trimming_start_y = config.trimming_start_y;
npad_device->SetCameraFormat(current_config.origin_format);
}
void ImageTransferProcessor::SetTransferMemoryPointer(u8* t_mem) {
is_transfer_memory_set = true;
transfer_memory = t_mem;
}
Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState(
std::vector<u8>& data) const {
const auto size = GetDataSize(current_config.trimming_format);
data.resize(size);
memcpy(data.data(), transfer_memory, size);
return processor_state;
}
} // namespace Service::IRS

View File

@@ -1,73 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hid/irs_types.h"
#include "core/hle/service/hid/irsensor/processor_base.h"
namespace Core::HID {
class EmulatedController;
} // namespace Core::HID
namespace Service::IRS {
class ImageTransferProcessor final : public ProcessorBase {
public:
explicit ImageTransferProcessor(Core::HID::HIDCore& hid_core_,
Core::IrSensor::DeviceFormat& device_format,
std::size_t npad_index);
~ImageTransferProcessor() override;
// Called when the processor is initialized
void StartProcessor() override;
// Called when the processor is suspended
void SuspendProcessor() override;
// Called when the processor is stopped
void StopProcessor() override;
// Sets config parameters of the camera
void SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config);
void SetConfig(Core::IrSensor::PackedImageTransferProcessorExConfig config);
// Transfer memory where the image data will be stored
void SetTransferMemoryPointer(u8* t_mem);
Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const;
private:
// This is nn::irsensor::ImageTransferProcessorConfig
struct ImageTransferProcessorConfig {
Core::IrSensor::CameraConfig camera_config;
Core::IrSensor::ImageTransferProcessorFormat format;
};
static_assert(sizeof(ImageTransferProcessorConfig) == 0x20,
"ImageTransferProcessorConfig is an invalid size");
// This is nn::irsensor::ImageTransferProcessorExConfig
struct ImageTransferProcessorExConfig {
Core::IrSensor::CameraConfig camera_config;
Core::IrSensor::ImageTransferProcessorFormat origin_format;
Core::IrSensor::ImageTransferProcessorFormat trimming_format;
u16 trimming_start_x;
u16 trimming_start_y;
bool is_external_light_filter_enabled;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(ImageTransferProcessorExConfig) == 0x28,
"ImageTransferProcessorExConfig is an invalid size");
void OnControllerUpdate(Core::HID::ControllerTriggerType type);
ImageTransferProcessorExConfig current_config{};
Core::IrSensor::ImageTransferProcessorState processor_state{};
Core::IrSensor::DeviceFormat& device;
Core::HID::EmulatedController* npad_device;
int callback_key{};
u8* transfer_memory = nullptr;
bool is_transfer_memory_set = false;
};
} // namespace Service::IRS

View File

@@ -1,27 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/hid/irsensor/ir_led_processor.h"
namespace Service::IRS {
IrLedProcessor::IrLedProcessor(Core::IrSensor::DeviceFormat& device_format)
: device(device_format) {
device.mode = Core::IrSensor::IrSensorMode::IrLedProcessor;
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
}
IrLedProcessor::~IrLedProcessor() = default;
void IrLedProcessor::StartProcessor() {}
void IrLedProcessor::SuspendProcessor() {}
void IrLedProcessor::StopProcessor() {}
void IrLedProcessor::SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config) {
current_config.light_target =
static_cast<Core::IrSensor::CameraLightTarget>(config.light_target);
}
} // namespace Service::IRS

View File

@@ -1,47 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hid/irs_types.h"
#include "core/hle/service/hid/irsensor/processor_base.h"
namespace Service::IRS {
class IrLedProcessor final : public ProcessorBase {
public:
explicit IrLedProcessor(Core::IrSensor::DeviceFormat& device_format);
~IrLedProcessor() override;
// Called when the processor is initialized
void StartProcessor() override;
// Called when the processor is suspended
void SuspendProcessor() override;
// Called when the processor is stopped
void StopProcessor() override;
// Sets config parameters of the camera
void SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config);
private:
// This is nn::irsensor::IrLedProcessorConfig
struct IrLedProcessorConfig {
Core::IrSensor::CameraLightTarget light_target;
};
static_assert(sizeof(IrLedProcessorConfig) == 0x4, "IrLedProcessorConfig is an invalid size");
struct IrLedProcessorState {
s64 sampling_number;
u64 timestamp;
std::array<u8, 0x8> data;
};
static_assert(sizeof(IrLedProcessorState) == 0x18, "IrLedProcessorState is an invalid size");
IrLedProcessorConfig current_config{};
Core::IrSensor::DeviceFormat& device;
};
} // namespace Service::IRS

View File

@@ -1,34 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/hid/irsensor/moment_processor.h"
namespace Service::IRS {
MomentProcessor::MomentProcessor(Core::IrSensor::DeviceFormat& device_format)
: device(device_format) {
device.mode = Core::IrSensor::IrSensorMode::MomentProcessor;
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
}
MomentProcessor::~MomentProcessor() = default;
void MomentProcessor::StartProcessor() {}
void MomentProcessor::SuspendProcessor() {}
void MomentProcessor::StopProcessor() {}
void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) {
current_config.camera_config.exposure_time = config.camera_config.exposure_time;
current_config.camera_config.gain = config.camera_config.gain;
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
current_config.camera_config.light_target =
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
current_config.window_of_interest = config.window_of_interest;
current_config.preprocess =
static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess);
current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold;
}
} // namespace Service::IRS

View File

@@ -1,61 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hid/irs_types.h"
#include "core/hle/service/hid/irsensor/processor_base.h"
namespace Service::IRS {
class MomentProcessor final : public ProcessorBase {
public:
explicit MomentProcessor(Core::IrSensor::DeviceFormat& device_format);
~MomentProcessor() override;
// Called when the processor is initialized
void StartProcessor() override;
// Called when the processor is suspended
void SuspendProcessor() override;
// Called when the processor is stopped
void StopProcessor() override;
// Sets config parameters of the camera
void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config);
private:
// This is nn::irsensor::MomentProcessorConfig
struct MomentProcessorConfig {
Core::IrSensor::CameraConfig camera_config;
Core::IrSensor::IrsRect window_of_interest;
Core::IrSensor::MomentProcessorPreprocess preprocess;
u32 preprocess_intensity_threshold;
};
static_assert(sizeof(MomentProcessorConfig) == 0x28,
"MomentProcessorConfig is an invalid size");
// This is nn::irsensor::MomentStatistic
struct MomentStatistic {
f32 average_intensity;
Core::IrSensor::IrsCentroid centroid;
};
static_assert(sizeof(MomentStatistic) == 0xC, "MomentStatistic is an invalid size");
// This is nn::irsensor::MomentProcessorState
struct MomentProcessorState {
s64 sampling_number;
u64 timestamp;
Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
INSERT_PADDING_BYTES(4);
std::array<MomentStatistic, 0x30> stadistic;
};
static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size");
MomentProcessorConfig current_config{};
Core::IrSensor::DeviceFormat& device;
};
} // namespace Service::IRS

View File

@@ -1,26 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/hid/irsensor/pointing_processor.h"
namespace Service::IRS {
PointingProcessor::PointingProcessor(Core::IrSensor::DeviceFormat& device_format)
: device(device_format) {
device.mode = Core::IrSensor::IrSensorMode::PointingProcessorMarker;
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
}
PointingProcessor::~PointingProcessor() = default;
void PointingProcessor::StartProcessor() {}
void PointingProcessor::SuspendProcessor() {}
void PointingProcessor::StopProcessor() {}
void PointingProcessor::SetConfig(Core::IrSensor::PackedPointingProcessorConfig config) {
current_config.window_of_interest = config.window_of_interest;
}
} // namespace Service::IRS

View File

@@ -1,61 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hid/irs_types.h"
#include "core/hle/service/hid/irsensor/processor_base.h"
namespace Service::IRS {
class PointingProcessor final : public ProcessorBase {
public:
explicit PointingProcessor(Core::IrSensor::DeviceFormat& device_format);
~PointingProcessor() override;
// Called when the processor is initialized
void StartProcessor() override;
// Called when the processor is suspended
void SuspendProcessor() override;
// Called when the processor is stopped
void StopProcessor() override;
// Sets config parameters of the camera
void SetConfig(Core::IrSensor::PackedPointingProcessorConfig config);
private:
// This is nn::irsensor::PointingProcessorConfig
struct PointingProcessorConfig {
Core::IrSensor::IrsRect window_of_interest;
};
static_assert(sizeof(PointingProcessorConfig) == 0x8,
"PointingProcessorConfig is an invalid size");
struct PointingProcessorMarkerData {
u8 pointing_status;
INSERT_PADDING_BYTES(3);
u32 unknown;
float unkown_float1;
float position_x;
float position_y;
float unkown_float2;
Core::IrSensor::IrsRect window_of_interest;
};
static_assert(sizeof(PointingProcessorMarkerData) == 0x20,
"PointingProcessorMarkerData is an invalid size");
struct PointingProcessorMarkerState {
s64 sampling_number;
u64 timestamp;
std::array<PointingProcessorMarkerData, 0x3> data;
};
static_assert(sizeof(PointingProcessorMarkerState) == 0x70,
"PointingProcessorMarkerState is an invalid size");
PointingProcessorConfig current_config{};
Core::IrSensor::DeviceFormat& device;
};
} // namespace Service::IRS

View File

@@ -1,67 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/hid/irsensor/processor_base.h"
namespace Service::IRS {
ProcessorBase::ProcessorBase() {}
ProcessorBase::~ProcessorBase() = default;
bool ProcessorBase::IsProcessorActive() const {
return is_active;
}
std::size_t ProcessorBase::GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const {
switch (format) {
case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
return 320 * 240;
case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
return 160 * 120;
case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
return 80 * 60;
case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
return 40 * 30;
case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
return 20 * 15;
default:
return 0;
}
}
std::size_t ProcessorBase::GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const {
switch (format) {
case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
return 320;
case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
return 160;
case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
return 80;
case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
return 40;
case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
return 20;
default:
return 0;
}
}
std::size_t ProcessorBase::GetDataHeight(
Core::IrSensor::ImageTransferProcessorFormat format) const {
switch (format) {
case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
return 240;
case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
return 120;
case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
return 60;
case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
return 30;
case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
return 15;
default:
return 0;
}
}
} // namespace Service::IRS

View File

@@ -1,33 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hid/irs_types.h"
namespace Service::IRS {
class ProcessorBase {
public:
explicit ProcessorBase();
virtual ~ProcessorBase();
virtual void StartProcessor() = 0;
virtual void SuspendProcessor() = 0;
virtual void StopProcessor() = 0;
bool IsProcessorActive() const;
protected:
/// Returns the number of bytes the image uses
std::size_t GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const;
/// Returns the width of the image
std::size_t GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const;
/// Returns the height of the image
std::size_t GetDataHeight(Core::IrSensor::ImageTransferProcessorFormat format) const;
bool is_active{false};
};
} // namespace Service::IRS

View File

@@ -1,29 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/hid/irsensor/tera_plugin_processor.h"
namespace Service::IRS {
TeraPluginProcessor::TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format)
: device(device_format) {
device.mode = Core::IrSensor::IrSensorMode::TeraPluginProcessor;
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
}
TeraPluginProcessor::~TeraPluginProcessor() = default;
void TeraPluginProcessor::StartProcessor() {}
void TeraPluginProcessor::SuspendProcessor() {}
void TeraPluginProcessor::StopProcessor() {}
void TeraPluginProcessor::SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config) {
current_config.mode = config.mode;
current_config.unknown_1 = config.unknown_1;
current_config.unknown_2 = config.unknown_2;
current_config.unknown_3 = config.unknown_3;
}
} // namespace Service::IRS

View File

@@ -1,53 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hid/irs_types.h"
#include "core/hle/service/hid/irsensor/processor_base.h"
namespace Service::IRS {
class TeraPluginProcessor final : public ProcessorBase {
public:
explicit TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format);
~TeraPluginProcessor() override;
// Called when the processor is initialized
void StartProcessor() override;
// Called when the processor is suspended
void SuspendProcessor() override;
// Called when the processor is stopped
void StopProcessor() override;
// Sets config parameters of the camera
void SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config);
private:
// This is nn::irsensor::TeraPluginProcessorConfig
struct TeraPluginProcessorConfig {
u8 mode;
u8 unknown_1;
u8 unknown_2;
u8 unknown_3;
};
static_assert(sizeof(TeraPluginProcessorConfig) == 0x4,
"TeraPluginProcessorConfig is an invalid size");
struct TeraPluginProcessorState {
s64 sampling_number;
u64 timestamp;
Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
std::array<u8, 0x12c> data;
};
static_assert(sizeof(TeraPluginProcessorState) == 0x140,
"TeraPluginProcessorState is an invalid size");
TeraPluginProcessorConfig current_config{};
Core::IrSensor::DeviceFormat& device;
};
} // namespace Service::IRS

View File

@@ -18,8 +18,8 @@ namespace {
} // Anonymous namespace
#include "core/internal_network/network.h"
#include "core/internal_network/network_interface.h"
#include "core/network/network.h"
#include "core/network/network_interface.h"
namespace Service::NIFM {

View File

@@ -13,8 +13,8 @@
#include "core/hle/kernel/k_thread.h"
#include "core/hle/service/sockets/bsd.h"
#include "core/hle/service/sockets/sockets_translate.h"
#include "core/internal_network/network.h"
#include "core/internal_network/sockets.h"
#include "core/network/network.h"
#include "core/network/sockets.h"
namespace Service::Sockets {

View File

@@ -16,7 +16,7 @@ class System;
namespace Network {
class Socket;
} // namespace Network
}
namespace Service::Sockets {

View File

@@ -7,7 +7,7 @@
#include "common/common_types.h"
#include "core/hle/service/sockets/sockets.h"
#include "core/hle/service/sockets/sockets_translate.h"
#include "core/internal_network/network.h"
#include "core/network/network.h"
namespace Service::Sockets {

View File

@@ -7,7 +7,7 @@
#include "common/common_types.h"
#include "core/hle/service/sockets/sockets.h"
#include "core/internal_network/network.h"
#include "core/network/network.h"
namespace Service::Sockets {

View File

@@ -29,9 +29,9 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/internal_network/network.h"
#include "core/internal_network/network_interface.h"
#include "core/internal_network/sockets.h"
#include "core/network/network.h"
#include "core/network/network_interface.h"
#include "core/network/sockets.h"
namespace Network {

View File

@@ -11,7 +11,7 @@
#include "common/logging/log.h"
#include "common/settings.h"
#include "common/string_util.h"
#include "core/internal_network/network_interface.h"
#include "core/network/network_interface.h"
#ifdef _WIN32
#include <iphlpapi.h>

View File

@@ -3,7 +3,6 @@
#pragma once
#include <map>
#include <memory>
#include <utility>
@@ -13,7 +12,7 @@
#endif
#include "common/common_types.h"
#include "core/internal_network/network.h"
#include "core/network/network.h"
// TODO: C++20 Replace std::vector usages with std::span

View File

@@ -1,6 +1,4 @@
add_library(input_common STATIC
drivers/camera.cpp
drivers/camera.h
drivers/gc_adapter.cpp
drivers/gc_adapter.h
drivers/keyboard.cpp

View File

@@ -1,82 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <fmt/format.h>
#include "common/param_package.h"
#include "input_common/drivers/camera.h"
namespace InputCommon {
constexpr PadIdentifier identifier = {
.guid = Common::UUID{},
.port = 0,
.pad = 0,
};
Camera::Camera(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
PreSetController(identifier);
}
void Camera::SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data) {
const std::size_t desired_width = getImageWidth();
const std::size_t desired_height = getImageHeight();
status.data.resize(desired_width * desired_height);
// Resize image to desired format
for (std::size_t y = 0; y < desired_height; y++) {
for (std::size_t x = 0; x < desired_width; x++) {
const std::size_t pixel_index = y * desired_width + x;
const std::size_t old_x = width * x / desired_width;
const std::size_t old_y = height * y / desired_height;
const std::size_t data_pixel_index = old_y * width + old_x;
status.data[pixel_index] = static_cast<u8>(data[data_pixel_index] & 0xFF);
}
}
SetCamera(identifier, status);
}
std::size_t Camera::getImageWidth() const {
switch (status.format) {
case Common::Input::CameraFormat::Size320x240:
return 320;
case Common::Input::CameraFormat::Size160x120:
return 160;
case Common::Input::CameraFormat::Size80x60:
return 80;
case Common::Input::CameraFormat::Size40x30:
return 40;
case Common::Input::CameraFormat::Size20x15:
return 20;
case Common::Input::CameraFormat::None:
default:
return 0;
}
}
std::size_t Camera::getImageHeight() const {
switch (status.format) {
case Common::Input::CameraFormat::Size320x240:
return 240;
case Common::Input::CameraFormat::Size160x120:
return 120;
case Common::Input::CameraFormat::Size80x60:
return 60;
case Common::Input::CameraFormat::Size40x30:
return 30;
case Common::Input::CameraFormat::Size20x15:
return 15;
case Common::Input::CameraFormat::None:
default:
return 0;
}
}
Common::Input::CameraError Camera::SetCameraFormat(
[[maybe_unused]] const PadIdentifier& identifier_,
const Common::Input::CameraFormat camera_format) {
status.format = camera_format;
return Common::Input::CameraError::None;
}
} // namespace InputCommon

View File

@@ -1,29 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "input_common/input_engine.h"
namespace InputCommon {
/**
* A button device factory representing a keyboard. It receives keyboard events and forward them
* to all button devices it created.
*/
class Camera final : public InputEngine {
public:
explicit Camera(std::string input_engine_);
void SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data);
std::size_t getImageWidth() const;
std::size_t getImageHeight() const;
Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_,
Common::Input::CameraFormat camera_format) override;
Common::Input::CameraStatus status{};
};
} // namespace InputCommon

View File

@@ -85,7 +85,7 @@ enum RegisterFlags : u8 {
struct Version {};
/**
* Requests the server to send information about what controllers are plugged into the ports
* In yuzu's case, we only have one controller, so for simplicity's sake, we can just send a
* In citra's case, we only have one controller, so for simplicity's sake, we can just send a
* request explicitly for the first controller port and leave it at that. In the future it would be
* nice to make this configurable
*/

View File

@@ -90,18 +90,6 @@ void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const B
TriggerOnMotionChange(identifier, motion, value);
}
void InputEngine::SetCamera(const PadIdentifier& identifier,
const Common::Input::CameraStatus& value) {
{
std::scoped_lock lock{mutex};
ControllerData& controller = controller_list.at(identifier);
if (!configuring) {
controller.camera = value;
}
}
TriggerOnCameraChange(identifier, value);
}
bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
std::scoped_lock lock{mutex};
const auto controller_iter = controller_list.find(identifier);
@@ -177,18 +165,6 @@ BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion)
return controller.motions.at(motion);
}
Common::Input::CameraStatus InputEngine::GetCamera(const PadIdentifier& identifier) const {
std::scoped_lock lock{mutex};
const auto controller_iter = controller_list.find(identifier);
if (controller_iter == controller_list.cend()) {
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
identifier.pad, identifier.port);
return {};
}
const ControllerData& controller = controller_iter->second;
return controller.camera;
}
void InputEngine::ResetButtonState() {
for (const auto& controller : controller_list) {
for (const auto& button : controller.second.buttons) {
@@ -341,20 +317,6 @@ void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int mot
});
}
void InputEngine::TriggerOnCameraChange(const PadIdentifier& identifier,
[[maybe_unused]] const Common::Input::CameraStatus& value) {
std::scoped_lock lock{mutex_callback};
for (const auto& poller_pair : callback_list) {
const InputIdentifier& poller = poller_pair.second;
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Camera, 0)) {
continue;
}
if (poller.callback.on_change) {
poller.callback.on_change();
}
}
}
bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier,
const PadIdentifier& identifier, EngineInputType type,
int index) const {

View File

@@ -36,12 +36,11 @@ struct BasicMotion {
// Types of input that are stored in the engine
enum class EngineInputType {
None,
Analog,
Battery,
Button,
Camera,
HatButton,
Analog,
Motion,
Battery,
};
namespace std {
@@ -116,17 +115,10 @@ public:
// Sets polling mode to a controller
virtual Common::Input::PollingError SetPollingMode(
[[maybe_unused]] const PadIdentifier& identifier,
[[maybe_unused]] const Common::Input::PollingMode polling_mode) {
[[maybe_unused]] const Common::Input::PollingMode vibration) {
return Common::Input::PollingError::NotSupported;
}
// Sets camera format to a controller
virtual Common::Input::CameraError SetCameraFormat(
[[maybe_unused]] const PadIdentifier& identifier,
[[maybe_unused]] Common::Input::CameraFormat camera_format) {
return Common::Input::CameraError::NotSupported;
}
// Returns the engine name
[[nodiscard]] const std::string& GetEngineName() const;
@@ -182,7 +174,6 @@ public:
f32 GetAxis(const PadIdentifier& identifier, int axis) const;
Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const;
BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const;
Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const;
int SetCallback(InputIdentifier input_identifier);
void SetMappingCallback(MappingCallback callback);
@@ -194,7 +185,6 @@ protected:
void SetAxis(const PadIdentifier& identifier, int axis, f32 value);
void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value);
void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value);
virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const {
return "Unknown";
@@ -207,7 +197,6 @@ private:
std::unordered_map<int, float> axes;
std::unordered_map<int, BasicMotion> motions;
Common::Input::BatteryLevel battery{};
Common::Input::CameraStatus camera{};
};
void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value);
@@ -216,8 +205,6 @@ private:
void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
void TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
const BasicMotion& value);
void TriggerOnCameraChange(const PadIdentifier& identifier,
const Common::Input::CameraStatus& value);
bool IsInputIdentifierEqual(const InputIdentifier& input_identifier,
const PadIdentifier& identifier, EngineInputType type,

View File

@@ -664,47 +664,6 @@ private:
InputEngine* input_engine;
};
class InputFromCamera final : public Common::Input::InputDevice {
public:
explicit InputFromCamera(PadIdentifier identifier_, InputEngine* input_engine_)
: identifier(identifier_), input_engine(input_engine_) {
UpdateCallback engine_callback{[this]() { OnChange(); }};
const InputIdentifier input_identifier{
.identifier = identifier,
.type = EngineInputType::Camera,
.index = 0,
.callback = engine_callback,
};
callback_key = input_engine->SetCallback(input_identifier);
}
~InputFromCamera() override {
input_engine->DeleteCallback(callback_key);
}
Common::Input::CameraStatus GetStatus() const {
return input_engine->GetCamera(identifier);
}
void ForceUpdate() override {
OnChange();
}
void OnChange() {
const Common::Input::CallbackStatus status{
.type = Common::Input::InputType::IrSensor,
.camera_status = GetStatus(),
};
TriggerOnChange(status);
}
private:
const PadIdentifier identifier;
int callback_key;
InputEngine* input_engine;
};
class OutputFromIdentifier final : public Common::Input::OutputDevice {
public:
explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_)
@@ -723,10 +682,6 @@ public:
return input_engine->SetPollingMode(identifier, polling_mode);
}
Common::Input::CameraError SetCameraFormat(Common::Input::CameraFormat camera_format) override {
return input_engine->SetCameraFormat(identifier, camera_format);
}
private:
const PadIdentifier identifier;
InputEngine* input_engine;
@@ -965,18 +920,6 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateMotionDevice(
properties_y, properties_z, input_engine.get());
}
std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateCameraDevice(
const Common::ParamPackage& params) {
const PadIdentifier identifier = {
.guid = Common::UUID{params.Get("guid", "")},
.port = static_cast<std::size_t>(params.Get("port", 0)),
.pad = static_cast<std::size_t>(params.Get("pad", 0)),
};
input_engine->PreSetController(identifier);
return std::make_unique<InputFromCamera>(identifier, input_engine.get());
}
InputFactory::InputFactory(std::shared_ptr<InputEngine> input_engine_)
: input_engine(std::move(input_engine_)) {}
@@ -985,9 +928,6 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::Create(
if (params.Has("battery")) {
return CreateBatteryDevice(params);
}
if (params.Has("camera")) {
return CreateCameraDevice(params);
}
if (params.Has("button") && params.Has("axis")) {
return CreateTriggerDevice(params);
}

View File

@@ -211,17 +211,6 @@ private:
*/
std::unique_ptr<Common::Input::InputDevice> CreateMotionDevice(Common::ParamPackage params);
/**
* Creates a camera device from the parameters given.
* @param params contains parameters for creating the device:
* - "guid": text string for identifying controllers
* - "port": port of the connected device
* - "pad": slot of the connected controller
* @returns a unique input device with the parameters specified
*/
std::unique_ptr<Common::Input::InputDevice> CreateCameraDevice(
const Common::ParamPackage& params);
std::shared_ptr<InputEngine> input_engine;
};
} // namespace InputCommon

View File

@@ -5,7 +5,6 @@
#include <memory>
#include "common/input.h"
#include "common/param_package.h"
#include "input_common/drivers/camera.h"
#include "input_common/drivers/gc_adapter.h"
#include "input_common/drivers/keyboard.h"
#include "input_common/drivers/mouse.h"
@@ -79,15 +78,6 @@ struct InputSubsystem::Impl {
Common::Input::RegisterFactory<Common::Input::OutputDevice>(tas_input->GetEngineName(),
tas_output_factory);
camera = std::make_shared<Camera>("camera");
camera->SetMappingCallback(mapping_callback);
camera_input_factory = std::make_shared<InputFactory>(camera);
camera_output_factory = std::make_shared<OutputFactory>(camera);
Common::Input::RegisterFactory<Common::Input::InputDevice>(camera->GetEngineName(),
camera_input_factory);
Common::Input::RegisterFactory<Common::Input::OutputDevice>(camera->GetEngineName(),
camera_output_factory);
#ifdef HAVE_SDL2
sdl = std::make_shared<SDLDriver>("sdl");
sdl->SetMappingCallback(mapping_callback);
@@ -327,7 +317,6 @@ struct InputSubsystem::Impl {
std::shared_ptr<TouchScreen> touch_screen;
std::shared_ptr<TasInput::Tas> tas_input;
std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
std::shared_ptr<Camera> camera;
std::shared_ptr<InputFactory> keyboard_factory;
std::shared_ptr<InputFactory> mouse_factory;
@@ -335,14 +324,12 @@ struct InputSubsystem::Impl {
std::shared_ptr<InputFactory> touch_screen_factory;
std::shared_ptr<InputFactory> udp_client_input_factory;
std::shared_ptr<InputFactory> tas_input_factory;
std::shared_ptr<InputFactory> camera_input_factory;
std::shared_ptr<OutputFactory> keyboard_output_factory;
std::shared_ptr<OutputFactory> mouse_output_factory;
std::shared_ptr<OutputFactory> gcadapter_output_factory;
std::shared_ptr<OutputFactory> udp_client_output_factory;
std::shared_ptr<OutputFactory> tas_output_factory;
std::shared_ptr<OutputFactory> camera_output_factory;
#ifdef HAVE_SDL2
std::shared_ptr<SDLDriver> sdl;
@@ -395,14 +382,6 @@ const TasInput::Tas* InputSubsystem::GetTas() const {
return impl->tas_input.get();
}
Camera* InputSubsystem::GetCamera() {
return impl->camera.get();
}
const Camera* InputSubsystem::GetCamera() const {
return impl->camera.get();
}
std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
return impl->GetInputDevices();
}

View File

@@ -30,7 +30,6 @@ enum Values : int;
}
namespace InputCommon {
class Camera;
class Keyboard;
class Mouse;
class TouchScreen;
@@ -93,15 +92,9 @@ public:
/// Retrieves the underlying tas input device.
[[nodiscard]] TasInput::Tas* GetTas();
/// Retrieves the underlying tas input device.
/// Retrieves the underlying tas input device.
[[nodiscard]] const TasInput::Tas* GetTas() const;
/// Retrieves the underlying camera input device.
[[nodiscard]] Camera* GetCamera();
/// Retrieves the underlying camera input device.
[[nodiscard]] const Camera* GetCamera() const;
/**
* Returns all available input devices that this Factory can create a new device with.
* Each returned ParamPackage should have a `display` field used for display, a `engine` field

View File

@@ -1,16 +0,0 @@
add_library(network STATIC
network.cpp
network.h
packet.cpp
packet.h
room.cpp
room.h
room_member.cpp
room_member.h
verify_user.cpp
verify_user.h
)
create_target_directory_groups(network)
target_link_libraries(network PRIVATE common enet Boost::boost)

View File

@@ -1,50 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/logging/log.h"
#include "enet/enet.h"
#include "network/network.h"
namespace Network {
RoomNetwork::RoomNetwork() {
m_room = std::make_shared<Room>();
m_room_member = std::make_shared<RoomMember>();
}
bool RoomNetwork::Init() {
if (enet_initialize() != 0) {
LOG_ERROR(Network, "Error initalizing ENet");
return false;
}
m_room = std::make_shared<Room>();
m_room_member = std::make_shared<RoomMember>();
LOG_DEBUG(Network, "initialized OK");
return true;
}
std::weak_ptr<Room> RoomNetwork::GetRoom() {
return m_room;
}
std::weak_ptr<RoomMember> RoomNetwork::GetRoomMember() {
return m_room_member;
}
void RoomNetwork::Shutdown() {
if (m_room_member) {
if (m_room_member->IsConnected())
m_room_member->Leave();
m_room_member.reset();
}
if (m_room) {
if (m_room->GetState() == Room::State::Open)
m_room->Destroy();
m_room.reset();
}
enet_deinitialize();
LOG_DEBUG(Network, "shutdown OK");
}
} // namespace Network

View File

@@ -1,33 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include "network/room.h"
#include "network/room_member.h"
namespace Network {
class RoomNetwork {
public:
RoomNetwork();
/// Initializes and registers the network device, the room, and the room member.
bool Init();
/// Returns a pointer to the room handle
std::weak_ptr<Room> GetRoom();
/// Returns a pointer to the room member handle
std::weak_ptr<RoomMember> GetRoomMember();
/// Unregisters the network device, the room, and the room member and shut them down.
void Shutdown();
private:
std::shared_ptr<RoomMember> m_room_member; ///< RoomMember (Client) for network games
std::shared_ptr<Room> m_room; ///< Room (Server) for network games
};
} // namespace Network

View File

@@ -1,262 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#ifdef _WIN32
#include <winsock2.h>
#else
#include <arpa/inet.h>
#endif
#include <cstring>
#include <string>
#include "network/packet.h"
namespace Network {
#ifndef htonll
static u64 htonll(u64 x) {
return ((1 == htonl(1)) ? (x) : ((uint64_t)htonl((x)&0xFFFFFFFF) << 32) | htonl((x) >> 32));
}
#endif
#ifndef ntohll
static u64 ntohll(u64 x) {
return ((1 == ntohl(1)) ? (x) : ((uint64_t)ntohl((x)&0xFFFFFFFF) << 32) | ntohl((x) >> 32));
}
#endif
void Packet::Append(const void* in_data, std::size_t size_in_bytes) {
if (in_data && (size_in_bytes > 0)) {
std::size_t start = data.size();
data.resize(start + size_in_bytes);
std::memcpy(&data[start], in_data, size_in_bytes);
}
}
void Packet::Read(void* out_data, std::size_t size_in_bytes) {
if (out_data && CheckSize(size_in_bytes)) {
std::memcpy(out_data, &data[read_pos], size_in_bytes);
read_pos += size_in_bytes;
}
}
void Packet::Clear() {
data.clear();
read_pos = 0;
is_valid = true;
}
const void* Packet::GetData() const {
return !data.empty() ? &data[0] : nullptr;
}
void Packet::IgnoreBytes(u32 length) {
read_pos += length;
}
std::size_t Packet::GetDataSize() const {
return data.size();
}
bool Packet::EndOfPacket() const {
return read_pos >= data.size();
}
Packet::operator bool() const {
return is_valid;
}
Packet& Packet::Read(bool& out_data) {
u8 value{};
if (Read(value)) {
out_data = (value != 0);
}
return *this;
}
Packet& Packet::Read(s8& out_data) {
Read(&out_data, sizeof(out_data));
return *this;
}
Packet& Packet::Read(u8& out_data) {
Read(&out_data, sizeof(out_data));
return *this;
}
Packet& Packet::Read(s16& out_data) {
s16 value{};
Read(&value, sizeof(value));
out_data = ntohs(value);
return *this;
}
Packet& Packet::Read(u16& out_data) {
u16 value{};
Read(&value, sizeof(value));
out_data = ntohs(value);
return *this;
}
Packet& Packet::Read(s32& out_data) {
s32 value{};
Read(&value, sizeof(value));
out_data = ntohl(value);
return *this;
}
Packet& Packet::Read(u32& out_data) {
u32 value{};
Read(&value, sizeof(value));
out_data = ntohl(value);
return *this;
}
Packet& Packet::Read(s64& out_data) {
s64 value{};
Read(&value, sizeof(value));
out_data = ntohll(value);
return *this;
}
Packet& Packet::Read(u64& out_data) {
u64 value{};
Read(&value, sizeof(value));
out_data = ntohll(value);
return *this;
}
Packet& Packet::Read(float& out_data) {
Read(&out_data, sizeof(out_data));
return *this;
}
Packet& Packet::Read(double& out_data) {
Read(&out_data, sizeof(out_data));
return *this;
}
Packet& Packet::Read(char* out_data) {
// First extract string length
u32 length = 0;
Read(length);
if ((length > 0) && CheckSize(length)) {
// Then extract characters
std::memcpy(out_data, &data[read_pos], length);
out_data[length] = '\0';
// Update reading position
read_pos += length;
}
return *this;
}
Packet& Packet::Read(std::string& out_data) {
// First extract string length
u32 length = 0;
Read(length);
out_data.clear();
if ((length > 0) && CheckSize(length)) {
// Then extract characters
out_data.assign(&data[read_pos], length);
// Update reading position
read_pos += length;
}
return *this;
}
Packet& Packet::Write(bool in_data) {
Write(static_cast<u8>(in_data));
return *this;
}
Packet& Packet::Write(s8 in_data) {
Append(&in_data, sizeof(in_data));
return *this;
}
Packet& Packet::Write(u8 in_data) {
Append(&in_data, sizeof(in_data));
return *this;
}
Packet& Packet::Write(s16 in_data) {
s16 toWrite = htons(in_data);
Append(&toWrite, sizeof(toWrite));
return *this;
}
Packet& Packet::Write(u16 in_data) {
u16 toWrite = htons(in_data);
Append(&toWrite, sizeof(toWrite));
return *this;
}
Packet& Packet::Write(s32 in_data) {
s32 toWrite = htonl(in_data);
Append(&toWrite, sizeof(toWrite));
return *this;
}
Packet& Packet::Write(u32 in_data) {
u32 toWrite = htonl(in_data);
Append(&toWrite, sizeof(toWrite));
return *this;
}
Packet& Packet::Write(s64 in_data) {
s64 toWrite = htonll(in_data);
Append(&toWrite, sizeof(toWrite));
return *this;
}
Packet& Packet::Write(u64 in_data) {
u64 toWrite = htonll(in_data);
Append(&toWrite, sizeof(toWrite));
return *this;
}
Packet& Packet::Write(float in_data) {
Append(&in_data, sizeof(in_data));
return *this;
}
Packet& Packet::Write(double in_data) {
Append(&in_data, sizeof(in_data));
return *this;
}
Packet& Packet::Write(const char* in_data) {
// First insert string length
u32 length = static_cast<u32>(std::strlen(in_data));
Write(length);
// Then insert characters
Append(in_data, length * sizeof(char));
return *this;
}
Packet& Packet::Write(const std::string& in_data) {
// First insert string length
u32 length = static_cast<u32>(in_data.size());
Write(length);
// Then insert characters
if (length > 0)
Append(in_data.c_str(), length * sizeof(std::string::value_type));
return *this;
}
bool Packet::CheckSize(std::size_t size) {
is_valid = is_valid && (read_pos + size <= data.size());
return is_valid;
}
} // namespace Network

View File

@@ -1,165 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <vector>
#include "common/common_types.h"
namespace Network {
/// A class that serializes data for network transfer. It also handles endianess
class Packet {
public:
Packet() = default;
~Packet() = default;
/**
* Append data to the end of the packet
* @param data Pointer to the sequence of bytes to append
* @param size_in_bytes Number of bytes to append
*/
void Append(const void* data, std::size_t size_in_bytes);
/**
* Reads data from the current read position of the packet
* @param out_data Pointer where the data should get written to
* @param size_in_bytes Number of bytes to read
*/
void Read(void* out_data, std::size_t size_in_bytes);
/**
* Clear the packet
* After calling Clear, the packet is empty.
*/
void Clear();
/**
* Ignores bytes while reading
* @param length THe number of bytes to ignore
*/
void IgnoreBytes(u32 length);
/**
* Get a pointer to the data contained in the packet
* @return Pointer to the data
*/
const void* GetData() const;
/**
* This function returns the number of bytes pointed to by
* what getData returns.
* @return Data size, in bytes
*/
std::size_t GetDataSize() const;
/**
* This function is useful to know if there is some data
* left to be read, without actually reading it.
* @return True if all data was read, false otherwise
*/
bool EndOfPacket() const;
explicit operator bool() const;
/// Overloads of read function to read data from the packet
Packet& Read(bool& out_data);
Packet& Read(s8& out_data);
Packet& Read(u8& out_data);
Packet& Read(s16& out_data);
Packet& Read(u16& out_data);
Packet& Read(s32& out_data);
Packet& Read(u32& out_data);
Packet& Read(s64& out_data);
Packet& Read(u64& out_data);
Packet& Read(float& out_data);
Packet& Read(double& out_data);
Packet& Read(char* out_data);
Packet& Read(std::string& out_data);
template <typename T>
Packet& Read(std::vector<T>& out_data);
template <typename T, std::size_t S>
Packet& Read(std::array<T, S>& out_data);
/// Overloads of write function to write data into the packet
Packet& Write(bool in_data);
Packet& Write(s8 in_data);
Packet& Write(u8 in_data);
Packet& Write(s16 in_data);
Packet& Write(u16 in_data);
Packet& Write(s32 in_data);
Packet& Write(u32 in_data);
Packet& Write(s64 in_data);
Packet& Write(u64 in_data);
Packet& Write(float in_data);
Packet& Write(double in_data);
Packet& Write(const char* in_data);
Packet& Write(const std::string& in_data);
template <typename T>
Packet& Write(const std::vector<T>& in_data);
template <typename T, std::size_t S>
Packet& Write(const std::array<T, S>& data);
private:
/**
* Check if the packet can extract a given number of bytes
* This function updates accordingly the state of the packet.
* @param size Size to check
* @return True if size bytes can be read from the packet
*/
bool CheckSize(std::size_t size);
// Member data
std::vector<char> data; ///< Data stored in the packet
std::size_t read_pos = 0; ///< Current reading position in the packet
bool is_valid = true; ///< Reading state of the packet
};
template <typename T>
Packet& Packet::Read(std::vector<T>& out_data) {
// First extract the size
u32 size = 0;
Read(size);
out_data.resize(size);
// Then extract the data
for (std::size_t i = 0; i < out_data.size(); ++i) {
T character;
Read(character);
out_data[i] = character;
}
return *this;
}
template <typename T, std::size_t S>
Packet& Packet::Read(std::array<T, S>& out_data) {
for (std::size_t i = 0; i < out_data.size(); ++i) {
T character;
Read(character);
out_data[i] = character;
}
return *this;
}
template <typename T>
Packet& Packet::Write(const std::vector<T>& in_data) {
// First insert the size
Write(static_cast<u32>(in_data.size()));
// Then insert the data
for (std::size_t i = 0; i < in_data.size(); ++i) {
Write(in_data[i]);
}
return *this;
}
template <typename T, std::size_t S>
Packet& Packet::Write(const std::array<T, S>& in_data) {
for (std::size_t i = 0; i < in_data.size(); ++i) {
Write(in_data[i]);
}
return *this;
}
} // namespace Network

File diff suppressed because it is too large Load Diff

View File

@@ -1,151 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <memory>
#include <string>
#include <vector>
#include "common/announce_multiplayer_room.h"
#include "common/common_types.h"
#include "network/verify_user.h"
namespace Network {
using AnnounceMultiplayerRoom::GameInfo;
using AnnounceMultiplayerRoom::MacAddress;
using AnnounceMultiplayerRoom::Member;
using AnnounceMultiplayerRoom::RoomInformation;
constexpr u32 network_version = 1; ///< The version of this Room and RoomMember
constexpr u16 DefaultRoomPort = 24872;
constexpr u32 MaxMessageSize = 500;
/// Maximum number of concurrent connections allowed to this room.
static constexpr u32 MaxConcurrentConnections = 254;
constexpr std::size_t NumChannels = 1; // Number of channels used for the connection
/// A special MAC address that tells the room we're joining to assign us a MAC address
/// automatically.
constexpr MacAddress NoPreferredMac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
// 802.11 broadcast MAC address
constexpr MacAddress BroadcastMac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
// The different types of messages that can be sent. The first byte of each packet defines the type
enum RoomMessageTypes : u8 {
IdJoinRequest = 1,
IdJoinSuccess,
IdRoomInformation,
IdSetGameInfo,
IdWifiPacket,
IdChatMessage,
IdNameCollision,
IdMacCollision,
IdVersionMismatch,
IdWrongPassword,
IdCloseRoom,
IdRoomIsFull,
IdConsoleIdCollision,
IdStatusMessage,
IdHostKicked,
IdHostBanned,
/// Moderation requests
IdModKick,
IdModBan,
IdModUnban,
IdModGetBanList,
// Moderation responses
IdModBanListResponse,
IdModPermissionDenied,
IdModNoSuchUser,
IdJoinSuccessAsMod,
};
/// Types of system status messages
enum StatusMessageTypes : u8 {
IdMemberJoin = 1, ///< Member joining
IdMemberLeave, ///< Member leaving
IdMemberKicked, ///< A member is kicked from the room
IdMemberBanned, ///< A member is banned from the room
IdAddressUnbanned, ///< A username / ip address is unbanned from the room
};
/// This is what a server [person creating a server] would use.
class Room final {
public:
enum class State : u8 {
Open, ///< The room is open and ready to accept connections.
Closed, ///< The room is not opened and can not accept connections.
};
Room();
~Room();
/**
* Gets the current state of the room.
*/
State GetState() const;
/**
* Gets the room information of the room.
*/
const RoomInformation& GetRoomInformation() const;
/**
* Gets the verify UID of this room.
*/
std::string GetVerifyUID() const;
/**
* Gets a list of the mbmers connected to the room.
*/
std::vector<Member> GetRoomMemberList() const;
/**
* Checks if the room is password protected
*/
bool HasPassword() const;
using UsernameBanList = std::vector<std::string>;
using IPBanList = std::vector<std::string>;
using BanList = std::pair<UsernameBanList, IPBanList>;
/**
* Creates the socket for this room. Will bind to default address if
* server is empty string.
*/
bool Create(const std::string& name, const std::string& description = "",
const std::string& server = "", u16 server_port = DefaultRoomPort,
const std::string& password = "",
const u32 max_connections = MaxConcurrentConnections,
const std::string& host_username = "", const GameInfo = {},
std::unique_ptr<VerifyUser::Backend> verify_backend = nullptr,
const BanList& ban_list = {}, bool enable_yuzu_mods = false);
/**
* Sets the verification GUID of the room.
*/
void SetVerifyUID(const std::string& uid);
/**
* Gets the ban list (including banned forum usernames and IPs) of the room.
*/
BanList GetBanList() const;
/**
* Destroys the socket
*/
void Destroy();
private:
class RoomImpl;
std::unique_ptr<RoomImpl> room_impl;
};
} // namespace Network

View File

@@ -1,696 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <atomic>
#include <list>
#include <mutex>
#include <set>
#include <thread>
#include "common/assert.h"
#include "enet/enet.h"
#include "network/packet.h"
#include "network/room_member.h"
namespace Network {
constexpr u32 ConnectionTimeoutMs = 5000;
class RoomMember::RoomMemberImpl {
public:
ENetHost* client = nullptr; ///< ENet network interface.
ENetPeer* server = nullptr; ///< The server peer the client is connected to
/// Information about the clients connected to the same room as us.
MemberList member_information;
/// Information about the room we're connected to.
RoomInformation room_information;
/// The current game name, id and version
GameInfo current_game_info;
std::atomic<State> state{State::Idle}; ///< Current state of the RoomMember.
void SetState(const State new_state);
void SetError(const Error new_error);
bool IsConnected() const;
std::string nickname; ///< The nickname of this member.
std::string username; ///< The username of this member.
mutable std::mutex username_mutex; ///< Mutex for locking username.
MacAddress mac_address; ///< The mac_address of this member.
std::mutex network_mutex; ///< Mutex that controls access to the `client` variable.
/// Thread that receives and dispatches network packets
std::unique_ptr<std::thread> loop_thread;
std::mutex send_list_mutex; ///< Mutex that controls access to the `send_list` variable.
std::list<Packet> send_list; ///< A list that stores all packets to send the async
template <typename T>
using CallbackSet = std::set<CallbackHandle<T>>;
std::mutex callback_mutex; ///< The mutex used for handling callbacks
class Callbacks {
public:
template <typename T>
CallbackSet<T>& Get();
private:
CallbackSet<WifiPacket> callback_set_wifi_packet;
CallbackSet<ChatEntry> callback_set_chat_messages;
CallbackSet<StatusMessageEntry> callback_set_status_messages;
CallbackSet<RoomInformation> callback_set_room_information;
CallbackSet<State> callback_set_state;
CallbackSet<Error> callback_set_error;
CallbackSet<Room::BanList> callback_set_ban_list;
};
Callbacks callbacks; ///< All CallbackSets to all events
void MemberLoop();
void StartLoop();
/**
* Sends data to the room. It will be send on channel 0 with flag RELIABLE
* @param packet The data to send
*/
void Send(Packet&& packet);
/**
* Sends a request to the server, asking for permission to join a room with the specified
* nickname and preferred mac.
* @params nickname The desired nickname.
* @params console_id_hash A hash of the Console ID.
* @params preferred_mac The preferred MAC address to use in the room, the NoPreferredMac tells
* @params password The password for the room
* the server to assign one for us.
*/
void SendJoinRequest(const std::string& nickname_, const std::string& console_id_hash,
const MacAddress& preferred_mac = NoPreferredMac,
const std::string& password = "", const std::string& token = "");
/**
* Extracts a MAC Address from a received ENet packet.
* @param event The ENet event that was received.
*/
void HandleJoinPacket(const ENetEvent* event);
/**
* Extracts RoomInformation and MemberInformation from a received ENet packet.
* @param event The ENet event that was received.
*/
void HandleRoomInformationPacket(const ENetEvent* event);
/**
* Extracts a WifiPacket from a received ENet packet.
* @param event The ENet event that was received.
*/
void HandleWifiPackets(const ENetEvent* event);
/**
* Extracts a chat entry from a received ENet packet and adds it to the chat queue.
* @param event The ENet event that was received.
*/
void HandleChatPacket(const ENetEvent* event);
/**
* Extracts a system message entry from a received ENet packet and adds it to the system message
* queue.
* @param event The ENet event that was received.
*/
void HandleStatusMessagePacket(const ENetEvent* event);
/**
* Extracts a ban list request response from a received ENet packet.
* @param event The ENet event that was received.
*/
void HandleModBanListResponsePacket(const ENetEvent* event);
/**
* Disconnects the RoomMember from the Room
*/
void Disconnect();
template <typename T>
void Invoke(const T& data);
template <typename T>
CallbackHandle<T> Bind(std::function<void(const T&)> callback);
};
// RoomMemberImpl
void RoomMember::RoomMemberImpl::SetState(const State new_state) {
if (state != new_state) {
state = new_state;
Invoke<State>(state);
}
}
void RoomMember::RoomMemberImpl::SetError(const Error new_error) {
Invoke<Error>(new_error);
}
bool RoomMember::RoomMemberImpl::IsConnected() const {
return state == State::Joining || state == State::Joined || state == State::Moderator;
}
void RoomMember::RoomMemberImpl::MemberLoop() {
// Receive packets while the connection is open
while (IsConnected()) {
std::lock_guard lock(network_mutex);
ENetEvent event;
if (enet_host_service(client, &event, 16) > 0) {
switch (event.type) {
case ENET_EVENT_TYPE_RECEIVE:
switch (event.packet->data[0]) {
case IdWifiPacket:
HandleWifiPackets(&event);
break;
case IdChatMessage:
HandleChatPacket(&event);
break;
case IdStatusMessage:
HandleStatusMessagePacket(&event);
break;
case IdRoomInformation:
HandleRoomInformationPacket(&event);
break;
case IdJoinSuccess:
case IdJoinSuccessAsMod:
// The join request was successful, we are now in the room.
// If we joined successfully, there must be at least one client in the room: us.
ASSERT_MSG(member_information.size() > 0,
"We have not yet received member information.");
HandleJoinPacket(&event); // Get the MAC Address for the client
if (event.packet->data[0] == IdJoinSuccessAsMod) {
SetState(State::Moderator);
} else {
SetState(State::Joined);
}
break;
case IdModBanListResponse:
HandleModBanListResponsePacket(&event);
break;
case IdRoomIsFull:
SetState(State::Idle);
SetError(Error::RoomIsFull);
break;
case IdNameCollision:
SetState(State::Idle);
SetError(Error::NameCollision);
break;
case IdMacCollision:
SetState(State::Idle);
SetError(Error::MacCollision);
break;
case IdConsoleIdCollision:
SetState(State::Idle);
SetError(Error::ConsoleIdCollision);
break;
case IdVersionMismatch:
SetState(State::Idle);
SetError(Error::WrongVersion);
break;
case IdWrongPassword:
SetState(State::Idle);
SetError(Error::WrongPassword);
break;
case IdCloseRoom:
SetState(State::Idle);
SetError(Error::LostConnection);
break;
case IdHostKicked:
SetState(State::Idle);
SetError(Error::HostKicked);
break;
case IdHostBanned:
SetState(State::Idle);
SetError(Error::HostBanned);
break;
case IdModPermissionDenied:
SetError(Error::PermissionDenied);
break;
case IdModNoSuchUser:
SetError(Error::NoSuchUser);
break;
}
enet_packet_destroy(event.packet);
break;
case ENET_EVENT_TYPE_DISCONNECT:
if (state == State::Joined || state == State::Moderator) {
SetState(State::Idle);
SetError(Error::LostConnection);
}
break;
case ENET_EVENT_TYPE_NONE:
break;
case ENET_EVENT_TYPE_CONNECT:
// The ENET_EVENT_TYPE_CONNECT event can not possibly happen here because we're
// already connected
ASSERT_MSG(false, "Received unexpected connect event while already connected");
break;
}
}
std::list<Packet> packets;
{
std::lock_guard send_lock(send_list_mutex);
packets.swap(send_list);
}
for (const auto& packet : packets) {
ENetPacket* enetPacket = enet_packet_create(packet.GetData(), packet.GetDataSize(),
ENET_PACKET_FLAG_RELIABLE);
enet_peer_send(server, 0, enetPacket);
}
enet_host_flush(client);
}
Disconnect();
};
void RoomMember::RoomMemberImpl::StartLoop() {
loop_thread = std::make_unique<std::thread>(&RoomMember::RoomMemberImpl::MemberLoop, this);
}
void RoomMember::RoomMemberImpl::Send(Packet&& packet) {
std::lock_guard lock(send_list_mutex);
send_list.push_back(std::move(packet));
}
void RoomMember::RoomMemberImpl::SendJoinRequest(const std::string& nickname_,
const std::string& console_id_hash,
const MacAddress& preferred_mac,
const std::string& password,
const std::string& token) {
Packet packet;
packet.Write(static_cast<u8>(IdJoinRequest));
packet.Write(nickname_);
packet.Write(console_id_hash);
packet.Write(preferred_mac);
packet.Write(network_version);
packet.Write(password);
packet.Write(token);
Send(std::move(packet));
}
void RoomMember::RoomMemberImpl::HandleRoomInformationPacket(const ENetEvent* event) {
Packet packet;
packet.Append(event->packet->data, event->packet->dataLength);
// Ignore the first byte, which is the message id.
packet.IgnoreBytes(sizeof(u8)); // Ignore the message type
RoomInformation info{};
packet.Read(info.name);
packet.Read(info.description);
packet.Read(info.member_slots);
packet.Read(info.port);
packet.Read(info.preferred_game.name);
packet.Read(info.host_username);
room_information.name = info.name;
room_information.description = info.description;
room_information.member_slots = info.member_slots;
room_information.port = info.port;
room_information.preferred_game = info.preferred_game;
room_information.host_username = info.host_username;
u32 num_members;
packet.Read(num_members);
member_information.resize(num_members);
for (auto& member : member_information) {
packet.Read(member.nickname);
packet.Read(member.mac_address);
packet.Read(member.game_info.name);
packet.Read(member.game_info.id);
packet.Read(member.username);
packet.Read(member.display_name);
packet.Read(member.avatar_url);
{
std::lock_guard lock(username_mutex);
if (member.nickname == nickname) {
username = member.username;
}
}
}
Invoke(room_information);
}
void RoomMember::RoomMemberImpl::HandleJoinPacket(const ENetEvent* event) {
Packet packet;
packet.Append(event->packet->data, event->packet->dataLength);
// Ignore the first byte, which is the message id.
packet.IgnoreBytes(sizeof(u8)); // Ignore the message type
// Parse the MAC Address from the packet
packet.Read(mac_address);
}
void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) {
WifiPacket wifi_packet{};
Packet packet;
packet.Append(event->packet->data, event->packet->dataLength);
// Ignore the first byte, which is the message id.
packet.IgnoreBytes(sizeof(u8)); // Ignore the message type
// Parse the WifiPacket from the packet
u8 frame_type;
packet.Read(frame_type);
WifiPacket::PacketType type = static_cast<WifiPacket::PacketType>(frame_type);
wifi_packet.type = type;
packet.Read(wifi_packet.channel);
packet.Read(wifi_packet.transmitter_address);
packet.Read(wifi_packet.destination_address);
packet.Read(wifi_packet.data);
Invoke<WifiPacket>(wifi_packet);
}
void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) {
Packet packet;
packet.Append(event->packet->data, event->packet->dataLength);
// Ignore the first byte, which is the message id.
packet.IgnoreBytes(sizeof(u8));
ChatEntry chat_entry{};
packet.Read(chat_entry.nickname);
packet.Read(chat_entry.username);
packet.Read(chat_entry.message);
Invoke<ChatEntry>(chat_entry);
}
void RoomMember::RoomMemberImpl::HandleStatusMessagePacket(const ENetEvent* event) {
Packet packet;
packet.Append(event->packet->data, event->packet->dataLength);
// Ignore the first byte, which is the message id.
packet.IgnoreBytes(sizeof(u8));
StatusMessageEntry status_message_entry{};
u8 type{};
packet.Read(type);
status_message_entry.type = static_cast<StatusMessageTypes>(type);
packet.Read(status_message_entry.nickname);
packet.Read(status_message_entry.username);
Invoke<StatusMessageEntry>(status_message_entry);
}
void RoomMember::RoomMemberImpl::HandleModBanListResponsePacket(const ENetEvent* event) {
Packet packet;
packet.Append(event->packet->data, event->packet->dataLength);
// Ignore the first byte, which is the message id.
packet.IgnoreBytes(sizeof(u8));
Room::BanList ban_list = {};
packet.Read(ban_list.first);
packet.Read(ban_list.second);
Invoke<Room::BanList>(ban_list);
}
void RoomMember::RoomMemberImpl::Disconnect() {
member_information.clear();
room_information.member_slots = 0;
room_information.name.clear();
if (!server) {
return;
}
enet_peer_disconnect(server, 0);
ENetEvent event;
while (enet_host_service(client, &event, ConnectionTimeoutMs) > 0) {
switch (event.type) {
case ENET_EVENT_TYPE_RECEIVE:
enet_packet_destroy(event.packet); // Ignore all incoming data
break;
case ENET_EVENT_TYPE_DISCONNECT:
server = nullptr;
return;
case ENET_EVENT_TYPE_NONE:
case ENET_EVENT_TYPE_CONNECT:
break;
}
}
// didn't disconnect gracefully force disconnect
enet_peer_reset(server);
server = nullptr;
}
template <>
RoomMember::RoomMemberImpl::CallbackSet<WifiPacket>& RoomMember::RoomMemberImpl::Callbacks::Get() {
return callback_set_wifi_packet;
}
template <>
RoomMember::RoomMemberImpl::CallbackSet<RoomMember::State>&
RoomMember::RoomMemberImpl::Callbacks::Get() {
return callback_set_state;
}
template <>
RoomMember::RoomMemberImpl::CallbackSet<RoomMember::Error>&
RoomMember::RoomMemberImpl::Callbacks::Get() {
return callback_set_error;
}
template <>
RoomMember::RoomMemberImpl::CallbackSet<RoomInformation>&
RoomMember::RoomMemberImpl::Callbacks::Get() {
return callback_set_room_information;
}
template <>
RoomMember::RoomMemberImpl::CallbackSet<ChatEntry>& RoomMember::RoomMemberImpl::Callbacks::Get() {
return callback_set_chat_messages;
}
template <>
RoomMember::RoomMemberImpl::CallbackSet<StatusMessageEntry>&
RoomMember::RoomMemberImpl::Callbacks::Get() {
return callback_set_status_messages;
}
template <>
RoomMember::RoomMemberImpl::CallbackSet<Room::BanList>&
RoomMember::RoomMemberImpl::Callbacks::Get() {
return callback_set_ban_list;
}
template <typename T>
void RoomMember::RoomMemberImpl::Invoke(const T& data) {
std::lock_guard lock(callback_mutex);
CallbackSet<T> callback_set = callbacks.Get<T>();
for (auto const& callback : callback_set) {
(*callback)(data);
}
}
template <typename T>
RoomMember::CallbackHandle<T> RoomMember::RoomMemberImpl::Bind(
std::function<void(const T&)> callback) {
std::lock_guard lock(callback_mutex);
CallbackHandle<T> handle;
handle = std::make_shared<std::function<void(const T&)>>(callback);
callbacks.Get<T>().insert(handle);
return handle;
}
// RoomMember
RoomMember::RoomMember() : room_member_impl{std::make_unique<RoomMemberImpl>()} {}
RoomMember::~RoomMember() {
ASSERT_MSG(!IsConnected(), "RoomMember is being destroyed while connected");
if (room_member_impl->loop_thread) {
Leave();
}
}
RoomMember::State RoomMember::GetState() const {
return room_member_impl->state;
}
const RoomMember::MemberList& RoomMember::GetMemberInformation() const {
return room_member_impl->member_information;
}
const std::string& RoomMember::GetNickname() const {
return room_member_impl->nickname;
}
const std::string& RoomMember::GetUsername() const {
std::lock_guard lock(room_member_impl->username_mutex);
return room_member_impl->username;
}
const MacAddress& RoomMember::GetMacAddress() const {
ASSERT_MSG(IsConnected(), "Tried to get MAC address while not connected");
return room_member_impl->mac_address;
}
RoomInformation RoomMember::GetRoomInformation() const {
return room_member_impl->room_information;
}
void RoomMember::Join(const std::string& nick, const std::string& console_id_hash,
const char* server_addr, u16 server_port, u16 client_port,
const MacAddress& preferred_mac, const std::string& password,
const std::string& token) {
// If the member is connected, kill the connection first
if (room_member_impl->loop_thread && room_member_impl->loop_thread->joinable()) {
Leave();
}
// If the thread isn't running but the ptr still exists, reset it
else if (room_member_impl->loop_thread) {
room_member_impl->loop_thread.reset();
}
if (!room_member_impl->client) {
room_member_impl->client = enet_host_create(nullptr, 1, NumChannels, 0, 0);
ASSERT_MSG(room_member_impl->client != nullptr, "Could not create client");
}
room_member_impl->SetState(State::Joining);
ENetAddress address{};
enet_address_set_host(&address, server_addr);
address.port = server_port;
room_member_impl->server =
enet_host_connect(room_member_impl->client, &address, NumChannels, 0);
if (!room_member_impl->server) {
room_member_impl->SetState(State::Idle);
room_member_impl->SetError(Error::UnknownError);
return;
}
ENetEvent event{};
int net = enet_host_service(room_member_impl->client, &event, ConnectionTimeoutMs);
if (net > 0 && event.type == ENET_EVENT_TYPE_CONNECT) {
room_member_impl->nickname = nick;
room_member_impl->StartLoop();
room_member_impl->SendJoinRequest(nick, console_id_hash, preferred_mac, password, token);
SendGameInfo(room_member_impl->current_game_info);
} else {
enet_peer_disconnect(room_member_impl->server, 0);
room_member_impl->SetState(State::Idle);
room_member_impl->SetError(Error::CouldNotConnect);
}
}
bool RoomMember::IsConnected() const {
return room_member_impl->IsConnected();
}
void RoomMember::SendWifiPacket(const WifiPacket& wifi_packet) {
Packet packet;
packet.Write(static_cast<u8>(IdWifiPacket));
packet.Write(static_cast<u8>(wifi_packet.type));
packet.Write(wifi_packet.channel);
packet.Write(wifi_packet.transmitter_address);
packet.Write(wifi_packet.destination_address);
packet.Write(wifi_packet.data);
room_member_impl->Send(std::move(packet));
}
void RoomMember::SendChatMessage(const std::string& message) {
Packet packet;
packet.Write(static_cast<u8>(IdChatMessage));
packet.Write(message);
room_member_impl->Send(std::move(packet));
}
void RoomMember::SendGameInfo(const GameInfo& game_info) {
room_member_impl->current_game_info = game_info;
if (!IsConnected())
return;
Packet packet;
packet.Write(static_cast<u8>(IdSetGameInfo));
packet.Write(game_info.name);
packet.Write(game_info.id);
room_member_impl->Send(std::move(packet));
}
void RoomMember::SendModerationRequest(RoomMessageTypes type, const std::string& nickname) {
ASSERT_MSG(type == IdModKick || type == IdModBan || type == IdModUnban,
"type is not a moderation request");
if (!IsConnected())
return;
Packet packet;
packet.Write(static_cast<u8>(type));
packet.Write(nickname);
room_member_impl->Send(std::move(packet));
}
void RoomMember::RequestBanList() {
if (!IsConnected())
return;
Packet packet;
packet.Write(static_cast<u8>(IdModGetBanList));
room_member_impl->Send(std::move(packet));
}
RoomMember::CallbackHandle<RoomMember::State> RoomMember::BindOnStateChanged(
std::function<void(const RoomMember::State&)> callback) {
return room_member_impl->Bind(callback);
}
RoomMember::CallbackHandle<RoomMember::Error> RoomMember::BindOnError(
std::function<void(const RoomMember::Error&)> callback) {
return room_member_impl->Bind(callback);
}
RoomMember::CallbackHandle<WifiPacket> RoomMember::BindOnWifiPacketReceived(
std::function<void(const WifiPacket&)> callback) {
return room_member_impl->Bind(callback);
}
RoomMember::CallbackHandle<RoomInformation> RoomMember::BindOnRoomInformationChanged(
std::function<void(const RoomInformation&)> callback) {
return room_member_impl->Bind(callback);
}
RoomMember::CallbackHandle<ChatEntry> RoomMember::BindOnChatMessageRecieved(
std::function<void(const ChatEntry&)> callback) {
return room_member_impl->Bind(callback);
}
RoomMember::CallbackHandle<StatusMessageEntry> RoomMember::BindOnStatusMessageReceived(
std::function<void(const StatusMessageEntry&)> callback) {
return room_member_impl->Bind(callback);
}
RoomMember::CallbackHandle<Room::BanList> RoomMember::BindOnBanListReceived(
std::function<void(const Room::BanList&)> callback) {
return room_member_impl->Bind(callback);
}
template <typename T>
void RoomMember::Unbind(CallbackHandle<T> handle) {
std::lock_guard lock(room_member_impl->callback_mutex);
room_member_impl->callbacks.Get<T>().erase(handle);
}
void RoomMember::Leave() {
room_member_impl->SetState(State::Idle);
room_member_impl->loop_thread->join();
room_member_impl->loop_thread.reset();
enet_host_destroy(room_member_impl->client);
room_member_impl->client = nullptr;
}
template void RoomMember::Unbind(CallbackHandle<WifiPacket>);
template void RoomMember::Unbind(CallbackHandle<RoomMember::State>);
template void RoomMember::Unbind(CallbackHandle<RoomMember::Error>);
template void RoomMember::Unbind(CallbackHandle<RoomInformation>);
template void RoomMember::Unbind(CallbackHandle<ChatEntry>);
template void RoomMember::Unbind(CallbackHandle<StatusMessageEntry>);
template void RoomMember::Unbind(CallbackHandle<Room::BanList>);
} // namespace Network

View File

@@ -1,318 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include "common/announce_multiplayer_room.h"
#include "common/common_types.h"
#include "network/room.h"
namespace Network {
using AnnounceMultiplayerRoom::GameInfo;
using AnnounceMultiplayerRoom::RoomInformation;
/// Information about the received WiFi packets.
/// Acts as our own 802.11 header.
struct WifiPacket {
enum class PacketType : u8 {
Beacon,
Data,
Authentication,
AssociationResponse,
Deauthentication,
NodeMap
};
PacketType type; ///< The type of 802.11 frame.
std::vector<u8> data; ///< Raw 802.11 frame data, starting at the management frame header
/// for management frames.
MacAddress transmitter_address; ///< Mac address of the transmitter.
MacAddress destination_address; ///< Mac address of the receiver.
u8 channel; ///< WiFi channel where this frame was transmitted.
};
/// Represents a chat message.
struct ChatEntry {
std::string nickname; ///< Nickname of the client who sent this message.
/// Web services username of the client who sent this message, can be empty.
std::string username;
std::string message; ///< Body of the message.
};
/// Represents a system status message.
struct StatusMessageEntry {
StatusMessageTypes type; ///< Type of the message
/// Subject of the message. i.e. the user who is joining/leaving/being banned, etc.
std::string nickname;
std::string username;
};
/**
* This is what a client [person joining a server] would use.
* It also has to be used if you host a game yourself (You'd create both, a Room and a
* RoomMembership for yourself)
*/
class RoomMember final {
public:
enum class State : u8 {
Uninitialized, ///< Not initialized
Idle, ///< Default state (i.e. not connected)
Joining, ///< The client is attempting to join a room.
Joined, ///< The client is connected to the room and is ready to send/receive packets.
Moderator, ///< The client is connnected to the room and is granted mod permissions.
};
enum class Error : u8 {
// Reasons why connection was closed
LostConnection, ///< Connection closed
HostKicked, ///< Kicked by the host
// Reasons why connection was rejected
UnknownError, ///< Some error [permissions to network device missing or something]
NameCollision, ///< Somebody is already using this name
MacCollision, ///< Somebody is already using that mac-address
ConsoleIdCollision, ///< Somebody in the room has the same Console ID
WrongVersion, ///< The room version is not the same as for this RoomMember
WrongPassword, ///< The password doesn't match the one from the Room
CouldNotConnect, ///< The room is not responding to a connection attempt
RoomIsFull, ///< Room is already at the maximum number of players
HostBanned, ///< The user is banned by the host
// Reasons why moderation request failed
PermissionDenied, ///< The user does not have mod permissions
NoSuchUser, ///< The nickname the user attempts to kick/ban does not exist
};
struct MemberInformation {
std::string nickname; ///< Nickname of the member.
std::string username; ///< The web services username of the member. Can be empty.
std::string display_name; ///< The web services display name of the member. Can be empty.
std::string avatar_url; ///< Url to the member's avatar. Can be empty.
GameInfo game_info; ///< Name of the game they're currently playing, or empty if they're
/// not playing anything.
MacAddress mac_address; ///< MAC address associated with this member.
};
using MemberList = std::vector<MemberInformation>;
// The handle for the callback functions
template <typename T>
using CallbackHandle = std::shared_ptr<std::function<void(const T&)>>;
/**
* Unbinds a callback function from the events.
* @param handle The connection handle to disconnect
*/
template <typename T>
void Unbind(CallbackHandle<T> handle);
RoomMember();
~RoomMember();
/**
* Returns the status of our connection to the room.
*/
State GetState() const;
/**
* Returns information about the members in the room we're currently connected to.
*/
const MemberList& GetMemberInformation() const;
/**
* Returns the nickname of the RoomMember.
*/
const std::string& GetNickname() const;
/**
* Returns the username of the RoomMember.
*/
const std::string& GetUsername() const;
/**
* Returns the MAC address of the RoomMember.
*/
const MacAddress& GetMacAddress() const;
/**
* Returns information about the room we're currently connected to.
*/
RoomInformation GetRoomInformation() const;
/**
* Returns whether we're connected to a server or not.
*/
bool IsConnected() const;
/**
* Attempts to join a room at the specified address and port, using the specified nickname.
* A console ID hash is passed in to check console ID conflicts.
* This may fail if the username or console ID is already taken.
*/
void Join(const std::string& nickname, const std::string& console_id_hash,
const char* server_addr = "127.0.0.1", u16 server_port = DefaultRoomPort,
u16 client_port = 0, const MacAddress& preferred_mac = NoPreferredMac,
const std::string& password = "", const std::string& token = "");
/**
* Sends a WiFi packet to the room.
* @param packet The WiFi packet to send.
*/
void SendWifiPacket(const WifiPacket& packet);
/**
* Sends a chat message to the room.
* @param message The contents of the message.
*/
void SendChatMessage(const std::string& message);
/**
* Sends the current game info to the room.
* @param game_info The game information.
*/
void SendGameInfo(const GameInfo& game_info);
/**
* Sends a moderation request to the room.
* @param type Moderation request type.
* @param nickname The subject of the request. (i.e. the user you want to kick/ban)
*/
void SendModerationRequest(RoomMessageTypes type, const std::string& nickname);
/**
* Attempts to retrieve ban list from the room.
* If success, the ban list callback would be called. Otherwise an error would be emitted.
*/
void RequestBanList();
/**
* Binds a function to an event that will be triggered every time the State of the member
* changed. The function wil be called every time the event is triggered. The callback function
* must not bind or unbind a function. Doing so will cause a deadlock
* @param callback The function to call
* @return A handle used for removing the function from the registered list
*/
CallbackHandle<State> BindOnStateChanged(std::function<void(const State&)> callback);
/**
* Binds a function to an event that will be triggered every time an error happened. The
* function wil be called every time the event is triggered. The callback function must not bind
* or unbind a function. Doing so will cause a deadlock
* @param callback The function to call
* @return A handle used for removing the function from the registered list
*/
CallbackHandle<Error> BindOnError(std::function<void(const Error&)> callback);
/**
* Binds a function to an event that will be triggered every time a WifiPacket is received.
* The function wil be called everytime the event is triggered.
* The callback function must not bind or unbind a function. Doing so will cause a deadlock
* @param callback The function to call
* @return A handle used for removing the function from the registered list
*/
CallbackHandle<WifiPacket> BindOnWifiPacketReceived(
std::function<void(const WifiPacket&)> callback);
/**
* Binds a function to an event that will be triggered every time the RoomInformation changes.
* The function wil be called every time the event is triggered.
* The callback function must not bind or unbind a function. Doing so will cause a deadlock
* @param callback The function to call
* @return A handle used for removing the function from the registered list
*/
CallbackHandle<RoomInformation> BindOnRoomInformationChanged(
std::function<void(const RoomInformation&)> callback);
/**
* Binds a function to an event that will be triggered every time a ChatMessage is received.
* The function wil be called every time the event is triggered.
* The callback function must not bind or unbind a function. Doing so will cause a deadlock
* @param callback The function to call
* @return A handle used for removing the function from the registered list
*/
CallbackHandle<ChatEntry> BindOnChatMessageRecieved(
std::function<void(const ChatEntry&)> callback);
/**
* Binds a function to an event that will be triggered every time a StatusMessage is
* received. The function will be called every time the event is triggered. The callback
* function must not bind or unbind a function. Doing so will cause a deadlock
* @param callback The function to call
* @return A handle used for removing the function from the registered list
*/
CallbackHandle<StatusMessageEntry> BindOnStatusMessageReceived(
std::function<void(const StatusMessageEntry&)> callback);
/**
* Binds a function to an event that will be triggered every time a requested ban list
* received. The function will be called every time the event is triggered. The callback
* function must not bind or unbind a function. Doing so will cause a deadlock
* @param callback The function to call
* @return A handle used for removing the function from the registered list
*/
CallbackHandle<Room::BanList> BindOnBanListReceived(
std::function<void(const Room::BanList&)> callback);
/**
* Leaves the current room.
*/
void Leave();
private:
class RoomMemberImpl;
std::unique_ptr<RoomMemberImpl> room_member_impl;
};
inline const char* GetStateStr(const RoomMember::State& s) {
switch (s) {
case RoomMember::State::Uninitialized:
return "Uninitialized";
case RoomMember::State::Idle:
return "Idle";
case RoomMember::State::Joining:
return "Joining";
case RoomMember::State::Joined:
return "Joined";
case RoomMember::State::Moderator:
return "Moderator";
}
return "Unknown";
}
inline const char* GetErrorStr(const RoomMember::Error& e) {
switch (e) {
case RoomMember::Error::LostConnection:
return "LostConnection";
case RoomMember::Error::HostKicked:
return "HostKicked";
case RoomMember::Error::UnknownError:
return "UnknownError";
case RoomMember::Error::NameCollision:
return "NameCollision";
case RoomMember::Error::MacCollision:
return "MaxCollision";
case RoomMember::Error::ConsoleIdCollision:
return "ConsoleIdCollision";
case RoomMember::Error::WrongVersion:
return "WrongVersion";
case RoomMember::Error::WrongPassword:
return "WrongPassword";
case RoomMember::Error::CouldNotConnect:
return "CouldNotConnect";
case RoomMember::Error::RoomIsFull:
return "RoomIsFull";
case RoomMember::Error::HostBanned:
return "HostBanned";
case RoomMember::Error::PermissionDenied:
return "PermissionDenied";
case RoomMember::Error::NoSuchUser:
return "NoSuchUser";
default:
return "Unknown";
}
}
} // namespace Network

View File

@@ -1,17 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2018 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "network/verify_user.h"
namespace Network::VerifyUser {
Backend::~Backend() = default;
NullBackend::~NullBackend() = default;
UserData NullBackend::LoadUserData([[maybe_unused]] const std::string& verify_uid,
[[maybe_unused]] const std::string& token) {
return {};
}
} // namespace Network::VerifyUser

Some files were not shown because too many files have changed in this diff Show More