summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/citra/config.cpp3
-rw-r--r--src/citra/default_ini.h14
-rw-r--r--src/citra/emu_window/emu_window_sdl2.cpp10
-rw-r--r--src/citra/emu_window/emu_window_sdl2.h4
-rw-r--r--src/citra_qt/bootmanager.cpp10
-rw-r--r--src/citra_qt/bootmanager.h4
-rw-r--r--src/citra_qt/configuration/config.cpp6
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/core.cpp5
-rw-r--r--src/core/frontend/emu_window.cpp23
-rw-r--r--src/core/frontend/emu_window.h83
-rw-r--r--src/core/frontend/input.h19
-rw-r--r--src/core/frontend/motion_emu.cpp89
-rw-r--r--src/core/frontend/motion_emu.h52
-rw-r--r--src/core/hle/service/apt/apt.cpp229
-rw-r--r--src/core/hle/service/apt/apt.h6
-rw-r--r--src/core/hle/service/dsp_dsp.cpp7
-rw-r--r--src/core/hle/service/hid/hid.cpp32
-rw-r--r--src/core/loader/ncch.cpp8
-rw-r--r--src/core/settings.h1
-rw-r--r--src/input_common/CMakeLists.txt2
-rw-r--r--src/input_common/main.cpp15
-rw-r--r--src/input_common/main.h7
-rw-r--r--src/input_common/motion_emu.cpp165
-rw-r--r--src/input_common/motion_emu.h46
-rw-r--r--src/input_common/sdl/sdl.cpp2
-rw-r--r--src/network/packet.cpp38
-rw-r--r--src/network/packet.h4
-rw-r--r--src/network/room.cpp84
-rw-r--r--src/network/room.h19
-rw-r--r--src/network/room_member.cpp128
-rw-r--r--src/network/room_member.h59
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp9
-rw-r--r--src/video_core/swrasterizer/lighting.cpp22
34 files changed, 845 insertions, 364 deletions
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index 69247b166..73846ed91 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -76,6 +76,9 @@ void Config::ReadValues() {
76 Settings::values.analogs[i] = default_param; 76 Settings::values.analogs[i] = default_param;
77 } 77 }
78 78
79 Settings::values.motion_device = sdl2_config->Get(
80 "Controls", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01");
81
79 // Core 82 // Core
80 Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); 83 Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);
81 84
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h
index a12498e0f..9ea779dd8 100644
--- a/src/citra/default_ini.h
+++ b/src/citra/default_ini.h
@@ -12,7 +12,7 @@ const char* sdl2_config_file = R"(
12# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..." 12# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..."
13# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values 13# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values
14 14
15# for button input, the following devices are avaible: 15# for button input, the following devices are available:
16# - "keyboard" (default) for keyboard input. Required parameters: 16# - "keyboard" (default) for keyboard input. Required parameters:
17# - "code": the code of the key to bind 17# - "code": the code of the key to bind
18# - "sdl" for joystick input using SDL. Required parameters: 18# - "sdl" for joystick input using SDL. Required parameters:
@@ -21,7 +21,7 @@ const char* sdl2_config_file = R"(
21# - "hat"(optional): the index of the hat to bind as direction buttons 21# - "hat"(optional): the index of the hat to bind as direction buttons
22# - "axis"(optional): the index of the axis to bind 22# - "axis"(optional): the index of the axis to bind
23# - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right" 23# - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right"
24# - "threshould"(only used for axis): a float value in (-1.0, 1.0) which the button is 24# - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
25# triggered if the axis value crosses 25# triggered if the axis value crosses
26# - "direction"(only used for axis): "+" means the button is triggered when the axis value 26# - "direction"(only used for axis): "+" means the button is triggered when the axis value
27# is greater than the threshold; "-" means the button is triggered when the axis value 27# is greater than the threshold; "-" means the button is triggered when the axis value
@@ -42,8 +42,8 @@ button_zl=
42button_zr= 42button_zr=
43button_home= 43button_home=
44 44
45# for analog input, the following devices are avaible: 45# for analog input, the following devices are available:
46# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters: 46# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters:
47# - "up", "down", "left", "right": sub-devices for each direction. 47# - "up", "down", "left", "right": sub-devices for each direction.
48# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00" 48# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00"
49# - "modifier": sub-devices as a modifier. 49# - "modifier": sub-devices as a modifier.
@@ -56,6 +56,12 @@ button_home=
56circle_pad= 56circle_pad=
57c_stick= 57c_stick=
58 58
59# for motion input, the following devices are available:
60# - "motion_emu" (default) for emulating motion input from mouse input. Required parameters:
61# - "update_period": update period in milliseconds (default to 100)
62# - "sensitivity": the coefficient converting mouse movement to tilting angle (default to 0.01)
63motion_device=
64
59[Core] 65[Core]
60# Whether to use the Just-In-Time (JIT) compiler for CPU emulation 66# Whether to use the Just-In-Time (JIT) compiler for CPU emulation
61# 0: Interpreter (slow), 1 (default): JIT (fast) 67# 0: Interpreter (slow), 1 (default): JIT (fast)
diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp
index b0f808399..25643715a 100644
--- a/src/citra/emu_window/emu_window_sdl2.cpp
+++ b/src/citra/emu_window/emu_window_sdl2.cpp
@@ -16,11 +16,12 @@
16#include "core/settings.h" 16#include "core/settings.h"
17#include "input_common/keyboard.h" 17#include "input_common/keyboard.h"
18#include "input_common/main.h" 18#include "input_common/main.h"
19#include "input_common/motion_emu.h"
19#include "network/network.h" 20#include "network/network.h"
20 21
21void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { 22void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
22 TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); 23 TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
23 motion_emu->Tilt(x, y); 24 InputCommon::GetMotionEmu()->Tilt(x, y);
24} 25}
25 26
26void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { 27void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
@@ -32,9 +33,9 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
32 } 33 }
33 } else if (button == SDL_BUTTON_RIGHT) { 34 } else if (button == SDL_BUTTON_RIGHT) {
34 if (state == SDL_PRESSED) { 35 if (state == SDL_PRESSED) {
35 motion_emu->BeginTilt(x, y); 36 InputCommon::GetMotionEmu()->BeginTilt(x, y);
36 } else { 37 } else {
37 motion_emu->EndTilt(); 38 InputCommon::GetMotionEmu()->EndTilt();
38 } 39 }
39 } 40 }
40} 41}
@@ -61,8 +62,6 @@ EmuWindow_SDL2::EmuWindow_SDL2() {
61 InputCommon::Init(); 62 InputCommon::Init();
62 Network::Init(); 63 Network::Init();
63 64
64 motion_emu = std::make_unique<Motion::MotionEmu>(*this);
65
66 SDL_SetMainReady(); 65 SDL_SetMainReady();
67 66
68 // Initialize the window 67 // Initialize the window
@@ -117,7 +116,6 @@ EmuWindow_SDL2::EmuWindow_SDL2() {
117EmuWindow_SDL2::~EmuWindow_SDL2() { 116EmuWindow_SDL2::~EmuWindow_SDL2() {
118 SDL_GL_DeleteContext(gl_context); 117 SDL_GL_DeleteContext(gl_context);
119 SDL_Quit(); 118 SDL_Quit();
120 motion_emu = nullptr;
121 119
122 Network::Shutdown(); 120 Network::Shutdown();
123 InputCommon::Shutdown(); 121 InputCommon::Shutdown();
diff --git a/src/citra/emu_window/emu_window_sdl2.h b/src/citra/emu_window/emu_window_sdl2.h
index 1ce2991f7..3664d2fbe 100644
--- a/src/citra/emu_window/emu_window_sdl2.h
+++ b/src/citra/emu_window/emu_window_sdl2.h
@@ -7,7 +7,6 @@
7#include <memory> 7#include <memory>
8#include <utility> 8#include <utility>
9#include "core/frontend/emu_window.h" 9#include "core/frontend/emu_window.h"
10#include "core/frontend/motion_emu.h"
11 10
12struct SDL_Window; 11struct SDL_Window;
13 12
@@ -57,7 +56,4 @@ private:
57 using SDL_GLContext = void*; 56 using SDL_GLContext = void*;
58 /// The OpenGL context associated with the window 57 /// The OpenGL context associated with the window
59 SDL_GLContext gl_context; 58 SDL_GLContext gl_context;
60
61 /// Motion sensors emulation
62 std::unique_ptr<Motion::MotionEmu> motion_emu;
63}; 59};
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp
index 30554890f..7107bfc60 100644
--- a/src/citra_qt/bootmanager.cpp
+++ b/src/citra_qt/bootmanager.cpp
@@ -17,6 +17,7 @@
17#include "core/settings.h" 17#include "core/settings.h"
18#include "input_common/keyboard.h" 18#include "input_common/keyboard.h"
19#include "input_common/main.h" 19#include "input_common/main.h"
20#include "input_common/motion_emu.h"
20#include "network/network.h" 21#include "network/network.h"
21 22
22EmuThread::EmuThread(GRenderWindow* render_window) 23EmuThread::EmuThread(GRenderWindow* render_window)
@@ -201,7 +202,6 @@ qreal GRenderWindow::windowPixelRatio() {
201} 202}
202 203
203void GRenderWindow::closeEvent(QCloseEvent* event) { 204void GRenderWindow::closeEvent(QCloseEvent* event) {
204 motion_emu = nullptr;
205 emit Closed(); 205 emit Closed();
206 QWidget::closeEvent(event); 206 QWidget::closeEvent(event);
207} 207}
@@ -221,7 +221,7 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
221 this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio), 221 this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio),
222 static_cast<unsigned>(pos.y() * pixelRatio)); 222 static_cast<unsigned>(pos.y() * pixelRatio));
223 } else if (event->button() == Qt::RightButton) { 223 } else if (event->button() == Qt::RightButton) {
224 motion_emu->BeginTilt(pos.x(), pos.y()); 224 InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
225 } 225 }
226} 226}
227 227
@@ -230,14 +230,14 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
230 qreal pixelRatio = windowPixelRatio(); 230 qreal pixelRatio = windowPixelRatio();
231 this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u), 231 this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u),
232 std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u)); 232 std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u));
233 motion_emu->Tilt(pos.x(), pos.y()); 233 InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
234} 234}
235 235
236void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { 236void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
237 if (event->button() == Qt::LeftButton) 237 if (event->button() == Qt::LeftButton)
238 this->TouchReleased(); 238 this->TouchReleased();
239 else if (event->button() == Qt::RightButton) 239 else if (event->button() == Qt::RightButton)
240 motion_emu->EndTilt(); 240 InputCommon::GetMotionEmu()->EndTilt();
241} 241}
242 242
243void GRenderWindow::focusOutEvent(QFocusEvent* event) { 243void GRenderWindow::focusOutEvent(QFocusEvent* event) {
@@ -290,13 +290,11 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(
290} 290}
291 291
292void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { 292void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) {
293 motion_emu = std::make_unique<Motion::MotionEmu>(*this);
294 this->emu_thread = emu_thread; 293 this->emu_thread = emu_thread;
295 child->DisablePainting(); 294 child->DisablePainting();
296} 295}
297 296
298void GRenderWindow::OnEmulationStopping() { 297void GRenderWindow::OnEmulationStopping() {
299 motion_emu = nullptr;
300 emu_thread = nullptr; 298 emu_thread = nullptr;
301 child->EnablePainting(); 299 child->EnablePainting();
302} 300}
diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h
index 4b3a3b3cc..6974edcbb 100644
--- a/src/citra_qt/bootmanager.h
+++ b/src/citra_qt/bootmanager.h
@@ -12,7 +12,6 @@
12#include "common/thread.h" 12#include "common/thread.h"
13#include "core/core.h" 13#include "core/core.h"
14#include "core/frontend/emu_window.h" 14#include "core/frontend/emu_window.h"
15#include "core/frontend/motion_emu.h"
16 15
17class QKeyEvent; 16class QKeyEvent;
18class QScreen; 17class QScreen;
@@ -158,9 +157,6 @@ private:
158 157
159 EmuThread* emu_thread; 158 EmuThread* emu_thread;
160 159
161 /// Motion sensors emulation
162 std::unique_ptr<Motion::MotionEmu> motion_emu;
163
164protected: 160protected:
165 void showEvent(QShowEvent* event) override; 161 void showEvent(QShowEvent* event) override;
166}; 162};
diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp
index 75abb4ce6..6e42db007 100644
--- a/src/citra_qt/configuration/config.cpp
+++ b/src/citra_qt/configuration/config.cpp
@@ -57,6 +57,11 @@ void Config::ReadValues() {
57 Settings::values.analogs[i] = default_param; 57 Settings::values.analogs[i] = default_param;
58 } 58 }
59 59
60 Settings::values.motion_device =
61 qt_config->value("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01")
62 .toString()
63 .toStdString();
64
60 qt_config->endGroup(); 65 qt_config->endGroup();
61 66
62 qt_config->beginGroup("Core"); 67 qt_config->beginGroup("Core");
@@ -203,6 +208,7 @@ void Config::SaveValues() {
203 qt_config->setValue(QString::fromStdString(Settings::NativeAnalog::mapping[i]), 208 qt_config->setValue(QString::fromStdString(Settings::NativeAnalog::mapping[i]),
204 QString::fromStdString(Settings::values.analogs[i])); 209 QString::fromStdString(Settings::values.analogs[i]));
205 } 210 }
211 qt_config->setValue("motion_device", QString::fromStdString(Settings::values.motion_device));
206 qt_config->endGroup(); 212 qt_config->endGroup();
207 213
208 qt_config->beginGroup("Core"); 214 qt_config->beginGroup("Core");
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 360f407f3..53bd50eb2 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -33,7 +33,6 @@ set(SRCS
33 frontend/camera/interface.cpp 33 frontend/camera/interface.cpp
34 frontend/emu_window.cpp 34 frontend/emu_window.cpp
35 frontend/framebuffer_layout.cpp 35 frontend/framebuffer_layout.cpp
36 frontend/motion_emu.cpp
37 gdbstub/gdbstub.cpp 36 gdbstub/gdbstub.cpp
38 hle/config_mem.cpp 37 hle/config_mem.cpp
39 hle/applets/applet.cpp 38 hle/applets/applet.cpp
@@ -226,7 +225,6 @@ set(HEADERS
226 frontend/emu_window.h 225 frontend/emu_window.h
227 frontend/framebuffer_layout.h 226 frontend/framebuffer_layout.h
228 frontend/input.h 227 frontend/input.h
229 frontend/motion_emu.h
230 gdbstub/gdbstub.h 228 gdbstub/gdbstub.h
231 hle/config_mem.h 229 hle/config_mem.h
232 hle/function_wrappers.h 230 hle/function_wrappers.h
@@ -388,7 +386,7 @@ set(HEADERS
388 386
389create_directory_groups(${SRCS} ${HEADERS}) 387create_directory_groups(${SRCS} ${HEADERS})
390add_library(core STATIC ${SRCS} ${HEADERS}) 388add_library(core STATIC ${SRCS} ${HEADERS})
391target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) 389target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core)
392target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt) 390target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt)
393if (ENABLE_WEB_SERVICE) 391if (ENABLE_WEB_SERVICE)
394 target_link_libraries(core PUBLIC json-headers web_service) 392 target_link_libraries(core PUBLIC json-headers web_service)
diff --git a/src/core/core.cpp b/src/core/core.cpp
index d08f18623..5332318cf 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -19,6 +19,7 @@
19#include "core/loader/loader.h" 19#include "core/loader/loader.h"
20#include "core/memory_setup.h" 20#include "core/memory_setup.h"
21#include "core/settings.h" 21#include "core/settings.h"
22#include "network/network.h"
22#include "video_core/video_core.h" 23#include "video_core/video_core.h"
23 24
24namespace Core { 25namespace Core {
@@ -188,6 +189,10 @@ void System::Shutdown() {
188 cpu_core = nullptr; 189 cpu_core = nullptr;
189 app_loader = nullptr; 190 app_loader = nullptr;
190 telemetry_session = nullptr; 191 telemetry_session = nullptr;
192 if (auto room_member = Network::GetRoomMember().lock()) {
193 Network::GameInfo game_info{};
194 room_member->SendGameInfo(game_info);
195 }
191 196
192 LOG_DEBUG(Core, "Shutdown OK"); 197 LOG_DEBUG(Core, "Shutdown OK");
193} 198}
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index 4f7d54a33..60b20d4e2 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -62,29 +62,6 @@ void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
62 TouchPressed(framebuffer_x, framebuffer_y); 62 TouchPressed(framebuffer_x, framebuffer_y);
63} 63}
64 64
65void EmuWindow::AccelerometerChanged(float x, float y, float z) {
66 constexpr float coef = 512;
67
68 std::lock_guard<std::mutex> lock(accel_mutex);
69
70 // TODO(wwylele): do a time stretch as it in GyroscopeChanged
71 // The time stretch formula should be like
72 // stretched_vector = (raw_vector - gravity) * stretch_ratio + gravity
73 accel_x = static_cast<s16>(x * coef);
74 accel_y = static_cast<s16>(y * coef);
75 accel_z = static_cast<s16>(z * coef);
76}
77
78void EmuWindow::GyroscopeChanged(float x, float y, float z) {
79 constexpr float FULL_FPS = 60;
80 float coef = GetGyroscopeRawToDpsCoefficient();
81 float stretch = Core::System::GetInstance().perf_stats.GetLastFrameTimeScale();
82 std::lock_guard<std::mutex> lock(gyro_mutex);
83 gyro_x = static_cast<s16>(x * coef * stretch);
84 gyro_y = static_cast<s16>(y * coef * stretch);
85 gyro_z = static_cast<s16>(z * coef * stretch);
86}
87
88void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) { 65void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) {
89 Layout::FramebufferLayout layout; 66 Layout::FramebufferLayout layout;
90 if (Settings::values.custom_layout == true) { 67 if (Settings::values.custom_layout == true) {
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 9414123a4..7bdee251c 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -69,27 +69,6 @@ public:
69 void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y); 69 void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
70 70
71 /** 71 /**
72 * Signal accelerometer state has changed.
73 * @param x X-axis accelerometer value
74 * @param y Y-axis accelerometer value
75 * @param z Z-axis accelerometer value
76 * @note all values are in unit of g (gravitational acceleration).
77 * e.g. x = 1.0 means 9.8m/s^2 in x direction.
78 * @see GetAccelerometerState for axis explanation.
79 */
80 void AccelerometerChanged(float x, float y, float z);
81
82 /**
83 * Signal gyroscope state has changed.
84 * @param x X-axis accelerometer value
85 * @param y Y-axis accelerometer value
86 * @param z Z-axis accelerometer value
87 * @note all values are in deg/sec.
88 * @see GetGyroscopeState for axis explanation.
89 */
90 void GyroscopeChanged(float x, float y, float z);
91
92 /**
93 * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed). 72 * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed).
94 * @note This should be called by the core emu thread to get a state set by the window thread. 73 * @note This should be called by the core emu thread to get a state set by the window thread.
95 * @todo Fix this function to be thread-safe. 74 * @todo Fix this function to be thread-safe.
@@ -101,52 +80,6 @@ public:
101 } 80 }
102 81
103 /** 82 /**
104 * Gets the current accelerometer state (acceleration along each three axis).
105 * Axis explained:
106 * +x is the same direction as LEFT on D-pad.
107 * +y is normal to the touch screen, pointing outward.
108 * +z is the same direction as UP on D-pad.
109 * Units:
110 * 1 unit of return value = 1/512 g (measured by hw test),
111 * where g is the gravitational acceleration (9.8 m/sec2).
112 * @note This should be called by the core emu thread to get a state set by the window thread.
113 * @return std::tuple of (x, y, z)
114 */
115 std::tuple<s16, s16, s16> GetAccelerometerState() {
116 std::lock_guard<std::mutex> lock(accel_mutex);
117 return std::make_tuple(accel_x, accel_y, accel_z);
118 }
119
120 /**
121 * Gets the current gyroscope state (angular rates about each three axis).
122 * Axis explained:
123 * +x is the same direction as LEFT on D-pad.
124 * +y is normal to the touch screen, pointing outward.
125 * +z is the same direction as UP on D-pad.
126 * Orientation is determined by right-hand rule.
127 * Units:
128 * 1 unit of return value = (1/coef) deg/sec,
129 * where coef is the return value of GetGyroscopeRawToDpsCoefficient().
130 * @note This should be called by the core emu thread to get a state set by the window thread.
131 * @return std::tuple of (x, y, z)
132 */
133 std::tuple<s16, s16, s16> GetGyroscopeState() {
134 std::lock_guard<std::mutex> lock(gyro_mutex);
135 return std::make_tuple(gyro_x, gyro_y, gyro_z);
136 }
137
138 /**
139 * Gets the coefficient for units conversion of gyroscope state.
140 * The conversion formula is r = coefficient * v,
141 * where v is angular rate in deg/sec,
142 * and r is the gyroscope state.
143 * @return float-type coefficient
144 */
145 f32 GetGyroscopeRawToDpsCoefficient() const {
146 return 14.375f; // taken from hw test, and gyroscope's document
147 }
148
149 /**
150 * Returns currently active configuration. 83 * Returns currently active configuration.
151 * @note Accesses to the returned object need not be consistent because it may be modified in 84 * @note Accesses to the returned object need not be consistent because it may be modified in
152 * another thread 85 * another thread
@@ -187,12 +120,6 @@ protected:
187 touch_x = 0; 120 touch_x = 0;
188 touch_y = 0; 121 touch_y = 0;
189 touch_pressed = false; 122 touch_pressed = false;
190 accel_x = 0;
191 accel_y = -512;
192 accel_z = 0;
193 gyro_x = 0;
194 gyro_y = 0;
195 gyro_z = 0;
196 } 123 }
197 virtual ~EmuWindow() {} 124 virtual ~EmuWindow() {}
198 125
@@ -255,16 +182,6 @@ private:
255 u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) 182 u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320)
256 u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240) 183 u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240)
257 184
258 std::mutex accel_mutex;
259 s16 accel_x; ///< Accelerometer X-axis value in native 3DS units
260 s16 accel_y; ///< Accelerometer Y-axis value in native 3DS units
261 s16 accel_z; ///< Accelerometer Z-axis value in native 3DS units
262
263 std::mutex gyro_mutex;
264 s16 gyro_x; ///< Gyroscope X-axis value in native 3DS units
265 s16 gyro_y; ///< Gyroscope Y-axis value in native 3DS units
266 s16 gyro_z; ///< Gyroscope Z-axis value in native 3DS units
267
268 /** 185 /**
269 * Clip the provided coordinates to be inside the touchscreen area. 186 * Clip the provided coordinates to be inside the touchscreen area.
270 */ 187 */
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index 0a5713dc0..5916a901d 100644
--- a/src/core/frontend/input.h
+++ b/src/core/frontend/input.h
@@ -11,6 +11,7 @@
11#include <utility> 11#include <utility>
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "common/param_package.h" 13#include "common/param_package.h"
14#include "common/vector_math.h"
14 15
15namespace Input { 16namespace Input {
16 17
@@ -107,4 +108,22 @@ using ButtonDevice = InputDevice<bool>;
107 */ 108 */
108using AnalogDevice = InputDevice<std::tuple<float, float>>; 109using AnalogDevice = InputDevice<std::tuple<float, float>>;
109 110
111/**
112 * A motion device is an input device that returns a tuple of accelerometer state vector and
113 * gyroscope state vector.
114 *
115 * For both vectors:
116 * x+ is the same direction as LEFT on D-pad.
117 * y+ is normal to the touch screen, pointing outward.
118 * z+ is the same direction as UP on D-pad.
119 *
120 * For accelerometer state vector
121 * Units: g (gravitational acceleration)
122 *
123 * For gyroscope state vector:
124 * Orientation is determined by right-hand rule.
125 * Units: deg/sec
126 */
127using MotionDevice = InputDevice<std::tuple<Math::Vec3<float>, Math::Vec3<float>>>;
128
110} // namespace Input 129} // namespace Input
diff --git a/src/core/frontend/motion_emu.cpp b/src/core/frontend/motion_emu.cpp
deleted file mode 100644
index 9a5b3185d..000000000
--- a/src/core/frontend/motion_emu.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/math_util.h"
6#include "common/quaternion.h"
7#include "core/frontend/emu_window.h"
8#include "core/frontend/motion_emu.h"
9
10namespace Motion {
11
12static constexpr int update_millisecond = 100;
13static constexpr auto update_duration =
14 std::chrono::duration_cast<std::chrono::steady_clock::duration>(
15 std::chrono::milliseconds(update_millisecond));
16
17MotionEmu::MotionEmu(EmuWindow& emu_window)
18 : motion_emu_thread(&MotionEmu::MotionEmuThread, this, std::ref(emu_window)) {}
19
20MotionEmu::~MotionEmu() {
21 if (motion_emu_thread.joinable()) {
22 shutdown_event.Set();
23 motion_emu_thread.join();
24 }
25}
26
27void MotionEmu::MotionEmuThread(EmuWindow& emu_window) {
28 auto update_time = std::chrono::steady_clock::now();
29 Math::Quaternion<float> q = MakeQuaternion(Math::Vec3<float>(), 0);
30 Math::Quaternion<float> old_q;
31
32 while (!shutdown_event.WaitUntil(update_time)) {
33 update_time += update_duration;
34 old_q = q;
35
36 {
37 std::lock_guard<std::mutex> guard(tilt_mutex);
38
39 // Find the quaternion describing current 3DS tilting
40 q = MakeQuaternion(Math::MakeVec(-tilt_direction.y, 0.0f, tilt_direction.x),
41 tilt_angle);
42 }
43
44 auto inv_q = q.Inverse();
45
46 // Set the gravity vector in world space
47 auto gravity = Math::MakeVec(0.0f, -1.0f, 0.0f);
48
49 // Find the angular rate vector in world space
50 auto angular_rate = ((q - old_q) * inv_q).xyz * 2;
51 angular_rate *= 1000 / update_millisecond / MathUtil::PI * 180;
52
53 // Transform the two vectors from world space to 3DS space
54 gravity = QuaternionRotate(inv_q, gravity);
55 angular_rate = QuaternionRotate(inv_q, angular_rate);
56
57 // Update the sensor state
58 emu_window.AccelerometerChanged(gravity.x, gravity.y, gravity.z);
59 emu_window.GyroscopeChanged(angular_rate.x, angular_rate.y, angular_rate.z);
60 }
61}
62
63void MotionEmu::BeginTilt(int x, int y) {
64 mouse_origin = Math::MakeVec(x, y);
65 is_tilting = true;
66}
67
68void MotionEmu::Tilt(int x, int y) {
69 constexpr float SENSITIVITY = 0.01f;
70 auto mouse_move = Math::MakeVec(x, y) - mouse_origin;
71 if (is_tilting) {
72 std::lock_guard<std::mutex> guard(tilt_mutex);
73 if (mouse_move.x == 0 && mouse_move.y == 0) {
74 tilt_angle = 0;
75 } else {
76 tilt_direction = mouse_move.Cast<float>();
77 tilt_angle = MathUtil::Clamp(tilt_direction.Normalize() * SENSITIVITY, 0.0f,
78 MathUtil::PI * 0.5f);
79 }
80 }
81}
82
83void MotionEmu::EndTilt() {
84 std::lock_guard<std::mutex> guard(tilt_mutex);
85 tilt_angle = 0;
86 is_tilting = false;
87}
88
89} // namespace Motion
diff --git a/src/core/frontend/motion_emu.h b/src/core/frontend/motion_emu.h
deleted file mode 100644
index 99d41a726..000000000
--- a/src/core/frontend/motion_emu.h
+++ /dev/null
@@ -1,52 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6#include "common/thread.h"
7#include "common/vector_math.h"
8
9class EmuWindow;
10
11namespace Motion {
12
13class MotionEmu final {
14public:
15 MotionEmu(EmuWindow& emu_window);
16 ~MotionEmu();
17
18 /**
19 * Signals that a motion sensor tilt has begun.
20 * @param x the x-coordinate of the cursor
21 * @param y the y-coordinate of the cursor
22 */
23 void BeginTilt(int x, int y);
24
25 /**
26 * Signals that a motion sensor tilt is occurring.
27 * @param x the x-coordinate of the cursor
28 * @param y the y-coordinate of the cursor
29 */
30 void Tilt(int x, int y);
31
32 /**
33 * Signals that a motion sensor tilt has ended.
34 */
35 void EndTilt();
36
37private:
38 Math::Vec2<int> mouse_origin;
39
40 std::mutex tilt_mutex;
41 Math::Vec2<float> tilt_direction;
42 float tilt_angle = 0;
43
44 bool is_tilting = false;
45
46 Common::Event shutdown_event;
47 std::thread motion_emu_thread;
48
49 void MotionEmuThread(EmuWindow& emu_window);
50};
51
52} // namespace Motion
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
index 0109fa2b2..58d94768c 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -34,8 +34,6 @@ static bool shared_font_loaded = false;
34static bool shared_font_relocated = false; 34static bool shared_font_relocated = false;
35 35
36static Kernel::SharedPtr<Kernel::Mutex> lock; 36static Kernel::SharedPtr<Kernel::Mutex> lock;
37static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event
38static Kernel::SharedPtr<Kernel::Event> parameter_event; ///< APT parameter event
39 37
40static u32 cpu_percent; ///< CPU time available to the running application 38static u32 cpu_percent; ///< CPU time available to the running application
41 39
@@ -44,32 +42,160 @@ static u8 unknown_ns_state_field;
44 42
45static ScreencapPostPermission screen_capture_post_permission; 43static ScreencapPostPermission screen_capture_post_permission;
46 44
47/// Parameter data to be returned in the next call to Glance/ReceiveParameter 45/// Parameter data to be returned in the next call to Glance/ReceiveParameter.
46/// TODO(Subv): Use std::optional once we migrate to C++17.
48static boost::optional<MessageParameter> next_parameter; 47static boost::optional<MessageParameter> next_parameter;
49 48
49enum class AppletPos { Application = 0, Library = 1, System = 2, SysLibrary = 3, Resident = 4 };
50
51static constexpr size_t NumAppletSlot = 4;
52
53enum class AppletSlot : u8 {
54 Application,
55 SystemApplet,
56 HomeMenu,
57 LibraryApplet,
58
59 // An invalid tag
60 Error,
61};
62
63union AppletAttributes {
64 u32 raw;
65
66 BitField<0, 3, u32> applet_pos;
67
68 AppletAttributes() : raw(0) {}
69 AppletAttributes(u32 attributes) : raw(attributes) {}
70};
71
72struct AppletSlotData {
73 AppletId applet_id;
74 AppletSlot slot;
75 bool registered;
76 AppletAttributes attributes;
77 Kernel::SharedPtr<Kernel::Event> notification_event;
78 Kernel::SharedPtr<Kernel::Event> parameter_event;
79};
80
81// Holds data about the concurrently running applets in the system.
82static std::array<AppletSlotData, NumAppletSlot> applet_slots = {};
83
84// This overload returns nullptr if no applet with the specified id has been started.
85static AppletSlotData* GetAppletSlotData(AppletId id) {
86 auto GetSlot = [](AppletSlot slot) -> AppletSlotData* {
87 return &applet_slots[static_cast<size_t>(slot)];
88 };
89
90 if (id == AppletId::Application) {
91 auto* slot = GetSlot(AppletSlot::Application);
92 if (slot->applet_id != AppletId::None)
93 return slot;
94
95 return nullptr;
96 }
97
98 if (id == AppletId::AnySystemApplet) {
99 auto* system_slot = GetSlot(AppletSlot::SystemApplet);
100 if (system_slot->applet_id != AppletId::None)
101 return system_slot;
102
103 // The Home Menu is also a system applet, but it lives in its own slot to be able to run
104 // concurrently with other system applets.
105 auto* home_slot = GetSlot(AppletSlot::HomeMenu);
106 if (home_slot->applet_id != AppletId::None)
107 return home_slot;
108
109 return nullptr;
110 }
111
112 if (id == AppletId::AnyLibraryApplet || id == AppletId::AnySysLibraryApplet) {
113 auto* slot = GetSlot(AppletSlot::LibraryApplet);
114 if (slot->applet_id == AppletId::None)
115 return nullptr;
116
117 u32 applet_pos = slot->attributes.applet_pos;
118
119 if (id == AppletId::AnyLibraryApplet && applet_pos == static_cast<u32>(AppletPos::Library))
120 return slot;
121
122 if (id == AppletId::AnySysLibraryApplet &&
123 applet_pos == static_cast<u32>(AppletPos::SysLibrary))
124 return slot;
125
126 return nullptr;
127 }
128
129 if (id == AppletId::HomeMenu || id == AppletId::AlternateMenu) {
130 auto* slot = GetSlot(AppletSlot::HomeMenu);
131 if (slot->applet_id != AppletId::None)
132 return slot;
133
134 return nullptr;
135 }
136
137 for (auto& slot : applet_slots) {
138 if (slot.applet_id == id)
139 return &slot;
140 }
141
142 return nullptr;
143}
144
145static AppletSlotData* GetAppletSlotData(AppletAttributes attributes) {
146 // Mapping from AppletPos to AppletSlot
147 static constexpr std::array<AppletSlot, 6> applet_position_slots = {
148 AppletSlot::Application, AppletSlot::LibraryApplet, AppletSlot::SystemApplet,
149 AppletSlot::LibraryApplet, AppletSlot::Error, AppletSlot::LibraryApplet};
150
151 u32 applet_pos = attributes.applet_pos;
152 if (applet_pos >= applet_position_slots.size())
153 return nullptr;
154
155 AppletSlot slot = applet_position_slots[applet_pos];
156
157 if (slot == AppletSlot::Error)
158 return nullptr;
159
160 return &applet_slots[static_cast<size_t>(slot)];
161}
162
50void SendParameter(const MessageParameter& parameter) { 163void SendParameter(const MessageParameter& parameter) {
51 next_parameter = parameter; 164 next_parameter = parameter;
52 // Signal the event to let the application know that a new parameter is ready to be read 165 // Signal the event to let the receiver know that a new parameter is ready to be read
53 parameter_event->Signal(); 166 auto* const slot_data = GetAppletSlotData(static_cast<AppletId>(parameter.destination_id));
167 ASSERT(slot_data);
168
169 slot_data->parameter_event->Signal();
54} 170}
55 171
56void Initialize(Service::Interface* self) { 172void Initialize(Service::Interface* self) {
57 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2, 2, 0); // 0x20080 173 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2, 2, 0); // 0x20080
58 u32 app_id = rp.Pop<u32>(); 174 u32 app_id = rp.Pop<u32>();
59 u32 flags = rp.Pop<u32>(); 175 u32 attributes = rp.Pop<u32>();
60 IPC::RequestBuilder rb = rp.MakeBuilder(1, 3);
61 rb.Push(RESULT_SUCCESS);
62 rb.PushCopyHandles(Kernel::g_handle_table.Create(notification_event).Unwrap(),
63 Kernel::g_handle_table.Create(parameter_event).Unwrap());
64 176
65 // TODO(bunnei): Check if these events are cleared every time Initialize is called. 177 LOG_DEBUG(Service_APT, "called app_id=0x%08X, attributes=0x%08X", app_id, attributes);
66 notification_event->Clear(); 178
67 parameter_event->Clear(); 179 auto* const slot_data = GetAppletSlotData(attributes);
180
181 // Note: The real NS service does not check if the attributes value is valid before accessing
182 // the data in the array
183 ASSERT_MSG(slot_data, "Invalid application attributes");
184
185 if (slot_data->registered) {
186 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
187 rb.Push(ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet,
188 ErrorSummary::InvalidState, ErrorLevel::Status));
189 return;
190 }
68 191
69 ASSERT_MSG((nullptr != lock), "Cannot initialize without lock"); 192 slot_data->applet_id = static_cast<AppletId>(app_id);
70 lock->Release(); 193 slot_data->attributes.raw = attributes;
71 194
72 LOG_DEBUG(Service_APT, "called app_id=0x%08X, flags=0x%08X", app_id, flags); 195 IPC::RequestBuilder rb = rp.MakeBuilder(1, 3);
196 rb.Push(RESULT_SUCCESS);
197 rb.PushCopyHandles(Kernel::g_handle_table.Create(slot_data->notification_event).Unwrap(),
198 Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap());
73} 199}
74 200
75void GetSharedFont(Service::Interface* self) { 201void GetSharedFont(Service::Interface* self) {
@@ -120,7 +246,12 @@ void GetLockHandle(Service::Interface* self) {
120 // this will cause the app to wait until parameter_event is signaled. 246 // this will cause the app to wait until parameter_event is signaled.
121 u32 applet_attributes = rp.Pop<u32>(); 247 u32 applet_attributes = rp.Pop<u32>();
122 IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); 248 IPC::RequestBuilder rb = rp.MakeBuilder(3, 2);
123 rb.Push(RESULT_SUCCESS); // No error 249 rb.Push(RESULT_SUCCESS); // No error
250
251 // TODO(Subv): The output attributes should have an AppletPos of either Library or System |
252 // Library (depending on the type of the last launched applet) if the input attributes'
253 // AppletPos has the Library bit set.
254
124 rb.Push(applet_attributes); // Applet Attributes, this value is passed to Enable. 255 rb.Push(applet_attributes); // Applet Attributes, this value is passed to Enable.
125 rb.Push<u32>(0); // Least significant bit = power button state 256 rb.Push<u32>(0); // Least significant bit = power button state
126 Kernel::Handle handle_copy = Kernel::g_handle_table.Create(lock).Unwrap(); 257 Kernel::Handle handle_copy = Kernel::g_handle_table.Create(lock).Unwrap();
@@ -133,10 +264,22 @@ void GetLockHandle(Service::Interface* self) {
133void Enable(Service::Interface* self) { 264void Enable(Service::Interface* self) {
134 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3, 1, 0); // 0x30040 265 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3, 1, 0); // 0x30040
135 u32 attributes = rp.Pop<u32>(); 266 u32 attributes = rp.Pop<u32>();
267
268 LOG_DEBUG(Service_APT, "called attributes=0x%08X", attributes);
269
136 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); 270 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
137 rb.Push(RESULT_SUCCESS); // No error 271
138 parameter_event->Signal(); // Let the application know that it has been started 272 auto* const slot_data = GetAppletSlotData(attributes);
139 LOG_WARNING(Service_APT, "(STUBBED) called attributes=0x%08X", attributes); 273
274 if (!slot_data) {
275 rb.Push(ResultCode(ErrCodes::InvalidAppletSlot, ErrorModule::Applet,
276 ErrorSummary::InvalidState, ErrorLevel::Status));
277 return;
278 }
279
280 slot_data->registered = true;
281
282 rb.Push(RESULT_SUCCESS);
140} 283}
141 284
142void GetAppletManInfo(Service::Interface* self) { 285void GetAppletManInfo(Service::Interface* self) {
@@ -154,22 +297,27 @@ void GetAppletManInfo(Service::Interface* self) {
154 297
155void IsRegistered(Service::Interface* self) { 298void IsRegistered(Service::Interface* self) {
156 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x9, 1, 0); // 0x90040 299 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x9, 1, 0); // 0x90040
157 u32 app_id = rp.Pop<u32>(); 300 AppletId app_id = static_cast<AppletId>(rp.Pop<u32>());
158 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); 301 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
159 rb.Push(RESULT_SUCCESS); // No error 302 rb.Push(RESULT_SUCCESS); // No error
160 303
161 // TODO(Subv): An application is considered "registered" if it has already called APT::Enable 304 auto* const slot_data = GetAppletSlotData(app_id);
162 // handle this properly once we implement multiprocess support. 305
163 bool is_registered = false; // Set to not registered by default 306 // Check if an LLE applet was registered first, then fallback to HLE applets
307 bool is_registered = slot_data && slot_data->registered;
164 308
165 if (app_id == static_cast<u32>(AppletId::AnyLibraryApplet)) { 309 if (!is_registered) {
166 is_registered = HLE::Applets::IsLibraryAppletRunning(); 310 if (app_id == AppletId::AnyLibraryApplet) {
167 } else if (auto applet = HLE::Applets::Applet::Get(static_cast<AppletId>(app_id))) { 311 is_registered = HLE::Applets::IsLibraryAppletRunning();
168 is_registered = true; // Set to registered 312 } else if (auto applet = HLE::Applets::Applet::Get(app_id)) {
313 // The applet exists, set it as registered.
314 is_registered = true;
315 }
169 } 316 }
317
170 rb.Push(is_registered); 318 rb.Push(is_registered);
171 319
172 LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); 320 LOG_DEBUG(Service_APT, "called app_id=0x%08X", static_cast<u32>(app_id));
173} 321}
174 322
175void InquireNotification(Service::Interface* self) { 323void InquireNotification(Service::Interface* self) {
@@ -864,14 +1012,23 @@ void Init() {
864 screen_capture_post_permission = 1012 screen_capture_post_permission =
865 ScreencapPostPermission::CleanThePermission; // TODO(JamePeng): verify the initial value 1013 ScreencapPostPermission::CleanThePermission; // TODO(JamePeng): verify the initial value
866 1014
867 // TODO(bunnei): Check if these are created in Initialize or on APT process startup. 1015 for (size_t slot = 0; slot < applet_slots.size(); ++slot) {
868 notification_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Notification"); 1016 auto& slot_data = applet_slots[slot];
869 parameter_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Start"); 1017 slot_data.slot = static_cast<AppletSlot>(slot);
1018 slot_data.applet_id = AppletId::None;
1019 slot_data.attributes.raw = 0;
1020 slot_data.registered = false;
1021 slot_data.notification_event =
1022 Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Notification");
1023 slot_data.parameter_event =
1024 Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Parameter");
1025 }
870 1026
871 // Initialize the parameter to wake up the application. 1027 // Initialize the parameter to wake up the application.
872 next_parameter.emplace(); 1028 next_parameter.emplace();
873 next_parameter->signal = static_cast<u32>(SignalType::Wakeup); 1029 next_parameter->signal = static_cast<u32>(SignalType::Wakeup);
874 next_parameter->destination_id = static_cast<u32>(AppletId::Application); 1030 next_parameter->destination_id = static_cast<u32>(AppletId::Application);
1031 applet_slots[static_cast<size_t>(AppletSlot::Application)].parameter_event->Signal();
875} 1032}
876 1033
877void Shutdown() { 1034void Shutdown() {
@@ -879,8 +1036,12 @@ void Shutdown() {
879 shared_font_loaded = false; 1036 shared_font_loaded = false;
880 shared_font_relocated = false; 1037 shared_font_relocated = false;
881 lock = nullptr; 1038 lock = nullptr;
882 notification_event = nullptr; 1039
883 parameter_event = nullptr; 1040 for (auto& slot : applet_slots) {
1041 slot.registered = false;
1042 slot.notification_event = nullptr;
1043 slot.parameter_event = nullptr;
1044 }
884 1045
885 next_parameter = boost::none; 1046 next_parameter = boost::none;
886 1047
diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h
index 106754853..96b28b438 100644
--- a/src/core/hle/service/apt/apt.h
+++ b/src/core/hle/service/apt/apt.h
@@ -72,6 +72,8 @@ enum class SignalType : u32 {
72 72
73/// App Id's used by APT functions 73/// App Id's used by APT functions
74enum class AppletId : u32 { 74enum class AppletId : u32 {
75 None = 0,
76 AnySystemApplet = 0x100,
75 HomeMenu = 0x101, 77 HomeMenu = 0x101,
76 AlternateMenu = 0x103, 78 AlternateMenu = 0x103,
77 Camera = 0x110, 79 Camera = 0x110,
@@ -83,6 +85,7 @@ enum class AppletId : u32 {
83 Miiverse = 0x117, 85 Miiverse = 0x117,
84 MiiversePost = 0x118, 86 MiiversePost = 0x118,
85 AmiiboSettings = 0x119, 87 AmiiboSettings = 0x119,
88 AnySysLibraryApplet = 0x200,
86 SoftwareKeyboard1 = 0x201, 89 SoftwareKeyboard1 = 0x201,
87 Ed1 = 0x202, 90 Ed1 = 0x202,
88 PnoteApp = 0x204, 91 PnoteApp = 0x204,
@@ -119,8 +122,9 @@ enum class ScreencapPostPermission : u32 {
119namespace ErrCodes { 122namespace ErrCodes {
120enum { 123enum {
121 ParameterPresent = 2, 124 ParameterPresent = 2,
125 InvalidAppletSlot = 4,
122}; 126};
123} 127} // namespace ErrCodes
124 128
125/// Send a parameter to the currently-running application, which will read it via ReceiveParameter 129/// Send a parameter to the currently-running application, which will read it via ReceiveParameter
126void SendParameter(const MessageParameter& parameter); 130void SendParameter(const MessageParameter& parameter);
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp
index 7d746054f..42f8950f9 100644
--- a/src/core/hle/service/dsp_dsp.cpp
+++ b/src/core/hle/service/dsp_dsp.cpp
@@ -147,9 +147,10 @@ static void LoadComponent(Service::Interface* self) {
147 LOG_INFO(Service_DSP, "Firmware hash: %#" PRIx64, 147 LOG_INFO(Service_DSP, "Firmware hash: %#" PRIx64,
148 Common::ComputeHash64(component_data.data(), component_data.size())); 148 Common::ComputeHash64(component_data.data(), component_data.size()));
149 // Some versions of the firmware have the location of DSP structures listed here. 149 // Some versions of the firmware have the location of DSP structures listed here.
150 ASSERT(size > 0x37C); 150 if (size > 0x37C) {
151 LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64, 151 LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64,
152 Common::ComputeHash64(component_data.data() + 0x340, 60)); 152 Common::ComputeHash64(component_data.data() + 0x340, 60));
153 }
153 154
154 LOG_WARNING(Service_DSP, 155 LOG_WARNING(Service_DSP,
155 "(STUBBED) called size=0x%X, prog_mask=0x%08X, data_mask=0x%08X, buffer=0x%08X", 156 "(STUBBED) called size=0x%X, prog_mask=0x%08X, data_mask=0x%08X, buffer=0x%08X",
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 2014b8461..31f34a7ae 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -7,6 +7,7 @@
7#include <cmath> 7#include <cmath>
8#include <memory> 8#include <memory>
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/core.h"
10#include "core/core_timing.h" 11#include "core/core_timing.h"
11#include "core/frontend/emu_window.h" 12#include "core/frontend/emu_window.h"
12#include "core/frontend/input.h" 13#include "core/frontend/input.h"
@@ -50,10 +51,14 @@ constexpr u64 pad_update_ticks = BASE_CLOCK_RATE_ARM11 / 234;
50constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104; 51constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104;
51constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101; 52constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101;
52 53
54constexpr float accelerometer_coef = 512.0f; // measured from hw test result
55constexpr float gyroscope_coef = 14.375f; // got from hwtest GetGyroscopeLowRawToDpsCoefficient call
56
53static std::atomic<bool> is_device_reload_pending; 57static std::atomic<bool> is_device_reload_pending;
54static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> 58static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
55 buttons; 59 buttons;
56static std::unique_ptr<Input::AnalogDevice> circle_pad; 60static std::unique_ptr<Input::AnalogDevice> circle_pad;
61static std::unique_ptr<Input::MotionDevice> motion_device;
57 62
58DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) { 63DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) {
59 // 30 degree and 60 degree are angular thresholds for directions 64 // 30 degree and 60 degree are angular thresholds for directions
@@ -90,6 +95,7 @@ static void LoadInputDevices() {
90 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>); 95 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
91 circle_pad = Input::CreateDevice<Input::AnalogDevice>( 96 circle_pad = Input::CreateDevice<Input::AnalogDevice>(
92 Settings::values.analogs[Settings::NativeAnalog::CirclePad]); 97 Settings::values.analogs[Settings::NativeAnalog::CirclePad]);
98 motion_device = Input::CreateDevice<Input::MotionDevice>(Settings::values.motion_device);
93} 99}
94 100
95static void UnloadInputDevices() { 101static void UnloadInputDevices() {
@@ -97,6 +103,7 @@ static void UnloadInputDevices() {
97 button.reset(); 103 button.reset();
98 } 104 }
99 circle_pad.reset(); 105 circle_pad.reset();
106 motion_device.reset();
100} 107}
101 108
102static void UpdatePadCallback(u64 userdata, int cycles_late) { 109static void UpdatePadCallback(u64 userdata, int cycles_late) {
@@ -193,10 +200,19 @@ static void UpdateAccelerometerCallback(u64 userdata, int cycles_late) {
193 mem->accelerometer.index = next_accelerometer_index; 200 mem->accelerometer.index = next_accelerometer_index;
194 next_accelerometer_index = (next_accelerometer_index + 1) % mem->accelerometer.entries.size(); 201 next_accelerometer_index = (next_accelerometer_index + 1) % mem->accelerometer.entries.size();
195 202
203 Math::Vec3<float> accel;
204 std::tie(accel, std::ignore) = motion_device->GetStatus();
205 accel *= accelerometer_coef;
206 // TODO(wwylele): do a time stretch like the one in UpdateGyroscopeCallback
207 // The time stretch formula should be like
208 // stretched_vector = (raw_vector - gravity) * stretch_ratio + gravity
209
196 AccelerometerDataEntry& accelerometer_entry = 210 AccelerometerDataEntry& accelerometer_entry =
197 mem->accelerometer.entries[mem->accelerometer.index]; 211 mem->accelerometer.entries[mem->accelerometer.index];
198 std::tie(accelerometer_entry.x, accelerometer_entry.y, accelerometer_entry.z) = 212
199 VideoCore::g_emu_window->GetAccelerometerState(); 213 accelerometer_entry.x = static_cast<s16>(accel.x);
214 accelerometer_entry.y = static_cast<s16>(accel.y);
215 accelerometer_entry.z = static_cast<s16>(accel.z);
200 216
201 // Make up "raw" entry 217 // Make up "raw" entry
202 // TODO(wwylele): 218 // TODO(wwylele):
@@ -227,8 +243,14 @@ static void UpdateGyroscopeCallback(u64 userdata, int cycles_late) {
227 next_gyroscope_index = (next_gyroscope_index + 1) % mem->gyroscope.entries.size(); 243 next_gyroscope_index = (next_gyroscope_index + 1) % mem->gyroscope.entries.size();
228 244
229 GyroscopeDataEntry& gyroscope_entry = mem->gyroscope.entries[mem->gyroscope.index]; 245 GyroscopeDataEntry& gyroscope_entry = mem->gyroscope.entries[mem->gyroscope.index];
230 std::tie(gyroscope_entry.x, gyroscope_entry.y, gyroscope_entry.z) = 246
231 VideoCore::g_emu_window->GetGyroscopeState(); 247 Math::Vec3<float> gyro;
248 std::tie(std::ignore, gyro) = motion_device->GetStatus();
249 double stretch = Core::System::GetInstance().perf_stats.GetLastFrameTimeScale();
250 gyro *= gyroscope_coef * stretch;
251 gyroscope_entry.x = static_cast<s16>(gyro.x);
252 gyroscope_entry.y = static_cast<s16>(gyro.y);
253 gyroscope_entry.z = static_cast<s16>(gyro.z);
232 254
233 // Make up "raw" entry 255 // Make up "raw" entry
234 mem->gyroscope.raw_entry.x = gyroscope_entry.x; 256 mem->gyroscope.raw_entry.x = gyroscope_entry.x;
@@ -326,7 +348,7 @@ void GetGyroscopeLowRawToDpsCoefficient(Service::Interface* self) {
326 348
327 cmd_buff[1] = RESULT_SUCCESS.raw; 349 cmd_buff[1] = RESULT_SUCCESS.raw;
328 350
329 f32 coef = VideoCore::g_emu_window->GetGyroscopeRawToDpsCoefficient(); 351 f32 coef = gyroscope_coef;
330 memcpy(&cmd_buff[2], &coef, 4); 352 memcpy(&cmd_buff[2], &coef, 4);
331} 353}
332 354
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index c007069a9..7aff7f29b 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -20,6 +20,7 @@
20#include "core/loader/ncch.h" 20#include "core/loader/ncch.h"
21#include "core/loader/smdh.h" 21#include "core/loader/smdh.h"
22#include "core/memory.h" 22#include "core/memory.h"
23#include "network/network.h"
23 24
24//////////////////////////////////////////////////////////////////////////////////////////////////// 25////////////////////////////////////////////////////////////////////////////////////////////////////
25// Loader namespace 26// Loader namespace
@@ -350,6 +351,13 @@ ResultStatus AppLoader_NCCH::Load() {
350 351
351 Core::Telemetry().AddField(Telemetry::FieldType::Session, "ProgramId", program_id); 352 Core::Telemetry().AddField(Telemetry::FieldType::Session, "ProgramId", program_id);
352 353
354 if (auto room_member = Network::GetRoomMember().lock()) {
355 Network::GameInfo game_info;
356 ReadTitle(game_info.name);
357 game_info.id = ncch_header.program_id;
358 room_member->SendGameInfo(game_info);
359 }
360
353 is_loaded = true; // Set state to loaded 361 is_loaded = true; // Set state to loaded
354 362
355 result = LoadExec(); // Load the executable into memory for booting 363 result = LoadExec(); // Load the executable into memory for booting
diff --git a/src/core/settings.h b/src/core/settings.h
index ee16bb90a..7e15b119b 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -79,6 +79,7 @@ struct Values {
79 // Controls 79 // Controls
80 std::array<std::string, NativeButton::NumButtons> buttons; 80 std::array<std::string, NativeButton::NumButtons> buttons;
81 std::array<std::string, NativeAnalog::NumAnalogs> analogs; 81 std::array<std::string, NativeAnalog::NumAnalogs> analogs;
82 std::string motion_device;
82 83
83 // Core 84 // Core
84 bool use_cpu_jit; 85 bool use_cpu_jit;
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index e3e36ada7..92792a702 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -2,12 +2,14 @@ set(SRCS
2 analog_from_button.cpp 2 analog_from_button.cpp
3 keyboard.cpp 3 keyboard.cpp
4 main.cpp 4 main.cpp
5 motion_emu.cpp
5 ) 6 )
6 7
7set(HEADERS 8set(HEADERS
8 analog_from_button.h 9 analog_from_button.h
9 keyboard.h 10 keyboard.h
10 main.h 11 main.h
12 motion_emu.h
11 ) 13 )
12 14
13if(SDL2_FOUND) 15if(SDL2_FOUND)
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 699f41e6b..557353740 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -7,6 +7,7 @@
7#include "input_common/analog_from_button.h" 7#include "input_common/analog_from_button.h"
8#include "input_common/keyboard.h" 8#include "input_common/keyboard.h"
9#include "input_common/main.h" 9#include "input_common/main.h"
10#include "input_common/motion_emu.h"
10#ifdef HAVE_SDL2 11#ifdef HAVE_SDL2
11#include "input_common/sdl/sdl.h" 12#include "input_common/sdl/sdl.h"
12#endif 13#endif
@@ -14,12 +15,16 @@
14namespace InputCommon { 15namespace InputCommon {
15 16
16static std::shared_ptr<Keyboard> keyboard; 17static std::shared_ptr<Keyboard> keyboard;
18static std::shared_ptr<MotionEmu> motion_emu;
17 19
18void Init() { 20void Init() {
19 keyboard = std::make_shared<InputCommon::Keyboard>(); 21 keyboard = std::make_shared<Keyboard>();
20 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard); 22 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
21 Input::RegisterFactory<Input::AnalogDevice>("analog_from_button", 23 Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
22 std::make_shared<InputCommon::AnalogFromButton>()); 24 std::make_shared<AnalogFromButton>());
25 motion_emu = std::make_shared<MotionEmu>();
26 Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu);
27
23#ifdef HAVE_SDL2 28#ifdef HAVE_SDL2
24 SDL::Init(); 29 SDL::Init();
25#endif 30#endif
@@ -29,6 +34,8 @@ void Shutdown() {
29 Input::UnregisterFactory<Input::ButtonDevice>("keyboard"); 34 Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
30 keyboard.reset(); 35 keyboard.reset();
31 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button"); 36 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
37 Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
38 motion_emu.reset();
32 39
33#ifdef HAVE_SDL2 40#ifdef HAVE_SDL2
34 SDL::Shutdown(); 41 SDL::Shutdown();
@@ -39,6 +46,10 @@ Keyboard* GetKeyboard() {
39 return keyboard.get(); 46 return keyboard.get();
40} 47}
41 48
49MotionEmu* GetMotionEmu() {
50 return motion_emu.get();
51}
52
42std::string GenerateKeyboardParam(int key_code) { 53std::string GenerateKeyboardParam(int key_code) {
43 Common::ParamPackage param{ 54 Common::ParamPackage param{
44 {"engine", "keyboard"}, {"code", std::to_string(key_code)}, 55 {"engine", "keyboard"}, {"code", std::to_string(key_code)},
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 140bbd014..5604f0fa8 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -11,7 +11,7 @@ namespace InputCommon {
11/// Initializes and registers all built-in input device factories. 11/// Initializes and registers all built-in input device factories.
12void Init(); 12void Init();
13 13
14/// Unresisters all build-in input device factories and shut them down. 14/// Deregisters all built-in input device factories and shuts them down.
15void Shutdown(); 15void Shutdown();
16 16
17class Keyboard; 17class Keyboard;
@@ -19,6 +19,11 @@ class Keyboard;
19/// Gets the keyboard button device factory. 19/// Gets the keyboard button device factory.
20Keyboard* GetKeyboard(); 20Keyboard* GetKeyboard();
21 21
22class MotionEmu;
23
24/// Gets the motion emulation factory.
25MotionEmu* GetMotionEmu();
26
22/// Generates a serialized param package for creating a keyboard button device 27/// Generates a serialized param package for creating a keyboard button device
23std::string GenerateKeyboardParam(int key_code); 28std::string GenerateKeyboardParam(int key_code);
24 29
diff --git a/src/input_common/motion_emu.cpp b/src/input_common/motion_emu.cpp
new file mode 100644
index 000000000..a1761f184
--- /dev/null
+++ b/src/input_common/motion_emu.cpp
@@ -0,0 +1,165 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <chrono>
6#include <mutex>
7#include <thread>
8#include <tuple>
9#include "common/math_util.h"
10#include "common/quaternion.h"
11#include "common/thread.h"
12#include "common/vector_math.h"
13#include "input_common/motion_emu.h"
14
15namespace InputCommon {
16
17// Implementation class of the motion emulation device
18class MotionEmuDevice {
19public:
20 MotionEmuDevice(int update_millisecond, float sensitivity)
21 : update_millisecond(update_millisecond),
22 update_duration(std::chrono::duration_cast<std::chrono::steady_clock::duration>(
23 std::chrono::milliseconds(update_millisecond))),
24 sensitivity(sensitivity), motion_emu_thread(&MotionEmuDevice::MotionEmuThread, this) {}
25
26 ~MotionEmuDevice() {
27 if (motion_emu_thread.joinable()) {
28 shutdown_event.Set();
29 motion_emu_thread.join();
30 }
31 }
32
33 void BeginTilt(int x, int y) {
34 mouse_origin = Math::MakeVec(x, y);
35 is_tilting = true;
36 }
37
38 void Tilt(int x, int y) {
39 auto mouse_move = Math::MakeVec(x, y) - mouse_origin;
40 if (is_tilting) {
41 std::lock_guard<std::mutex> guard(tilt_mutex);
42 if (mouse_move.x == 0 && mouse_move.y == 0) {
43 tilt_angle = 0;
44 } else {
45 tilt_direction = mouse_move.Cast<float>();
46 tilt_angle = MathUtil::Clamp(tilt_direction.Normalize() * sensitivity, 0.0f,
47 MathUtil::PI * 0.5f);
48 }
49 }
50 }
51
52 void EndTilt() {
53 std::lock_guard<std::mutex> guard(tilt_mutex);
54 tilt_angle = 0;
55 is_tilting = false;
56 }
57
58 std::tuple<Math::Vec3<float>, Math::Vec3<float>> GetStatus() {
59 std::lock_guard<std::mutex> guard(status_mutex);
60 return status;
61 }
62
63private:
64 const int update_millisecond;
65 const std::chrono::steady_clock::duration update_duration;
66 const float sensitivity;
67
68 Math::Vec2<int> mouse_origin;
69
70 std::mutex tilt_mutex;
71 Math::Vec2<float> tilt_direction;
72 float tilt_angle = 0;
73
74 bool is_tilting = false;
75
76 Common::Event shutdown_event;
77 std::thread motion_emu_thread;
78
79 std::tuple<Math::Vec3<float>, Math::Vec3<float>> status;
80 std::mutex status_mutex;
81
82 void MotionEmuThread() {
83 auto update_time = std::chrono::steady_clock::now();
84 Math::Quaternion<float> q = MakeQuaternion(Math::Vec3<float>(), 0);
85 Math::Quaternion<float> old_q;
86
87 while (!shutdown_event.WaitUntil(update_time)) {
88 update_time += update_duration;
89 old_q = q;
90
91 {
92 std::lock_guard<std::mutex> guard(tilt_mutex);
93
94 // Find the quaternion describing current 3DS tilting
95 q = MakeQuaternion(Math::MakeVec(-tilt_direction.y, 0.0f, tilt_direction.x),
96 tilt_angle);
97 }
98
99 auto inv_q = q.Inverse();
100
101 // Set the gravity vector in world space
102 auto gravity = Math::MakeVec(0.0f, -1.0f, 0.0f);
103
104 // Find the angular rate vector in world space
105 auto angular_rate = ((q - old_q) * inv_q).xyz * 2;
106 angular_rate *= 1000 / update_millisecond / MathUtil::PI * 180;
107
108 // Transform the two vectors from world space to 3DS space
109 gravity = QuaternionRotate(inv_q, gravity);
110 angular_rate = QuaternionRotate(inv_q, angular_rate);
111
112 // Update the sensor state
113 {
114 std::lock_guard<std::mutex> guard(status_mutex);
115 status = std::make_tuple(gravity, angular_rate);
116 }
117 }
118 }
119};
120
121// Interface wrapper held by input receiver as a unique_ptr. It holds the implementation class as
122// a shared_ptr, which is also observed by the factory class as a weak_ptr. In this way the factory
123// can forward all the inputs to the implementation only when it is valid.
124class MotionEmuDeviceWrapper : public Input::MotionDevice {
125public:
126 MotionEmuDeviceWrapper(int update_millisecond, float sensitivity) {
127 device = std::make_shared<MotionEmuDevice>(update_millisecond, sensitivity);
128 }
129
130 std::tuple<Math::Vec3<float>, Math::Vec3<float>> GetStatus() const {
131 return device->GetStatus();
132 }
133
134 std::shared_ptr<MotionEmuDevice> device;
135};
136
137std::unique_ptr<Input::MotionDevice> MotionEmu::Create(const Common::ParamPackage& params) {
138 int update_period = params.Get("update_period", 100);
139 float sensitivity = params.Get("sensitivity", 0.01f);
140 auto device_wrapper = std::make_unique<MotionEmuDeviceWrapper>(update_period, sensitivity);
141 // Previously created device is disconnected here. Having two motion devices for 3DS is not
142 // expected.
143 current_device = device_wrapper->device;
144 return std::move(device_wrapper);
145}
146
147void MotionEmu::BeginTilt(int x, int y) {
148 if (auto ptr = current_device.lock()) {
149 ptr->BeginTilt(x, y);
150 }
151}
152
153void MotionEmu::Tilt(int x, int y) {
154 if (auto ptr = current_device.lock()) {
155 ptr->Tilt(x, y);
156 }
157}
158
159void MotionEmu::EndTilt() {
160 if (auto ptr = current_device.lock()) {
161 ptr->EndTilt();
162 }
163}
164
165} // namespace InputCommon
diff --git a/src/input_common/motion_emu.h b/src/input_common/motion_emu.h
new file mode 100644
index 000000000..7a7e22467
--- /dev/null
+++ b/src/input_common/motion_emu.h
@@ -0,0 +1,46 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/frontend/input.h"
8
9namespace InputCommon {
10
11class MotionEmuDevice;
12
13class MotionEmu : public Input::Factory<Input::MotionDevice> {
14public:
15 /**
16 * Creates a motion device emulated from mouse input
17 * @param params contains parameters for creating the device:
18 * - "update_period": update period in milliseconds
19 * - "sensitivity": the coefficient converting mouse movement to tilting angle
20 */
21 std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override;
22
23 /**
24 * Signals that a motion sensor tilt has begun.
25 * @param x the x-coordinate of the cursor
26 * @param y the y-coordinate of the cursor
27 */
28 void BeginTilt(int x, int y);
29
30 /**
31 * Signals that a motion sensor tilt is occurring.
32 * @param x the x-coordinate of the cursor
33 * @param y the y-coordinate of the cursor
34 */
35 void Tilt(int x, int y);
36
37 /**
38 * Signals that a motion sensor tilt has ended.
39 */
40 void EndTilt();
41
42private:
43 std::weak_ptr<MotionEmuDevice> current_device;
44};
45
46} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl.cpp b/src/input_common/sdl/sdl.cpp
index 756ee58b7..d404afa89 100644
--- a/src/input_common/sdl/sdl.cpp
+++ b/src/input_common/sdl/sdl.cpp
@@ -159,7 +159,7 @@ public:
159 * - "axis"(optional): the index of the axis to bind 159 * - "axis"(optional): the index of the axis to bind
160 * - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", 160 * - "direction"(only used for hat): the direction name of the hat to bind. Can be "up",
161 * "down", "left" or "right" 161 * "down", "left" or "right"
162 * - "threshould"(only used for axis): a float value in (-1.0, 1.0) which the button is 162 * - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
163 * triggered if the axis value crosses 163 * triggered if the axis value crosses
164 * - "direction"(only used for axis): "+" means the button is triggered when the axis value 164 * - "direction"(only used for axis): "+" means the button is triggered when the axis value
165 * is greater than the threshold; "-" means the button is triggered when the axis value 165 * is greater than the threshold; "-" means the button is triggered when the axis value
diff --git a/src/network/packet.cpp b/src/network/packet.cpp
index 660e92c0d..cc60f2fbc 100644
--- a/src/network/packet.cpp
+++ b/src/network/packet.cpp
@@ -13,6 +13,18 @@
13 13
14namespace Network { 14namespace Network {
15 15
16#ifndef htonll
17u64 htonll(u64 x) {
18 return ((1 == htonl(1)) ? (x) : ((uint64_t)htonl((x)&0xFFFFFFFF) << 32) | htonl((x) >> 32));
19}
20#endif
21
22#ifndef ntohll
23u64 ntohll(u64 x) {
24 return ((1 == ntohl(1)) ? (x) : ((uint64_t)ntohl((x)&0xFFFFFFFF) << 32) | ntohl((x) >> 32));
25}
26#endif
27
16void Packet::Append(const void* in_data, std::size_t size_in_bytes) { 28void Packet::Append(const void* in_data, std::size_t size_in_bytes) {
17 if (in_data && (size_in_bytes > 0)) { 29 if (in_data && (size_in_bytes > 0)) {
18 std::size_t start = data.size(); 30 std::size_t start = data.size();
@@ -100,6 +112,20 @@ Packet& Packet::operator>>(u32& out_data) {
100 return *this; 112 return *this;
101} 113}
102 114
115Packet& Packet::operator>>(s64& out_data) {
116 s64 value;
117 Read(&value, sizeof(value));
118 out_data = ntohll(value);
119 return *this;
120}
121
122Packet& Packet::operator>>(u64& out_data) {
123 u64 value;
124 Read(&value, sizeof(value));
125 out_data = ntohll(value);
126 return *this;
127}
128
103Packet& Packet::operator>>(float& out_data) { 129Packet& Packet::operator>>(float& out_data) {
104 Read(&out_data, sizeof(out_data)); 130 Read(&out_data, sizeof(out_data));
105 return *this; 131 return *this;
@@ -183,6 +209,18 @@ Packet& Packet::operator<<(u32 in_data) {
183 return *this; 209 return *this;
184} 210}
185 211
212Packet& Packet::operator<<(s64 in_data) {
213 s64 toWrite = htonll(in_data);
214 Append(&toWrite, sizeof(toWrite));
215 return *this;
216}
217
218Packet& Packet::operator<<(u64 in_data) {
219 u64 toWrite = htonll(in_data);
220 Append(&toWrite, sizeof(toWrite));
221 return *this;
222}
223
186Packet& Packet::operator<<(float in_data) { 224Packet& Packet::operator<<(float in_data) {
187 Append(&in_data, sizeof(in_data)); 225 Append(&in_data, sizeof(in_data));
188 return *this; 226 return *this;
diff --git a/src/network/packet.h b/src/network/packet.h
index 94b351ab1..5a2e58dc2 100644
--- a/src/network/packet.h
+++ b/src/network/packet.h
@@ -72,6 +72,8 @@ public:
72 Packet& operator>>(u16& out_data); 72 Packet& operator>>(u16& out_data);
73 Packet& operator>>(s32& out_data); 73 Packet& operator>>(s32& out_data);
74 Packet& operator>>(u32& out_data); 74 Packet& operator>>(u32& out_data);
75 Packet& operator>>(s64& out_data);
76 Packet& operator>>(u64& out_data);
75 Packet& operator>>(float& out_data); 77 Packet& operator>>(float& out_data);
76 Packet& operator>>(double& out_data); 78 Packet& operator>>(double& out_data);
77 Packet& operator>>(char* out_data); 79 Packet& operator>>(char* out_data);
@@ -89,6 +91,8 @@ public:
89 Packet& operator<<(u16 in_data); 91 Packet& operator<<(u16 in_data);
90 Packet& operator<<(s32 in_data); 92 Packet& operator<<(s32 in_data);
91 Packet& operator<<(u32 in_data); 93 Packet& operator<<(u32 in_data);
94 Packet& operator<<(s64 in_data);
95 Packet& operator<<(u64 in_data);
92 Packet& operator<<(float in_data); 96 Packet& operator<<(float in_data);
93 Packet& operator<<(double in_data); 97 Packet& operator<<(double in_data);
94 Packet& operator<<(const char* in_data); 98 Packet& operator<<(const char* in_data);
diff --git a/src/network/room.cpp b/src/network/room.cpp
index fbbaf8b93..261049ab0 100644
--- a/src/network/room.cpp
+++ b/src/network/room.cpp
@@ -4,9 +4,9 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <atomic> 6#include <atomic>
7#include <mutex>
7#include <random> 8#include <random>
8#include <thread> 9#include <thread>
9#include <vector>
10#include "enet/enet.h" 10#include "enet/enet.h"
11#include "network/packet.h" 11#include "network/packet.h"
12#include "network/room.h" 12#include "network/room.h"
@@ -29,12 +29,14 @@ public:
29 29
30 struct Member { 30 struct Member {
31 std::string nickname; ///< The nickname of the member. 31 std::string nickname; ///< The nickname of the member.
32 std::string game_name; ///< The current game of the member 32 GameInfo game_info; ///< The current game of the member
33 MacAddress mac_address; ///< The assigned mac address of the member. 33 MacAddress mac_address; ///< The assigned mac address of the member.
34 ENetPeer* peer; ///< The remote peer. 34 ENetPeer* peer; ///< The remote peer.
35 }; 35 };
36 using MemberList = std::vector<Member>; 36 using MemberList = std::vector<Member>;
37 MemberList members; ///< Information about the members of this room. 37 MemberList members; ///< Information about the members of this room
38 mutable std::mutex member_mutex; ///< Mutex for locking the members list
39 /// This should be a std::shared_mutex as soon as C++17 is supported
38 40
39 RoomImpl() 41 RoomImpl()
40 : random_gen(std::random_device()()), NintendoOUI{0x00, 0x1F, 0x32, 0x00, 0x00, 0x00} {} 42 : random_gen(std::random_device()()), NintendoOUI{0x00, 0x1F, 0x32, 0x00, 0x00, 0x00} {}
@@ -147,7 +149,7 @@ void Room::RoomImpl::ServerLoop() {
147 case IdJoinRequest: 149 case IdJoinRequest:
148 HandleJoinRequest(&event); 150 HandleJoinRequest(&event);
149 break; 151 break;
150 case IdSetGameName: 152 case IdSetGameInfo:
151 HandleGameNamePacket(&event); 153 HandleGameNamePacket(&event);
152 break; 154 break;
153 case IdWifiPacket: 155 case IdWifiPacket:
@@ -213,7 +215,10 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
213 member.nickname = nickname; 215 member.nickname = nickname;
214 member.peer = event->peer; 216 member.peer = event->peer;
215 217
216 members.push_back(std::move(member)); 218 {
219 std::lock_guard<std::mutex> lock(member_mutex);
220 members.push_back(std::move(member));
221 }
217 222
218 // Notify everyone that the room information has changed. 223 // Notify everyone that the room information has changed.
219 BroadcastRoomInformation(); 224 BroadcastRoomInformation();
@@ -223,12 +228,14 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
223bool Room::RoomImpl::IsValidNickname(const std::string& nickname) const { 228bool Room::RoomImpl::IsValidNickname(const std::string& nickname) const {
224 // A nickname is valid if it is not already taken by anybody else in the room. 229 // A nickname is valid if it is not already taken by anybody else in the room.
225 // TODO(B3N30): Check for empty names, spaces, etc. 230 // TODO(B3N30): Check for empty names, spaces, etc.
231 std::lock_guard<std::mutex> lock(member_mutex);
226 return std::all_of(members.begin(), members.end(), 232 return std::all_of(members.begin(), members.end(),
227 [&nickname](const auto& member) { return member.nickname != nickname; }); 233 [&nickname](const auto& member) { return member.nickname != nickname; });
228} 234}
229 235
230bool Room::RoomImpl::IsValidMacAddress(const MacAddress& address) const { 236bool Room::RoomImpl::IsValidMacAddress(const MacAddress& address) const {
231 // A MAC address is valid if it is not already taken by anybody else in the room. 237 // A MAC address is valid if it is not already taken by anybody else in the room.
238 std::lock_guard<std::mutex> lock(member_mutex);
232 return std::all_of(members.begin(), members.end(), 239 return std::all_of(members.begin(), members.end(),
233 [&address](const auto& member) { return member.mac_address != address; }); 240 [&address](const auto& member) { return member.mac_address != address; });
234} 241}
@@ -279,6 +286,7 @@ void Room::RoomImpl::SendCloseMessage() {
279 packet << static_cast<u8>(IdCloseRoom); 286 packet << static_cast<u8>(IdCloseRoom);
280 ENetPacket* enet_packet = 287 ENetPacket* enet_packet =
281 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); 288 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
289 std::lock_guard<std::mutex> lock(member_mutex);
282 for (auto& member : members) { 290 for (auto& member : members) {
283 enet_peer_send(member.peer, 0, enet_packet); 291 enet_peer_send(member.peer, 0, enet_packet);
284 } 292 }
@@ -295,10 +303,14 @@ void Room::RoomImpl::BroadcastRoomInformation() {
295 packet << room_information.member_slots; 303 packet << room_information.member_slots;
296 304
297 packet << static_cast<u32>(members.size()); 305 packet << static_cast<u32>(members.size());
298 for (const auto& member : members) { 306 {
299 packet << member.nickname; 307 std::lock_guard<std::mutex> lock(member_mutex);
300 packet << member.mac_address; 308 for (const auto& member : members) {
301 packet << member.game_name; 309 packet << member.nickname;
310 packet << member.mac_address;
311 packet << member.game_info.name;
312 packet << member.game_info.id;
313 }
302 } 314 }
303 315
304 ENetPacket* enet_packet = 316 ENetPacket* enet_packet =
@@ -335,11 +347,13 @@ void Room::RoomImpl::HandleWifiPacket(const ENetEvent* event) {
335 ENET_PACKET_FLAG_RELIABLE); 347 ENET_PACKET_FLAG_RELIABLE);
336 348
337 if (destination_address == BroadcastMac) { // Send the data to everyone except the sender 349 if (destination_address == BroadcastMac) { // Send the data to everyone except the sender
350 std::lock_guard<std::mutex> lock(member_mutex);
338 for (const auto& member : members) { 351 for (const auto& member : members) {
339 if (member.peer != event->peer) 352 if (member.peer != event->peer)
340 enet_peer_send(member.peer, 0, enet_packet); 353 enet_peer_send(member.peer, 0, enet_packet);
341 } 354 }
342 } else { // Send the data only to the destination client 355 } else { // Send the data only to the destination client
356 std::lock_guard<std::mutex> lock(member_mutex);
343 auto member = std::find_if(members.begin(), members.end(), 357 auto member = std::find_if(members.begin(), members.end(),
344 [destination_address](const Member& member) -> bool { 358 [destination_address](const Member& member) -> bool {
345 return member.mac_address == destination_address; 359 return member.mac_address == destination_address;
@@ -361,6 +375,8 @@ void Room::RoomImpl::HandleChatPacket(const ENetEvent* event) {
361 auto CompareNetworkAddress = [event](const Member member) -> bool { 375 auto CompareNetworkAddress = [event](const Member member) -> bool {
362 return member.peer == event->peer; 376 return member.peer == event->peer;
363 }; 377 };
378
379 std::lock_guard<std::mutex> lock(member_mutex);
364 const auto sending_member = std::find_if(members.begin(), members.end(), CompareNetworkAddress); 380 const auto sending_member = std::find_if(members.begin(), members.end(), CompareNetworkAddress);
365 if (sending_member == members.end()) { 381 if (sending_member == members.end()) {
366 return; // Received a chat message from a unknown sender 382 return; // Received a chat message from a unknown sender
@@ -385,22 +401,32 @@ void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) {
385 in_packet.Append(event->packet->data, event->packet->dataLength); 401 in_packet.Append(event->packet->data, event->packet->dataLength);
386 402
387 in_packet.IgnoreBytes(sizeof(u8)); // Igonore the message type 403 in_packet.IgnoreBytes(sizeof(u8)); // Igonore the message type
388 std::string game_name; 404 GameInfo game_info;
389 in_packet >> game_name; 405 in_packet >> game_info.name;
390 auto member = 406 in_packet >> game_info.id;
391 std::find_if(members.begin(), members.end(), 407
392 [event](const Member& member) -> bool { return member.peer == event->peer; }); 408 {
393 if (member != members.end()) { 409 std::lock_guard<std::mutex> lock(member_mutex);
394 member->game_name = game_name; 410 auto member =
395 BroadcastRoomInformation(); 411 std::find_if(members.begin(), members.end(), [event](const Member& member) -> bool {
412 return member.peer == event->peer;
413 });
414 if (member != members.end()) {
415 member->game_info = game_info;
416 }
396 } 417 }
418 BroadcastRoomInformation();
397} 419}
398 420
399void Room::RoomImpl::HandleClientDisconnection(ENetPeer* client) { 421void Room::RoomImpl::HandleClientDisconnection(ENetPeer* client) {
400 // Remove the client from the members list. 422 // Remove the client from the members list.
401 members.erase(std::remove_if(members.begin(), members.end(), 423 {
402 [client](const Member& member) { return member.peer == client; }), 424 std::lock_guard<std::mutex> lock(member_mutex);
403 members.end()); 425 members.erase(
426 std::remove_if(members.begin(), members.end(),
427 [client](const Member& member) { return member.peer == client; }),
428 members.end());
429 }
404 430
405 // Announce the change to all clients. 431 // Announce the change to all clients.
406 enet_peer_disconnect(client, 0); 432 enet_peer_disconnect(client, 0);
@@ -437,6 +463,19 @@ const RoomInformation& Room::GetRoomInformation() const {
437 return room_impl->room_information; 463 return room_impl->room_information;
438} 464}
439 465
466std::vector<Room::Member> Room::GetRoomMemberList() const {
467 std::vector<Room::Member> member_list;
468 std::lock_guard<std::mutex> lock(room_impl->member_mutex);
469 for (const auto& member_impl : room_impl->members) {
470 Member member;
471 member.nickname = member_impl.nickname;
472 member.mac_address = member_impl.mac_address;
473 member.game_info = member_impl.game_info;
474 member_list.push_back(member);
475 }
476 return member_list;
477};
478
440void Room::Destroy() { 479void Room::Destroy() {
441 room_impl->state = State::Closed; 480 room_impl->state = State::Closed;
442 room_impl->room_thread->join(); 481 room_impl->room_thread->join();
@@ -447,7 +486,10 @@ void Room::Destroy() {
447 } 486 }
448 room_impl->room_information = {}; 487 room_impl->room_information = {};
449 room_impl->server = nullptr; 488 room_impl->server = nullptr;
450 room_impl->members.clear(); 489 {
490 std::lock_guard<std::mutex> lock(room_impl->member_mutex);
491 room_impl->members.clear();
492 }
451 room_impl->room_information.member_slots = 0; 493 room_impl->room_information.member_slots = 0;
452 room_impl->room_information.name.clear(); 494 room_impl->room_information.name.clear();
453} 495}
diff --git a/src/network/room.h b/src/network/room.h
index 65b0d008a..8285a4d0c 100644
--- a/src/network/room.h
+++ b/src/network/room.h
@@ -7,6 +7,7 @@
7#include <array> 7#include <array>
8#include <memory> 8#include <memory>
9#include <string> 9#include <string>
10#include <vector>
10#include "common/common_types.h" 11#include "common/common_types.h"
11 12
12namespace Network { 13namespace Network {
@@ -21,6 +22,11 @@ struct RoomInformation {
21 u32 member_slots; ///< Maximum number of members in this room 22 u32 member_slots; ///< Maximum number of members in this room
22}; 23};
23 24
25struct GameInfo {
26 std::string name{""};
27 u64 id{0};
28};
29
24using MacAddress = std::array<u8, 6>; 30using MacAddress = std::array<u8, 6>;
25/// A special MAC address that tells the room we're joining to assign us a MAC address 31/// A special MAC address that tells the room we're joining to assign us a MAC address
26/// automatically. 32/// automatically.
@@ -34,7 +40,7 @@ enum RoomMessageTypes : u8 {
34 IdJoinRequest = 1, 40 IdJoinRequest = 1,
35 IdJoinSuccess, 41 IdJoinSuccess,
36 IdRoomInformation, 42 IdRoomInformation,
37 IdSetGameName, 43 IdSetGameInfo,
38 IdWifiPacket, 44 IdWifiPacket,
39 IdChatMessage, 45 IdChatMessage,
40 IdNameCollision, 46 IdNameCollision,
@@ -51,6 +57,12 @@ public:
51 Closed, ///< The room is not opened and can not accept connections. 57 Closed, ///< The room is not opened and can not accept connections.
52 }; 58 };
53 59
60 struct Member {
61 std::string nickname; ///< The nickname of the member.
62 GameInfo game_info; ///< The current game of the member
63 MacAddress mac_address; ///< The assigned mac address of the member.
64 };
65
54 Room(); 66 Room();
55 ~Room(); 67 ~Room();
56 68
@@ -65,6 +77,11 @@ public:
65 const RoomInformation& GetRoomInformation() const; 77 const RoomInformation& GetRoomInformation() const;
66 78
67 /** 79 /**
80 * Gets a list of the mbmers connected to the room.
81 */
82 std::vector<Member> GetRoomMemberList() const;
83
84 /**
68 * Creates the socket for this room. Will bind to default address if 85 * Creates the socket for this room. Will bind to default address if
69 * server is empty string. 86 * server is empty string.
70 */ 87 */
diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp
index dac9bacae..f229ec6fd 100644
--- a/src/network/room_member.cpp
+++ b/src/network/room_member.cpp
@@ -5,6 +5,7 @@
5#include <atomic> 5#include <atomic>
6#include <list> 6#include <list>
7#include <mutex> 7#include <mutex>
8#include <set>
8#include <thread> 9#include <thread>
9#include "common/assert.h" 10#include "common/assert.h"
10#include "enet/enet.h" 11#include "enet/enet.h"
@@ -25,6 +26,9 @@ public:
25 /// Information about the room we're connected to. 26 /// Information about the room we're connected to.
26 RoomInformation room_information; 27 RoomInformation room_information;
27 28
29 /// The current game name, id and version
30 GameInfo current_game_info;
31
28 std::atomic<State> state{State::Idle}; ///< Current state of the RoomMember. 32 std::atomic<State> state{State::Idle}; ///< Current state of the RoomMember.
29 void SetState(const State new_state); 33 void SetState(const State new_state);
30 bool IsConnected() const; 34 bool IsConnected() const;
@@ -37,6 +41,24 @@ public:
37 std::unique_ptr<std::thread> loop_thread; 41 std::unique_ptr<std::thread> loop_thread;
38 std::mutex send_list_mutex; ///< Mutex that controls access to the `send_list` variable. 42 std::mutex send_list_mutex; ///< Mutex that controls access to the `send_list` variable.
39 std::list<Packet> send_list; ///< A list that stores all packets to send the async 43 std::list<Packet> send_list; ///< A list that stores all packets to send the async
44
45 template <typename T>
46 using CallbackSet = std::set<CallbackHandle<T>>;
47 std::mutex callback_mutex; ///< The mutex used for handling callbacks
48
49 class Callbacks {
50 public:
51 template <typename T>
52 CallbackSet<T>& Get();
53
54 private:
55 CallbackSet<WifiPacket> callback_set_wifi_packet;
56 CallbackSet<ChatEntry> callback_set_chat_messages;
57 CallbackSet<RoomInformation> callback_set_room_information;
58 CallbackSet<State> callback_set_state;
59 };
60 Callbacks callbacks; ///< All CallbackSets to all events
61
40 void MemberLoop(); 62 void MemberLoop();
41 63
42 void StartLoop(); 64 void StartLoop();
@@ -84,12 +106,20 @@ public:
84 * Disconnects the RoomMember from the Room 106 * Disconnects the RoomMember from the Room
85 */ 107 */
86 void Disconnect(); 108 void Disconnect();
109
110 template <typename T>
111 void Invoke(const T& data);
112
113 template <typename T>
114 CallbackHandle<T> Bind(std::function<void(const T&)> callback);
87}; 115};
88 116
89// RoomMemberImpl 117// RoomMemberImpl
90void RoomMember::RoomMemberImpl::SetState(const State new_state) { 118void RoomMember::RoomMemberImpl::SetState(const State new_state) {
91 state = new_state; 119 if (state != new_state) {
92 // TODO(B3N30): Invoke the callback functions 120 state = new_state;
121 Invoke<State>(state);
122 }
93} 123}
94 124
95bool RoomMember::RoomMemberImpl::IsConnected() const { 125bool RoomMember::RoomMemberImpl::IsConnected() const {
@@ -195,9 +225,10 @@ void RoomMember::RoomMemberImpl::HandleRoomInformationPacket(const ENetEvent* ev
195 for (auto& member : member_information) { 225 for (auto& member : member_information) {
196 packet >> member.nickname; 226 packet >> member.nickname;
197 packet >> member.mac_address; 227 packet >> member.mac_address;
198 packet >> member.game_name; 228 packet >> member.game_info.name;
229 packet >> member.game_info.id;
199 } 230 }
200 // TODO(B3N30): Invoke callbacks 231 Invoke(room_information);
201} 232}
202 233
203void RoomMember::RoomMemberImpl::HandleJoinPacket(const ENetEvent* event) { 234void RoomMember::RoomMemberImpl::HandleJoinPacket(const ENetEvent* event) {
@@ -209,7 +240,7 @@ void RoomMember::RoomMemberImpl::HandleJoinPacket(const ENetEvent* event) {
209 240
210 // Parse the MAC Address from the packet 241 // Parse the MAC Address from the packet
211 packet >> mac_address; 242 packet >> mac_address;
212 // TODO(B3N30): Invoke callbacks 243 SetState(State::Joined);
213} 244}
214 245
215void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) { 246void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) {
@@ -235,7 +266,7 @@ void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) {
235 266
236 packet >> wifi_packet.data; 267 packet >> wifi_packet.data;
237 268
238 // TODO(B3N30): Invoke callbacks 269 Invoke<WifiPacket>(wifi_packet);
239} 270}
240 271
241void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) { 272void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) {
@@ -248,7 +279,7 @@ void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) {
248 ChatEntry chat_entry{}; 279 ChatEntry chat_entry{};
249 packet >> chat_entry.nickname; 280 packet >> chat_entry.nickname;
250 packet >> chat_entry.message; 281 packet >> chat_entry.message;
251 // TODO(B3N30): Invoke callbacks 282 Invoke<ChatEntry>(chat_entry);
252} 283}
253 284
254void RoomMember::RoomMemberImpl::Disconnect() { 285void RoomMember::RoomMemberImpl::Disconnect() {
@@ -276,6 +307,46 @@ void RoomMember::RoomMemberImpl::Disconnect() {
276 server = nullptr; 307 server = nullptr;
277} 308}
278 309
310template <>
311RoomMember::RoomMemberImpl::CallbackSet<WifiPacket>& RoomMember::RoomMemberImpl::Callbacks::Get() {
312 return callback_set_wifi_packet;
313}
314
315template <>
316RoomMember::RoomMemberImpl::CallbackSet<RoomMember::State>&
317RoomMember::RoomMemberImpl::Callbacks::Get() {
318 return callback_set_state;
319}
320
321template <>
322RoomMember::RoomMemberImpl::CallbackSet<RoomInformation>&
323RoomMember::RoomMemberImpl::Callbacks::Get() {
324 return callback_set_room_information;
325}
326
327template <>
328RoomMember::RoomMemberImpl::CallbackSet<ChatEntry>& RoomMember::RoomMemberImpl::Callbacks::Get() {
329 return callback_set_chat_messages;
330}
331
332template <typename T>
333void RoomMember::RoomMemberImpl::Invoke(const T& data) {
334 std::lock_guard<std::mutex> lock(callback_mutex);
335 CallbackSet<T> callback_set = callbacks.Get<T>();
336 for (auto const& callback : callback_set)
337 (*callback)(data);
338}
339
340template <typename T>
341RoomMember::CallbackHandle<T> RoomMember::RoomMemberImpl::Bind(
342 std::function<void(const T&)> callback) {
343 std::lock_guard<std::mutex> lock(callback_mutex);
344 CallbackHandle<T> handle;
345 handle = std::make_shared<std::function<void(const T&)>>(callback);
346 callbacks.Get<T>().insert(handle);
347 return handle;
348}
349
279// RoomMember 350// RoomMember
280RoomMember::RoomMember() : room_member_impl{std::make_unique<RoomMemberImpl>()} { 351RoomMember::RoomMember() : room_member_impl{std::make_unique<RoomMemberImpl>()} {
281 room_member_impl->client = enet_host_create(nullptr, 1, NumChannels, 0, 0); 352 room_member_impl->client = enet_host_create(nullptr, 1, NumChannels, 0, 0);
@@ -339,6 +410,7 @@ void RoomMember::Join(const std::string& nick, const char* server_addr, u16 serv
339 room_member_impl->SetState(State::Joining); 410 room_member_impl->SetState(State::Joining);
340 room_member_impl->StartLoop(); 411 room_member_impl->StartLoop();
341 room_member_impl->SendJoinRequest(nick, preferred_mac); 412 room_member_impl->SendJoinRequest(nick, preferred_mac);
413 SendGameInfo(room_member_impl->current_game_info);
342 } else { 414 } else {
343 room_member_impl->SetState(State::CouldNotConnect); 415 room_member_impl->SetState(State::CouldNotConnect);
344 } 416 }
@@ -366,17 +438,53 @@ void RoomMember::SendChatMessage(const std::string& message) {
366 room_member_impl->Send(std::move(packet)); 438 room_member_impl->Send(std::move(packet));
367} 439}
368 440
369void RoomMember::SendGameName(const std::string& game_name) { 441void RoomMember::SendGameInfo(const GameInfo& game_info) {
442 room_member_impl->current_game_info = game_info;
443 if (!IsConnected())
444 return;
445
370 Packet packet; 446 Packet packet;
371 packet << static_cast<u8>(IdSetGameName); 447 packet << static_cast<u8>(IdSetGameInfo);
372 packet << game_name; 448 packet << game_info.name;
449 packet << game_info.id;
373 room_member_impl->Send(std::move(packet)); 450 room_member_impl->Send(std::move(packet));
374} 451}
375 452
453RoomMember::CallbackHandle<RoomMember::State> RoomMember::BindOnStateChanged(
454 std::function<void(const RoomMember::State&)> callback) {
455 return room_member_impl->Bind(callback);
456}
457
458RoomMember::CallbackHandle<WifiPacket> RoomMember::BindOnWifiPacketReceived(
459 std::function<void(const WifiPacket&)> callback) {
460 return room_member_impl->Bind(callback);
461}
462
463RoomMember::CallbackHandle<RoomInformation> RoomMember::BindOnRoomInformationChanged(
464 std::function<void(const RoomInformation&)> callback) {
465 return room_member_impl->Bind(callback);
466}
467
468RoomMember::CallbackHandle<ChatEntry> RoomMember::BindOnChatMessageRecieved(
469 std::function<void(const ChatEntry&)> callback) {
470 return room_member_impl->Bind(callback);
471}
472
473template <typename T>
474void RoomMember::Unbind(CallbackHandle<T> handle) {
475 std::lock_guard<std::mutex> lock(room_member_impl->callback_mutex);
476 room_member_impl->callbacks.Get<T>().erase(handle);
477}
478
376void RoomMember::Leave() { 479void RoomMember::Leave() {
377 room_member_impl->SetState(State::Idle); 480 room_member_impl->SetState(State::Idle);
378 room_member_impl->loop_thread->join(); 481 room_member_impl->loop_thread->join();
379 room_member_impl->loop_thread.reset(); 482 room_member_impl->loop_thread.reset();
380} 483}
381 484
485template void RoomMember::Unbind(CallbackHandle<WifiPacket>);
486template void RoomMember::Unbind(CallbackHandle<RoomMember::State>);
487template void RoomMember::Unbind(CallbackHandle<RoomInformation>);
488template void RoomMember::Unbind(CallbackHandle<ChatEntry>);
489
382} // namespace Network 490} // namespace Network
diff --git a/src/network/room_member.h b/src/network/room_member.h
index bc1af3a7e..98770a234 100644
--- a/src/network/room_member.h
+++ b/src/network/room_member.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <functional>
7#include <memory> 8#include <memory>
8#include <string> 9#include <string>
9#include <vector> 10#include <vector>
@@ -53,12 +54,23 @@ public:
53 54
54 struct MemberInformation { 55 struct MemberInformation {
55 std::string nickname; ///< Nickname of the member. 56 std::string nickname; ///< Nickname of the member.
56 std::string game_name; ///< Name of the game they're currently playing, or empty if they're 57 GameInfo game_info; ///< Name of the game they're currently playing, or empty if they're
57 /// not playing anything. 58 /// not playing anything.
58 MacAddress mac_address; ///< MAC address associated with this member. 59 MacAddress mac_address; ///< MAC address associated with this member.
59 }; 60 };
60 using MemberList = std::vector<MemberInformation>; 61 using MemberList = std::vector<MemberInformation>;
61 62
63 // The handle for the callback functions
64 template <typename T>
65 using CallbackHandle = std::shared_ptr<std::function<void(const T&)>>;
66
67 /**
68 * Unbinds a callback function from the events.
69 * @param handle The connection handle to disconnect
70 */
71 template <typename T>
72 void Unbind(CallbackHandle<T> handle);
73
62 RoomMember(); 74 RoomMember();
63 ~RoomMember(); 75 ~RoomMember();
64 76
@@ -113,10 +125,49 @@ public:
113 void SendChatMessage(const std::string& message); 125 void SendChatMessage(const std::string& message);
114 126
115 /** 127 /**
116 * Sends the current game name to the room. 128 * Sends the current game info to the room.
117 * @param game_name The game name. 129 * @param game_info The game information.
130 */
131 void SendGameInfo(const GameInfo& game_info);
132
133 /**
134 * Binds a function to an event that will be triggered every time the State of the member
135 * changed. The function wil be called every time the event is triggered. The callback function
136 * must not bind or unbind a function. Doing so will cause a deadlock
137 * @param callback The function to call
138 * @return A handle used for removing the function from the registered list
139 */
140 CallbackHandle<State> BindOnStateChanged(std::function<void(const State&)> callback);
141
142 /**
143 * Binds a function to an event that will be triggered every time a WifiPacket is received.
144 * The function wil be called everytime the event is triggered.
145 * The callback function must not bind or unbind a function. Doing so will cause a deadlock
146 * @param callback The function to call
147 * @return A handle used for removing the function from the registered list
148 */
149 CallbackHandle<WifiPacket> BindOnWifiPacketReceived(
150 std::function<void(const WifiPacket&)> callback);
151
152 /**
153 * Binds a function to an event that will be triggered every time the RoomInformation changes.
154 * The function wil be called every time the event is triggered.
155 * The callback function must not bind or unbind a function. Doing so will cause a deadlock
156 * @param callback The function to call
157 * @return A handle used for removing the function from the registered list
158 */
159 CallbackHandle<RoomInformation> BindOnRoomInformationChanged(
160 std::function<void(const RoomInformation&)> callback);
161
162 /**
163 * Binds a function to an event that will be triggered every time a ChatMessage is received.
164 * The function wil be called every time the event is triggered.
165 * The callback function must not bind or unbind a function. Doing so will cause a deadlock
166 * @param callback The function to call
167 * @return A handle used for removing the function from the registered list
118 */ 168 */
119 void SendGameName(const std::string& game_name); 169 CallbackHandle<ChatEntry> BindOnChatMessageRecieved(
170 std::function<void(const ChatEntry&)> callback);
120 171
121 /** 172 /**
122 * Leaves the current room. 173 * Leaves the current room.
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index bb192affd..ae67aab05 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -525,11 +525,12 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
525 "float geo_factor = 1.0;\n"; 525 "float geo_factor = 1.0;\n";
526 526
527 // Compute fragment normals and tangents 527 // Compute fragment normals and tangents
528 const std::string pertubation = 528 auto Perturbation = [&]() {
529 "2.0 * (" + SampleTexture(config, lighting.bump_selector) + ").rgb - 1.0"; 529 return "2.0 * (" + SampleTexture(config, lighting.bump_selector) + ").rgb - 1.0";
530 };
530 if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) { 531 if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) {
531 // Bump mapping is enabled using a normal map 532 // Bump mapping is enabled using a normal map
532 out += "vec3 surface_normal = " + pertubation + ";\n"; 533 out += "vec3 surface_normal = " + Perturbation() + ";\n";
533 534
534 // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher 535 // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher
535 // precision result 536 // precision result
@@ -543,7 +544,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
543 out += "vec3 surface_tangent = vec3(1.0, 0.0, 0.0);\n"; 544 out += "vec3 surface_tangent = vec3(1.0, 0.0, 0.0);\n";
544 } else if (lighting.bump_mode == LightingRegs::LightingBumpMode::TangentMap) { 545 } else if (lighting.bump_mode == LightingRegs::LightingBumpMode::TangentMap) {
545 // Bump mapping is enabled using a tangent map 546 // Bump mapping is enabled using a tangent map
546 out += "vec3 surface_tangent = " + pertubation + ";\n"; 547 out += "vec3 surface_tangent = " + Perturbation() + ";\n";
547 // Mathematically, recomputing Z-component of the tangent vector won't affect the relevant 548 // Mathematically, recomputing Z-component of the tangent vector won't affect the relevant
548 // computation below, which is also confirmed on 3DS. So we don't bother recomputing here 549 // computation below, which is also confirmed on 3DS. So we don't bother recomputing here
549 // even if 'renorm' is enabled. 550 // even if 'renorm' is enabled.
diff --git a/src/video_core/swrasterizer/lighting.cpp b/src/video_core/swrasterizer/lighting.cpp
index 91683afa4..39a3e396d 100644
--- a/src/video_core/swrasterizer/lighting.cpp
+++ b/src/video_core/swrasterizer/lighting.cpp
@@ -96,6 +96,12 @@ std::tuple<Math::Vec4<u8>, Math::Vec4<u8>> ComputeFragmentsColors(
96 result = Math::Dot(light_vector, normal); 96 result = Math::Dot(light_vector, normal);
97 break; 97 break;
98 98
99 case LightingRegs::LightingLutInput::SP: {
100 Math::Vec3<s32> spot_dir{light_config.spot_x.Value(), light_config.spot_y.Value(),
101 light_config.spot_z.Value()};
102 result = Math::Dot(light_vector, spot_dir.Cast<float>() / 2047.0f);
103 break;
104 }
99 default: 105 default:
100 LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %u\n", static_cast<u32>(input)); 106 LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %u\n", static_cast<u32>(input));
101 UNIMPLEMENTED(); 107 UNIMPLEMENTED();
@@ -126,6 +132,16 @@ std::tuple<Math::Vec4<u8>, Math::Vec4<u8>> ComputeFragmentsColors(
126 LookupLightingLut(lighting_state, static_cast<size_t>(sampler), index, delta); 132 LookupLightingLut(lighting_state, static_cast<size_t>(sampler), index, delta);
127 }; 133 };
128 134
135 // If enabled, compute spot light attenuation value
136 float spot_atten = 1.0f;
137 if (!lighting.IsSpotAttenDisabled(num) &&
138 LightingRegs::IsLightingSamplerSupported(
139 lighting.config0.config, LightingRegs::LightingSampler::SpotlightAttenuation)) {
140 auto lut = LightingRegs::SpotlightAttenuationSampler(num);
141 spot_atten = GetLutValue(lighting.lut_input.sp, lighting.abs_lut_input.disable_sp == 0,
142 lighting.lut_scale.sp, lut);
143 }
144
129 // Specular 0 component 145 // Specular 0 component
130 float d0_lut_value = 1.0f; 146 float d0_lut_value = 1.0f;
131 if (lighting.config1.disable_lut_d0 == 0 && 147 if (lighting.config1.disable_lut_d0 == 0 &&
@@ -238,10 +254,10 @@ std::tuple<Math::Vec4<u8>, Math::Vec4<u8>> ComputeFragmentsColors(
238 254
239 auto diffuse = 255 auto diffuse =
240 light_config.diffuse.ToVec3f() * dot_product + light_config.ambient.ToVec3f(); 256 light_config.diffuse.ToVec3f() * dot_product + light_config.ambient.ToVec3f();
241 diffuse_sum += Math::MakeVec(diffuse * dist_atten, 0.0f); 257 diffuse_sum += Math::MakeVec(diffuse * dist_atten * spot_atten, 0.0f);
242 258
243 specular_sum += 259 specular_sum += Math::MakeVec(
244 Math::MakeVec((specular_0 + specular_1) * clamp_highlights * dist_atten, 0.0f); 260 (specular_0 + specular_1) * clamp_highlights * dist_atten * spot_atten, 0.0f);
245 } 261 }
246 262
247 diffuse_sum += Math::MakeVec(lighting.global_ambient.ToVec3f(), 0.0f); 263 diffuse_sum += Math::MakeVec(lighting.global_ambient.ToVec3f(), 0.0f);