summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/command_generator.cpp23
-rw-r--r--src/core/file_sys/control_metadata.h2
-rw-r--r--src/core/frontend/input.h22
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp106
-rw-r--r--src/core/hle/service/hid/controllers/npad.h54
-rw-r--r--src/core/hle/service/hid/hid.cpp37
-rw-r--r--src/core/hle/service/hid/hid.h2
-rw-r--r--src/core/hle/service/nfp/nfp.cpp23
-rw-r--r--src/core/hle/service/service.cpp22
-rw-r--r--src/core/settings.h1
-rw-r--r--src/input_common/main.cpp46
-rw-r--r--src/input_common/main.h24
-rw-r--r--src/input_common/motion_emu.cpp17
-rw-r--r--src/input_common/settings.cpp7
-rw-r--r--src/input_common/settings.h17
-rw-r--r--src/input_common/udp/client.cpp176
-rw-r--r--src/input_common/udp/client.h77
-rw-r--r--src/input_common/udp/udp.cpp181
-rw-r--r--src/input_common/udp/udp.h61
-rw-r--r--src/video_core/shader/decode/image.cpp1
-rw-r--r--src/video_core/shader/decode/texture.cpp2
-rw-r--r--src/yuzu/configuration/config.cpp31
-rw-r--r--src/yuzu/configuration/config.h1
-rw-r--r--src/yuzu/configuration/configure_input.cpp7
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp117
-rw-r--r--src/yuzu/configuration/configure_input_player.h6
-rw-r--r--src/yuzu/configuration/configure_input_player.ui109
-rw-r--r--src/yuzu_cmd/config.cpp2
-rw-r--r--src/yuzu_tester/config.cpp1
29 files changed, 971 insertions, 204 deletions
diff --git a/src/audio_core/command_generator.cpp b/src/audio_core/command_generator.cpp
index 84782cde6..8f7da49e6 100644
--- a/src/audio_core/command_generator.cpp
+++ b/src/audio_core/command_generator.cpp
@@ -196,7 +196,7 @@ void CommandGenerator::PreCommand() {
196 for (std::size_t i = 0; i < splitter_context.GetInfoCount(); i++) { 196 for (std::size_t i = 0; i < splitter_context.GetInfoCount(); i++) {
197 const auto& base = splitter_context.GetInfo(i); 197 const auto& base = splitter_context.GetInfo(i);
198 std::string graph = fmt::format("b[{}]", i); 198 std::string graph = fmt::format("b[{}]", i);
199 auto* head = base.GetHead(); 199 const auto* head = base.GetHead();
200 while (head != nullptr) { 200 while (head != nullptr) {
201 graph += fmt::format("->{}", head->GetMixId()); 201 graph += fmt::format("->{}", head->GetMixId());
202 head = head->GetNextDestination(); 202 head = head->GetNextDestination();
@@ -214,7 +214,7 @@ void CommandGenerator::PostCommand() {
214 214
215void CommandGenerator::GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state, 215void CommandGenerator::GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
216 s32 channel) { 216 s32 channel) {
217 auto& in_params = voice_info.GetInParams(); 217 const auto& in_params = voice_info.GetInParams();
218 const auto depop = in_params.should_depop; 218 const auto depop = in_params.should_depop;
219 219
220 if (depop) { 220 if (depop) {
@@ -405,7 +405,7 @@ void CommandGenerator::GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset,
405} 405}
406 406
407void CommandGenerator::GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled) { 407void CommandGenerator::GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled) {
408 auto aux = dynamic_cast<EffectAuxInfo*>(info); 408 auto* aux = dynamic_cast<EffectAuxInfo*>(info);
409 const auto& params = aux->GetParams(); 409 const auto& params = aux->GetParams();
410 if (aux->GetSendBuffer() != 0 && aux->GetRecvBuffer() != 0) { 410 if (aux->GetSendBuffer() != 0 && aux->GetRecvBuffer() != 0) {
411 const auto max_channels = params.count; 411 const auto max_channels = params.count;
@@ -571,7 +571,7 @@ void CommandGenerator::GenerateSubMixCommand(ServerMixInfo& mix_info) {
571 if (dumping_frame) { 571 if (dumping_frame) {
572 LOG_DEBUG(Audio, "(DSP_TRACE) GenerateSubMixCommand"); 572 LOG_DEBUG(Audio, "(DSP_TRACE) GenerateSubMixCommand");
573 } 573 }
574 auto& in_params = mix_info.GetInParams(); 574 const auto& in_params = mix_info.GetInParams();
575 GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset, 575 GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset,
576 in_params.sample_rate); 576 in_params.sample_rate);
577 577
@@ -650,7 +650,7 @@ void CommandGenerator::GenerateFinalMixCommand() {
650 LOG_DEBUG(Audio, "(DSP_TRACE) GenerateFinalMixCommand"); 650 LOG_DEBUG(Audio, "(DSP_TRACE) GenerateFinalMixCommand");
651 } 651 }
652 auto& mix_info = mix_context.GetFinalMixInfo(); 652 auto& mix_info = mix_context.GetFinalMixInfo();
653 const auto in_params = mix_info.GetInParams(); 653 const auto& in_params = mix_info.GetInParams();
654 654
655 GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset, 655 GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset,
656 in_params.sample_rate); 656 in_params.sample_rate);
@@ -674,7 +674,7 @@ void CommandGenerator::GenerateFinalMixCommand() {
674 674
675s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, 675s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
676 s32 sample_count, s32 channel, std::size_t mix_offset) { 676 s32 sample_count, s32 channel, std::size_t mix_offset) {
677 auto& in_params = voice_info.GetInParams(); 677 const auto& in_params = voice_info.GetInParams();
678 const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; 678 const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
679 if (wave_buffer.buffer_address == 0) { 679 if (wave_buffer.buffer_address == 0) {
680 return 0; 680 return 0;
@@ -714,7 +714,7 @@ s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_s
714 714
715s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, 715s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
716 s32 sample_count, s32 channel, std::size_t mix_offset) { 716 s32 sample_count, s32 channel, std::size_t mix_offset) {
717 auto& in_params = voice_info.GetInParams(); 717 const auto& in_params = voice_info.GetInParams();
718 const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; 718 const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
719 if (wave_buffer.buffer_address == 0) { 719 if (wave_buffer.buffer_address == 0) {
720 return 0; 720 return 0;
@@ -766,8 +766,8 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s
766 val = std::clamp<s32>(val, -32768, 32767); 766 val = std::clamp<s32>(val, -32768, 32767);
767 // Advance output feedback. 767 // Advance output feedback.
768 yn2 = yn1; 768 yn2 = yn1;
769 yn1 = val; 769 yn1 = static_cast<s16>(val);
770 return static_cast<s16>(val); 770 return yn1;
771 }; 771 };
772 772
773 std::size_t buffer_offset{}; 773 std::size_t buffer_offset{};
@@ -853,7 +853,7 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o
853 VoiceState& dsp_state, s32 channel, 853 VoiceState& dsp_state, s32 channel,
854 s32 target_sample_rate, s32 sample_count, 854 s32 target_sample_rate, s32 sample_count,
855 s32 node_id) { 855 s32 node_id) {
856 auto& in_params = voice_info.GetInParams(); 856 const auto& in_params = voice_info.GetInParams();
857 if (dumping_frame) { 857 if (dumping_frame) {
858 LOG_DEBUG(Audio, 858 LOG_DEBUG(Audio,
859 "(DSP_TRACE) DecodeFromWaveBuffers, node_id={}, channel={}, " 859 "(DSP_TRACE) DecodeFromWaveBuffers, node_id={}, channel={}, "
@@ -867,7 +867,8 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o
867 static_cast<float>(in_params.sample_rate) / static_cast<float>(target_sample_rate) * 867 static_cast<float>(in_params.sample_rate) / static_cast<float>(target_sample_rate) *
868 static_cast<float>(static_cast<s32>(in_params.pitch * 32768.0f))); 868 static_cast<float>(static_cast<s32>(in_params.pitch * 32768.0f)));
869 auto* output_base = output; 869 auto* output_base = output;
870 if ((dsp_state.fraction + sample_count * resample_rate) > (SCALED_MIX_BUFFER_SIZE - 4ULL)) { 870 if (dsp_state.fraction + sample_count * resample_rate >
871 static_cast<s32>(SCALED_MIX_BUFFER_SIZE - 4ULL)) {
871 return; 872 return;
872 } 873 }
873 874
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index 9ab86e35b..403c4219a 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -83,7 +83,7 @@ enum class Language : u8 {
83 Italian = 7, 83 Italian = 7,
84 Dutch = 8, 84 Dutch = 8,
85 CanadianFrench = 9, 85 CanadianFrench = 9,
86 Portugese = 10, 86 Portuguese = 10,
87 Russian = 11, 87 Russian = 11,
88 Korean = 12, 88 Korean = 12,
89 Taiwanese = 13, 89 Taiwanese = 13,
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index 2b098b7c6..9da0d2829 100644
--- a/src/core/frontend/input.h
+++ b/src/core/frontend/input.h
@@ -119,11 +119,11 @@ using ButtonDevice = InputDevice<bool>;
119using AnalogDevice = InputDevice<std::tuple<float, float>>; 119using AnalogDevice = InputDevice<std::tuple<float, float>>;
120 120
121/** 121/**
122 * A motion device is an input device that returns a tuple of accelerometer state vector and 122 * A motion status is an object that returns a tuple of accelerometer state vector,
123 * gyroscope state vector. 123 * gyroscope state vector, rotation state vector and orientation state matrix.
124 * 124 *
125 * For both vectors: 125 * For both vectors:
126 * x+ is the same direction as LEFT on D-pad. 126 * x+ is the same direction as RIGHT on D-pad.
127 * y+ is normal to the touch screen, pointing outward. 127 * y+ is normal to the touch screen, pointing outward.
128 * z+ is the same direction as UP on D-pad. 128 * z+ is the same direction as UP on D-pad.
129 * 129 *
@@ -133,8 +133,22 @@ using AnalogDevice = InputDevice<std::tuple<float, float>>;
133 * For gyroscope state vector: 133 * For gyroscope state vector:
134 * Orientation is determined by right-hand rule. 134 * Orientation is determined by right-hand rule.
135 * Units: deg/sec 135 * Units: deg/sec
136 *
137 * For rotation state vector
138 * Units: rotations
139 *
140 * For orientation state matrix
141 * x vector
142 * y vector
143 * z vector
144 */
145using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common::Vec3<float>,
146 std::array<Common::Vec3f, 3>>;
147
148/**
149 * A motion device is an input device that returns a motion status object
136 */ 150 */
137using MotionDevice = InputDevice<std::tuple<Common::Vec3<float>, Common::Vec3<float>>>; 151using MotionDevice = InputDevice<MotionStatus>;
138 152
139/** 153/**
140 * A touch device is an input device that returns a tuple of two floats and a bool. The floats are 154 * A touch device is an input device that returns a tuple of two floats and a bool. The floats are
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 7818c098f..b65d59373 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -250,6 +250,9 @@ void Controller_NPad::OnLoadInputDevices() {
250 std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, 250 std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
251 players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END, 251 players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
252 sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>); 252 sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
253 std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
254 players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END,
255 motions[i].begin(), Input::CreateDevice<Input::MotionDevice>);
253 } 256 }
254} 257}
255 258
@@ -266,6 +269,7 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
266 auto& rstick_entry = npad_pad_states[controller_idx].r_stick; 269 auto& rstick_entry = npad_pad_states[controller_idx].r_stick;
267 const auto& button_state = buttons[controller_idx]; 270 const auto& button_state = buttons[controller_idx];
268 const auto& analog_state = sticks[controller_idx]; 271 const auto& analog_state = sticks[controller_idx];
272 const auto& motion_state = motions[controller_idx];
269 const auto [stick_l_x_f, stick_l_y_f] = 273 const auto [stick_l_x_f, stick_l_y_f] =
270 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); 274 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
271 const auto [stick_r_x_f, stick_r_y_f] = 275 const auto [stick_r_x_f, stick_r_y_f] =
@@ -360,6 +364,45 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
360 continue; 364 continue;
361 } 365 }
362 const u32 npad_index = static_cast<u32>(i); 366 const u32 npad_index = static_cast<u32>(i);
367
368 const std::array<SixAxisGeneric*, 6> controller_sixaxes{
369 &npad.sixaxis_full, &npad.sixaxis_handheld, &npad.sixaxis_dual_left,
370 &npad.sixaxis_dual_right, &npad.sixaxis_left, &npad.sixaxis_right,
371 };
372
373 for (auto* sixaxis_sensor : controller_sixaxes) {
374 sixaxis_sensor->common.entry_count = 16;
375 sixaxis_sensor->common.total_entry_count = 17;
376
377 const auto& last_entry =
378 sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index];
379
380 sixaxis_sensor->common.timestamp = core_timing.GetCPUTicks();
381 sixaxis_sensor->common.last_entry_index =
382 (sixaxis_sensor->common.last_entry_index + 1) % 17;
383
384 auto& cur_entry = sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index];
385
386 cur_entry.timestamp = last_entry.timestamp + 1;
387 cur_entry.timestamp2 = cur_entry.timestamp;
388 }
389
390 // Try to read sixaxis sensor states
391 std::array<MotionDevice, 2> motion_devices;
392
393 if (sixaxis_sensors_enabled && Settings::values.motion_enabled) {
394 sixaxis_at_rest = true;
395 for (std::size_t e = 0; e < motion_devices.size(); ++e) {
396 const auto& device = motions[i][e];
397 if (device) {
398 std::tie(motion_devices[e].accel, motion_devices[e].gyro,
399 motion_devices[e].rotation, motion_devices[e].orientation) =
400 device->GetStatus();
401 sixaxis_at_rest = sixaxis_at_rest && motion_devices[e].gyro.Length2() < 0.0001f;
402 }
403 }
404 }
405
363 RequestPadStateUpdate(npad_index); 406 RequestPadStateUpdate(npad_index);
364 auto& pad_state = npad_pad_states[npad_index]; 407 auto& pad_state = npad_pad_states[npad_index];
365 408
@@ -377,6 +420,18 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
377 420
378 libnx_entry.connection_status.raw = 0; 421 libnx_entry.connection_status.raw = 0;
379 libnx_entry.connection_status.IsConnected.Assign(1); 422 libnx_entry.connection_status.IsConnected.Assign(1);
423 auto& full_sixaxis_entry =
424 npad.sixaxis_full.sixaxis[npad.sixaxis_full.common.last_entry_index];
425 auto& handheld_sixaxis_entry =
426 npad.sixaxis_handheld.sixaxis[npad.sixaxis_handheld.common.last_entry_index];
427 auto& dual_left_sixaxis_entry =
428 npad.sixaxis_dual_left.sixaxis[npad.sixaxis_dual_left.common.last_entry_index];
429 auto& dual_right_sixaxis_entry =
430 npad.sixaxis_dual_right.sixaxis[npad.sixaxis_dual_right.common.last_entry_index];
431 auto& left_sixaxis_entry =
432 npad.sixaxis_left.sixaxis[npad.sixaxis_left.common.last_entry_index];
433 auto& right_sixaxis_entry =
434 npad.sixaxis_right.sixaxis[npad.sixaxis_right.common.last_entry_index];
380 435
381 switch (controller_type) { 436 switch (controller_type) {
382 case NPadControllerType::None: 437 case NPadControllerType::None:
@@ -391,6 +446,13 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
391 main_controller.pad.r_stick = pad_state.r_stick; 446 main_controller.pad.r_stick = pad_state.r_stick;
392 447
393 libnx_entry.connection_status.IsWired.Assign(1); 448 libnx_entry.connection_status.IsWired.Assign(1);
449
450 if (sixaxis_sensors_enabled && motions[i][0]) {
451 full_sixaxis_entry.accel = motion_devices[0].accel;
452 full_sixaxis_entry.gyro = motion_devices[0].gyro;
453 full_sixaxis_entry.rotation = motion_devices[0].rotation;
454 full_sixaxis_entry.orientation = motion_devices[0].orientation;
455 }
394 break; 456 break;
395 case NPadControllerType::Handheld: 457 case NPadControllerType::Handheld:
396 handheld_entry.connection_status.raw = 0; 458 handheld_entry.connection_status.raw = 0;
@@ -409,6 +471,13 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
409 libnx_entry.connection_status.IsRightJoyConnected.Assign(1); 471 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
410 libnx_entry.connection_status.IsLeftJoyWired.Assign(1); 472 libnx_entry.connection_status.IsLeftJoyWired.Assign(1);
411 libnx_entry.connection_status.IsRightJoyWired.Assign(1); 473 libnx_entry.connection_status.IsRightJoyWired.Assign(1);
474
475 if (sixaxis_sensors_enabled && motions[i][0]) {
476 handheld_sixaxis_entry.accel = motion_devices[0].accel;
477 handheld_sixaxis_entry.gyro = motion_devices[0].gyro;
478 handheld_sixaxis_entry.rotation = motion_devices[0].rotation;
479 handheld_sixaxis_entry.orientation = motion_devices[0].orientation;
480 }
412 break; 481 break;
413 case NPadControllerType::JoyDual: 482 case NPadControllerType::JoyDual:
414 dual_entry.connection_status.raw = 0; 483 dual_entry.connection_status.raw = 0;
@@ -421,6 +490,21 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
421 490
422 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1); 491 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
423 libnx_entry.connection_status.IsRightJoyConnected.Assign(1); 492 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
493
494 if (sixaxis_sensors_enabled && motions[i][0]) {
495 // Set motion for the left joycon
496 dual_left_sixaxis_entry.accel = motion_devices[0].accel;
497 dual_left_sixaxis_entry.gyro = motion_devices[0].gyro;
498 dual_left_sixaxis_entry.rotation = motion_devices[0].rotation;
499 dual_left_sixaxis_entry.orientation = motion_devices[0].orientation;
500 }
501 if (sixaxis_sensors_enabled && motions[i][1]) {
502 // Set motion for the right joycon
503 dual_right_sixaxis_entry.accel = motion_devices[1].accel;
504 dual_right_sixaxis_entry.gyro = motion_devices[1].gyro;
505 dual_right_sixaxis_entry.rotation = motion_devices[1].rotation;
506 dual_right_sixaxis_entry.orientation = motion_devices[1].orientation;
507 }
424 break; 508 break;
425 case NPadControllerType::JoyLeft: 509 case NPadControllerType::JoyLeft:
426 left_entry.connection_status.raw = 0; 510 left_entry.connection_status.raw = 0;
@@ -431,6 +515,13 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
431 left_entry.pad.r_stick = pad_state.r_stick; 515 left_entry.pad.r_stick = pad_state.r_stick;
432 516
433 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1); 517 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
518
519 if (sixaxis_sensors_enabled && motions[i][0]) {
520 left_sixaxis_entry.accel = motion_devices[0].accel;
521 left_sixaxis_entry.gyro = motion_devices[0].gyro;
522 left_sixaxis_entry.rotation = motion_devices[0].rotation;
523 left_sixaxis_entry.orientation = motion_devices[0].orientation;
524 }
434 break; 525 break;
435 case NPadControllerType::JoyRight: 526 case NPadControllerType::JoyRight:
436 right_entry.connection_status.raw = 0; 527 right_entry.connection_status.raw = 0;
@@ -441,6 +532,13 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
441 right_entry.pad.r_stick = pad_state.r_stick; 532 right_entry.pad.r_stick = pad_state.r_stick;
442 533
443 libnx_entry.connection_status.IsRightJoyConnected.Assign(1); 534 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
535
536 if (sixaxis_sensors_enabled && motions[i][1]) {
537 right_sixaxis_entry.accel = motion_devices[1].accel;
538 right_sixaxis_entry.gyro = motion_devices[1].gyro;
539 right_sixaxis_entry.rotation = motion_devices[1].rotation;
540 right_sixaxis_entry.orientation = motion_devices[1].orientation;
541 }
444 break; 542 break;
445 case NPadControllerType::Pokeball: 543 case NPadControllerType::Pokeball:
446 pokeball_entry.connection_status.raw = 0; 544 pokeball_entry.connection_status.raw = 0;
@@ -582,6 +680,14 @@ Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMo
582 return gyroscope_zero_drift_mode; 680 return gyroscope_zero_drift_mode;
583} 681}
584 682
683bool Controller_NPad::IsSixAxisSensorAtRest() const {
684 return sixaxis_at_rest;
685}
686
687void Controller_NPad::SetSixAxisEnabled(bool six_axis_status) {
688 sixaxis_sensors_enabled = six_axis_status;
689}
690
585void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) { 691void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) {
586 const auto npad_index_1 = NPadIdToIndex(npad_id_1); 692 const auto npad_index_1 = NPadIdToIndex(npad_id_1);
587 const auto npad_index_2 = NPadIdToIndex(npad_id_2); 693 const auto npad_index_2 = NPadIdToIndex(npad_id_2);
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index e9788da8d..78e7c320b 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -130,6 +130,8 @@ public:
130 130
131 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); 131 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode);
132 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; 132 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
133 bool IsSixAxisSensorAtRest() const;
134 void SetSixAxisEnabled(bool six_axis_status);
133 LedPattern GetLedPattern(u32 npad_id); 135 LedPattern GetLedPattern(u32 npad_id);
134 void SetVibrationEnabled(bool can_vibrate); 136 void SetVibrationEnabled(bool can_vibrate);
135 bool IsVibrationEnabled() const; 137 bool IsVibrationEnabled() const;
@@ -252,6 +254,24 @@ private:
252 }; 254 };
253 static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size"); 255 static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size");
254 256
257 struct SixAxisStates {
258 s64_le timestamp{};
259 INSERT_PADDING_WORDS(2);
260 s64_le timestamp2{};
261 Common::Vec3f accel{};
262 Common::Vec3f gyro{};
263 Common::Vec3f rotation{};
264 std::array<Common::Vec3f, 3> orientation{};
265 s64_le always_one{1};
266 };
267 static_assert(sizeof(SixAxisStates) == 0x68, "SixAxisStates is an invalid size");
268
269 struct SixAxisGeneric {
270 CommonHeader common{};
271 std::array<SixAxisStates, 17> sixaxis{};
272 };
273 static_assert(sizeof(SixAxisGeneric) == 0x708, "SixAxisGeneric is an invalid size");
274
255 enum class ColorReadError : u32_le { 275 enum class ColorReadError : u32_le {
256 ReadOk = 0, 276 ReadOk = 0,
257 ColorDoesntExist = 1, 277 ColorDoesntExist = 1,
@@ -281,6 +301,13 @@ private:
281 }; 301 };
282 }; 302 };
283 303
304 struct MotionDevice {
305 Common::Vec3f accel;
306 Common::Vec3f gyro;
307 Common::Vec3f rotation;
308 std::array<Common::Vec3f, 3> orientation;
309 };
310
284 struct NPadEntry { 311 struct NPadEntry {
285 NPadType joy_styles; 312 NPadType joy_styles;
286 NPadAssignments pad_assignment; 313 NPadAssignments pad_assignment;
@@ -300,9 +327,12 @@ private:
300 NPadGeneric pokeball_states; 327 NPadGeneric pokeball_states;
301 NPadGeneric libnx; // TODO(ogniK): Find out what this actually is, libnx seems to only be 328 NPadGeneric libnx; // TODO(ogniK): Find out what this actually is, libnx seems to only be
302 // relying on this for the time being 329 // relying on this for the time being
303 INSERT_PADDING_BYTES( 330 SixAxisGeneric sixaxis_full;
304 0x708 * 331 SixAxisGeneric sixaxis_handheld;
305 6); // TODO(ogniK): SixAxis states, require more information before implementation 332 SixAxisGeneric sixaxis_dual_left;
333 SixAxisGeneric sixaxis_dual_right;
334 SixAxisGeneric sixaxis_left;
335 SixAxisGeneric sixaxis_right;
306 NPadDevice device_type; 336 NPadDevice device_type;
307 NPadProperties properties; 337 NPadProperties properties;
308 INSERT_PADDING_WORDS(1); 338 INSERT_PADDING_WORDS(1);
@@ -325,14 +355,18 @@ private:
325 355
326 NPadType style{}; 356 NPadType style{};
327 std::array<NPadEntry, 10> shared_memory_entries{}; 357 std::array<NPadEntry, 10> shared_memory_entries{};
328 std::array< 358 using ButtonArray = std::array<
329 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>, 359 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>,
330 10> 360 10>;
331 buttons; 361 using StickArray = std::array<
332 std::array<
333 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>, 362 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
334 10> 363 10>;
335 sticks; 364 using MotionArray = std::array<
365 std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTION_HID>,
366 10>;
367 ButtonArray buttons;
368 StickArray sticks;
369 MotionArray motions;
336 std::vector<u32> supported_npad_id_types{}; 370 std::vector<u32> supported_npad_id_types{};
337 NpadHoldType hold_type{NpadHoldType::Vertical}; 371 NpadHoldType hold_type{NpadHoldType::Vertical};
338 // Each controller should have their own styleset changed event 372 // Each controller should have their own styleset changed event
@@ -341,6 +375,8 @@ private:
341 std::array<ControllerHolder, 10> connected_controllers{}; 375 std::array<ControllerHolder, 10> connected_controllers{};
342 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; 376 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
343 bool can_controllers_vibrate{true}; 377 bool can_controllers_vibrate{true};
378 bool sixaxis_sensors_enabled{true};
379 bool sixaxis_at_rest{true};
344 std::array<ControllerPad, 10> npad_pad_states{}; 380 std::array<ControllerPad, 10> npad_pad_states{};
345 bool is_in_lr_assignment_mode{false}; 381 bool is_in_lr_assignment_mode{false};
346 Core::System& system; 382 Core::System& system;
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index bd3c2f26b..302a25540 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -164,8 +164,8 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
164 {56, nullptr, "ActivateJoyXpad"}, 164 {56, nullptr, "ActivateJoyXpad"},
165 {58, nullptr, "GetJoyXpadLifoHandle"}, 165 {58, nullptr, "GetJoyXpadLifoHandle"},
166 {59, nullptr, "GetJoyXpadIds"}, 166 {59, nullptr, "GetJoyXpadIds"},
167 {60, nullptr, "ActivateSixAxisSensor"}, 167 {60, &Hid::ActivateSixAxisSensor, "ActivateSixAxisSensor"},
168 {61, nullptr, "DeactivateSixAxisSensor"}, 168 {61, &Hid::DeactivateSixAxisSensor, "DeactivateSixAxisSensor"},
169 {62, nullptr, "GetSixAxisSensorLifoHandle"}, 169 {62, nullptr, "GetSixAxisSensorLifoHandle"},
170 {63, nullptr, "ActivateJoySixAxisSensor"}, 170 {63, nullptr, "ActivateJoySixAxisSensor"},
171 {64, nullptr, "DeactivateJoySixAxisSensor"}, 171 {64, nullptr, "DeactivateJoySixAxisSensor"},
@@ -329,6 +329,31 @@ void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) {
329 rb.Push(0); 329 rb.Push(0);
330} 330}
331 331
332void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
333 IPC::RequestParser rp{ctx};
334 const auto handle{rp.Pop<u32>()};
335 const auto applet_resource_user_id{rp.Pop<u64>()};
336 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true);
337 LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle,
338 applet_resource_user_id);
339
340 IPC::ResponseBuilder rb{ctx, 2};
341 rb.Push(RESULT_SUCCESS);
342}
343
344void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
345 IPC::RequestParser rp{ctx};
346 const auto handle{rp.Pop<u32>()};
347 const auto applet_resource_user_id{rp.Pop<u64>()};
348 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false);
349
350 LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle,
351 applet_resource_user_id);
352
353 IPC::ResponseBuilder rb{ctx, 2};
354 rb.Push(RESULT_SUCCESS);
355}
356
332void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) { 357void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) {
333 IPC::RequestParser rp{ctx}; 358 IPC::RequestParser rp{ctx};
334 const auto applet_resource_user_id{rp.Pop<u64>()}; 359 const auto applet_resource_user_id{rp.Pop<u64>()};
@@ -484,13 +509,13 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
484 const auto handle{rp.Pop<u32>()}; 509 const auto handle{rp.Pop<u32>()};
485 const auto applet_resource_user_id{rp.Pop<u64>()}; 510 const auto applet_resource_user_id{rp.Pop<u64>()};
486 511
487 LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, 512 LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle,
488 applet_resource_user_id); 513 applet_resource_user_id);
489 514
490 IPC::ResponseBuilder rb{ctx, 3}; 515 IPC::ResponseBuilder rb{ctx, 3};
491 rb.Push(RESULT_SUCCESS); 516 rb.Push(RESULT_SUCCESS);
492 // TODO (Hexagon12): Properly implement reading gyroscope values from controllers. 517 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
493 rb.Push(true); 518 .IsSixAxisSensorAtRest());
494} 519}
495 520
496void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { 521void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index efb07547f..e04aaf1e9 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -86,6 +86,8 @@ private:
86 void CreateAppletResource(Kernel::HLERequestContext& ctx); 86 void CreateAppletResource(Kernel::HLERequestContext& ctx);
87 void ActivateXpad(Kernel::HLERequestContext& ctx); 87 void ActivateXpad(Kernel::HLERequestContext& ctx);
88 void GetXpadIDs(Kernel::HLERequestContext& ctx); 88 void GetXpadIDs(Kernel::HLERequestContext& ctx);
89 void ActivateSixAxisSensor(Kernel::HLERequestContext& ctx);
90 void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx);
89 void ActivateDebugPad(Kernel::HLERequestContext& ctx); 91 void ActivateDebugPad(Kernel::HLERequestContext& ctx);
90 void ActivateTouchScreen(Kernel::HLERequestContext& ctx); 92 void ActivateTouchScreen(Kernel::HLERequestContext& ctx);
91 void ActivateMouse(Kernel::HLERequestContext& ctx); 93 void ActivateMouse(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 5e2d769a4..a0469ffbd 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array>
5#include <atomic> 6#include <atomic>
6 7
7#include "common/logging/log.h" 8#include "common/logging/log.h"
@@ -72,10 +73,10 @@ private:
72 std::array<u8, 10> uuid; 73 std::array<u8, 10> uuid;
73 u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it 74 u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it
74 // mean something else 75 // mean something else
75 INSERT_PADDING_BYTES(0x15); 76 std::array<u8, 0x15> padding_1;
76 u32_le protocol; 77 u32_le protocol;
77 u32_le tag_type; 78 u32_le tag_type;
78 INSERT_PADDING_BYTES(0x2c); 79 std::array<u8, 0x2c> padding_2;
79 }; 80 };
80 static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size"); 81 static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size");
81 82
@@ -213,13 +214,15 @@ private:
213 LOG_DEBUG(Service_NFP, "called"); 214 LOG_DEBUG(Service_NFP, "called");
214 215
215 IPC::ResponseBuilder rb{ctx, 2}; 216 IPC::ResponseBuilder rb{ctx, 2};
216 auto amiibo = nfp_interface.GetAmiiboBuffer(); 217 const auto& amiibo = nfp_interface.GetAmiiboBuffer();
217 TagInfo tag_info{}; 218 const TagInfo tag_info{
218 tag_info.uuid = amiibo.uuid; 219 .uuid = amiibo.uuid,
219 tag_info.uuid_length = static_cast<u8>(tag_info.uuid.size()); 220 .uuid_length = static_cast<u8>(tag_info.uuid.size()),
220 221 .padding_1 = {},
221 tag_info.protocol = 1; // TODO(ogniK): Figure out actual values 222 .protocol = 1, // TODO(ogniK): Figure out actual values
222 tag_info.tag_type = 2; 223 .tag_type = 2,
224 .padding_2 = {},
225 };
223 ctx.WriteBuffer(tag_info); 226 ctx.WriteBuffer(tag_info);
224 rb.Push(RESULT_SUCCESS); 227 rb.Push(RESULT_SUCCESS);
225 } 228 }
@@ -236,7 +239,7 @@ private:
236 LOG_DEBUG(Service_NFP, "called"); 239 LOG_DEBUG(Service_NFP, "called");
237 240
238 IPC::ResponseBuilder rb{ctx, 2}; 241 IPC::ResponseBuilder rb{ctx, 2};
239 auto amiibo = nfp_interface.GetAmiiboBuffer(); 242 const auto& amiibo = nfp_interface.GetAmiiboBuffer();
240 ctx.WriteBuffer(amiibo.model_info); 243 ctx.WriteBuffer(amiibo.model_info);
241 rb.Push(RESULT_SUCCESS); 244 rb.Push(RESULT_SUCCESS);
242 } 245 }
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 94bc5ade7..76b3533ec 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -72,25 +72,6 @@
72 72
73namespace Service { 73namespace Service {
74 74
75/**
76 * Creates a function string for logging, complete with the name (or header code, depending
77 * on what's passed in) the port name, and all the cmd_buff arguments.
78 */
79[[maybe_unused]] static std::string MakeFunctionString(std::string_view name,
80 std::string_view port_name,
81 const u32* cmd_buff) {
82 // Number of params == bits 0-5 + bits 6-11
83 int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
84
85 std::string function_string = fmt::format("function '{}': port={}", name, port_name);
86 for (int i = 1; i <= num_params; ++i) {
87 function_string += fmt::format(", cmd_buff[{}]=0x{:X}", i, cmd_buff[i]);
88 }
89 return function_string;
90}
91
92////////////////////////////////////////////////////////////////////////////////////////////////////
93
94ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_sessions, 75ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_sessions,
95 InvokerFn* handler_invoker) 76 InvokerFn* handler_invoker)
96 : service_name(service_name), max_sessions(max_sessions), handler_invoker(handler_invoker) {} 77 : service_name(service_name), max_sessions(max_sessions), handler_invoker(handler_invoker) {}
@@ -189,9 +170,6 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
189 return RESULT_SUCCESS; 170 return RESULT_SUCCESS;
190} 171}
191 172
192////////////////////////////////////////////////////////////////////////////////////////////////////
193// Module interface
194
195/// Initialize ServiceManager 173/// Initialize ServiceManager
196void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { 174void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
197 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it 175 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
diff --git a/src/core/settings.h b/src/core/settings.h
index 80f0d95a7..9834f44bb 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -152,6 +152,7 @@ struct Values {
152 152
153 bool vibration_enabled; 153 bool vibration_enabled;
154 154
155 bool motion_enabled;
155 std::string motion_device; 156 std::string motion_device;
156 std::string touch_device; 157 std::string touch_device;
157 TouchscreenInput touchscreen; 158 TouchscreenInput touchscreen;
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index ea1a1cee6..062ec66b5 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -12,6 +12,7 @@
12#include "input_common/main.h" 12#include "input_common/main.h"
13#include "input_common/motion_emu.h" 13#include "input_common/motion_emu.h"
14#include "input_common/touch_from_button.h" 14#include "input_common/touch_from_button.h"
15#include "input_common/udp/client.h"
15#include "input_common/udp/udp.h" 16#include "input_common/udp/udp.h"
16#ifdef HAVE_SDL2 17#ifdef HAVE_SDL2
17#include "input_common/sdl/sdl.h" 18#include "input_common/sdl/sdl.h"
@@ -40,7 +41,11 @@ struct InputSubsystem::Impl {
40 sdl = SDL::Init(); 41 sdl = SDL::Init();
41#endif 42#endif
42 43
43 udp = CemuhookUDP::Init(); 44 udp = std::make_shared<InputCommon::CemuhookUDP::Client>();
45 udpmotion = std::make_shared<UDPMotionFactory>(udp);
46 Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", udpmotion);
47 udptouch = std::make_shared<UDPTouchFactory>(udp);
48 Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", udptouch);
44 } 49 }
45 50
46 void Shutdown() { 51 void Shutdown() {
@@ -53,12 +58,17 @@ struct InputSubsystem::Impl {
53#ifdef HAVE_SDL2 58#ifdef HAVE_SDL2
54 sdl.reset(); 59 sdl.reset();
55#endif 60#endif
56 udp.reset();
57 Input::UnregisterFactory<Input::ButtonDevice>("gcpad"); 61 Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
58 Input::UnregisterFactory<Input::AnalogDevice>("gcpad"); 62 Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
59 63
60 gcbuttons.reset(); 64 gcbuttons.reset();
61 gcanalog.reset(); 65 gcanalog.reset();
66
67 Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp");
68 Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp");
69
70 udpmotion.reset();
71 udptouch.reset();
62 } 72 }
63 73
64 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const { 74 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
@@ -109,14 +119,28 @@ struct InputSubsystem::Impl {
109 return {}; 119 return {};
110 } 120 }
111 121
122 [[nodiscard]] MotionMapping GetMotionMappingForDevice(
123 const Common::ParamPackage& params) const {
124 if (!params.Has("class") || params.Get("class", "") == "any") {
125 return {};
126 }
127 if (params.Get("class", "") == "cemuhookudp") {
128 // TODO return the correct motion device
129 return {};
130 }
131 return {};
132 }
133
112 std::shared_ptr<Keyboard> keyboard; 134 std::shared_ptr<Keyboard> keyboard;
113 std::shared_ptr<MotionEmu> motion_emu; 135 std::shared_ptr<MotionEmu> motion_emu;
114#ifdef HAVE_SDL2 136#ifdef HAVE_SDL2
115 std::unique_ptr<SDL::State> sdl; 137 std::unique_ptr<SDL::State> sdl;
116#endif 138#endif
117 std::unique_ptr<CemuhookUDP::State> udp;
118 std::shared_ptr<GCButtonFactory> gcbuttons; 139 std::shared_ptr<GCButtonFactory> gcbuttons;
119 std::shared_ptr<GCAnalogFactory> gcanalog; 140 std::shared_ptr<GCAnalogFactory> gcanalog;
141 std::shared_ptr<UDPMotionFactory> udpmotion;
142 std::shared_ptr<UDPTouchFactory> udptouch;
143 std::shared_ptr<CemuhookUDP::Client> udp;
120}; 144};
121 145
122InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {} 146InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
@@ -175,6 +199,22 @@ const GCButtonFactory* InputSubsystem::GetGCButtons() const {
175 return impl->gcbuttons.get(); 199 return impl->gcbuttons.get();
176} 200}
177 201
202UDPMotionFactory* InputSubsystem::GetUDPMotions() {
203 return impl->udpmotion.get();
204}
205
206const UDPMotionFactory* InputSubsystem::GetUDPMotions() const {
207 return impl->udpmotion.get();
208}
209
210UDPTouchFactory* InputSubsystem::GetUDPTouch() {
211 return impl->udptouch.get();
212}
213
214const UDPTouchFactory* InputSubsystem::GetUDPTouch() const {
215 return impl->udptouch.get();
216}
217
178void InputSubsystem::ReloadInputDevices() { 218void InputSubsystem::ReloadInputDevices() {
179 if (!impl->udp) { 219 if (!impl->udp) {
180 return; 220 return;
diff --git a/src/input_common/main.h b/src/input_common/main.h
index f3fbf696e..dded3f1ef 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -21,10 +21,14 @@ namespace Settings::NativeButton {
21enum Values : int; 21enum Values : int;
22} 22}
23 23
24namespace Settings::NativeMotion {
25enum Values : int;
26}
27
24namespace InputCommon { 28namespace InputCommon {
25namespace Polling { 29namespace Polling {
26 30
27enum class DeviceType { Button, AnalogPreferred }; 31enum class DeviceType { Button, AnalogPreferred, Motion };
28 32
29/** 33/**
30 * A class that can be used to get inputs from an input device like controllers without having to 34 * A class that can be used to get inputs from an input device like controllers without having to
@@ -50,6 +54,8 @@ public:
50 54
51class GCAnalogFactory; 55class GCAnalogFactory;
52class GCButtonFactory; 56class GCButtonFactory;
57class UDPMotionFactory;
58class UDPTouchFactory;
53class Keyboard; 59class Keyboard;
54class MotionEmu; 60class MotionEmu;
55 61
@@ -59,6 +65,7 @@ class MotionEmu;
59 */ 65 */
60using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>; 66using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>;
61using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>; 67using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>;
68using MotionMapping = std::unordered_map<Settings::NativeMotion::Values, Common::ParamPackage>;
62 69
63class InputSubsystem { 70class InputSubsystem {
64public: 71public:
@@ -103,6 +110,9 @@ public:
103 /// Retrieves the button mappings for the given device. 110 /// Retrieves the button mappings for the given device.
104 [[nodiscard]] ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& device) const; 111 [[nodiscard]] ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& device) const;
105 112
113 /// Retrieves the motion mappings for the given device.
114 [[nodiscard]] MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& device) const;
115
106 /// Retrieves the underlying GameCube analog handler. 116 /// Retrieves the underlying GameCube analog handler.
107 [[nodiscard]] GCAnalogFactory* GetGCAnalogs(); 117 [[nodiscard]] GCAnalogFactory* GetGCAnalogs();
108 118
@@ -115,6 +125,18 @@ public:
115 /// Retrieves the underlying GameCube button handler. 125 /// Retrieves the underlying GameCube button handler.
116 [[nodiscard]] const GCButtonFactory* GetGCButtons() const; 126 [[nodiscard]] const GCButtonFactory* GetGCButtons() const;
117 127
128 /// Retrieves the underlying udp motion handler.
129 [[nodiscard]] UDPMotionFactory* GetUDPMotions();
130
131 /// Retrieves the underlying udp motion handler.
132 [[nodiscard]] const UDPMotionFactory* GetUDPMotions() const;
133
134 /// Retrieves the underlying udp touch handler.
135 [[nodiscard]] UDPTouchFactory* GetUDPTouch();
136
137 /// Retrieves the underlying udp touch handler.
138 [[nodiscard]] const UDPTouchFactory* GetUDPTouch() const;
139
118 /// Reloads the input devices 140 /// Reloads the input devices
119 void ReloadInputDevices(); 141 void ReloadInputDevices();
120 142
diff --git a/src/input_common/motion_emu.cpp b/src/input_common/motion_emu.cpp
index d4cdf76a3..69fd3c1d2 100644
--- a/src/input_common/motion_emu.cpp
+++ b/src/input_common/motion_emu.cpp
@@ -56,7 +56,7 @@ public:
56 is_tilting = false; 56 is_tilting = false;
57 } 57 }
58 58
59 std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() { 59 Input::MotionStatus GetStatus() {
60 std::lock_guard guard{status_mutex}; 60 std::lock_guard guard{status_mutex};
61 return status; 61 return status;
62 } 62 }
@@ -76,7 +76,7 @@ private:
76 76
77 Common::Event shutdown_event; 77 Common::Event shutdown_event;
78 78
79 std::tuple<Common::Vec3<float>, Common::Vec3<float>> status; 79 Input::MotionStatus status;
80 std::mutex status_mutex; 80 std::mutex status_mutex;
81 81
82 // Note: always keep the thread declaration at the end so that other objects are initialized 82 // Note: always keep the thread declaration at the end so that other objects are initialized
@@ -113,10 +113,19 @@ private:
113 gravity = QuaternionRotate(inv_q, gravity); 113 gravity = QuaternionRotate(inv_q, gravity);
114 angular_rate = QuaternionRotate(inv_q, angular_rate); 114 angular_rate = QuaternionRotate(inv_q, angular_rate);
115 115
116 // TODO: Calculate the correct rotation vector and orientation matrix
117 const auto matrix4x4 = q.ToMatrix();
118 const auto rotation = Common::MakeVec(0.0f, 0.0f, 0.0f);
119 const std::array orientation{
120 Common::Vec3f(matrix4x4[0], matrix4x4[1], -matrix4x4[2]),
121 Common::Vec3f(matrix4x4[4], matrix4x4[5], -matrix4x4[6]),
122 Common::Vec3f(-matrix4x4[8], -matrix4x4[9], matrix4x4[10]),
123 };
124
116 // Update the sensor state 125 // Update the sensor state
117 { 126 {
118 std::lock_guard guard{status_mutex}; 127 std::lock_guard guard{status_mutex};
119 status = std::make_tuple(gravity, angular_rate); 128 status = std::make_tuple(gravity, angular_rate, rotation, orientation);
120 } 129 }
121 } 130 }
122 } 131 }
@@ -131,7 +140,7 @@ public:
131 device = std::make_shared<MotionEmuDevice>(update_millisecond, sensitivity); 140 device = std::make_shared<MotionEmuDevice>(update_millisecond, sensitivity);
132 } 141 }
133 142
134 std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() const override { 143 Input::MotionStatus GetStatus() const override {
135 return device->GetStatus(); 144 return device->GetStatus();
136 } 145 }
137 146
diff --git a/src/input_common/settings.cpp b/src/input_common/settings.cpp
index 80c719cf4..b66c05856 100644
--- a/src/input_common/settings.cpp
+++ b/src/input_common/settings.cpp
@@ -14,6 +14,13 @@ const std::array<const char*, NumButtons> mapping = {{
14}}; 14}};
15} 15}
16 16
17namespace NativeMotion {
18const std::array<const char*, NumMotions> mapping = {{
19 "motionleft",
20 "motionright",
21}};
22}
23
17namespace NativeAnalog { 24namespace NativeAnalog {
18const std::array<const char*, NumAnalogs> mapping = {{ 25const std::array<const char*, NumAnalogs> mapping = {{
19 "lstick", 26 "lstick",
diff --git a/src/input_common/settings.h b/src/input_common/settings.h
index 2d258960b..ab0b95cf1 100644
--- a/src/input_common/settings.h
+++ b/src/input_common/settings.h
@@ -66,6 +66,21 @@ constexpr int NUM_STICKS_HID = NumAnalogs;
66extern const std::array<const char*, NumAnalogs> mapping; 66extern const std::array<const char*, NumAnalogs> mapping;
67} // namespace NativeAnalog 67} // namespace NativeAnalog
68 68
69namespace NativeMotion {
70enum Values : int {
71 MOTIONLEFT,
72 MOTIONRIGHT,
73
74 NumMotions,
75};
76
77constexpr int MOTION_HID_BEGIN = MOTIONLEFT;
78constexpr int MOTION_HID_END = NumMotions;
79constexpr int NUM_MOTION_HID = NumMotions;
80
81extern const std::array<const char*, NumMotions> mapping;
82} // namespace NativeMotion
83
69namespace NativeMouseButton { 84namespace NativeMouseButton {
70enum Values { 85enum Values {
71 Left, 86 Left,
@@ -292,6 +307,7 @@ constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
292 307
293using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>; 308using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
294using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; 309using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
310using MotionRaw = std::array<std::string, NativeMotion::NumMotions>;
295using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>; 311using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
296using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>; 312using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
297using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>; 313using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
@@ -314,6 +330,7 @@ struct PlayerInput {
314 ControllerType controller_type; 330 ControllerType controller_type;
315 ButtonsRaw buttons; 331 ButtonsRaw buttons;
316 AnalogsRaw analogs; 332 AnalogsRaw analogs;
333 MotionRaw motions;
317 std::string lstick_mod; 334 std::string lstick_mod;
318 std::string rstick_mod; 335 std::string rstick_mod;
319 336
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp
index 3f4eaf448..2b6a68d4b 100644
--- a/src/input_common/udp/client.cpp
+++ b/src/input_common/udp/client.cpp
@@ -2,14 +2,13 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6#include <array>
7#include <chrono> 5#include <chrono>
8#include <cstring> 6#include <cstring>
9#include <functional> 7#include <functional>
10#include <thread> 8#include <thread>
11#include <boost/asio.hpp> 9#include <boost/asio.hpp>
12#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "core/settings.h"
13#include "input_common/udp/client.h" 12#include "input_common/udp/client.h"
14#include "input_common/udp/protocol.h" 13#include "input_common/udp/protocol.h"
15 14
@@ -131,21 +130,59 @@ static void SocketLoop(Socket* socket) {
131 socket->Loop(); 130 socket->Loop();
132} 131}
133 132
134Client::Client(std::shared_ptr<DeviceStatus> status, const std::string& host, u16 port, 133Client::Client() {
135 u8 pad_index, u32 client_id) 134 LOG_INFO(Input, "Udp Initialization started");
136 : status(std::move(status)) { 135 for (std::size_t client = 0; client < clients.size(); client++) {
137 StartCommunication(host, port, pad_index, client_id); 136 u8 pad = client % 4;
137 StartCommunication(client, Settings::values.udp_input_address,
138 Settings::values.udp_input_port, pad, 24872);
139 // Set motion parameters
140 // SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode
141 // Real HW values are unknown, 0.0001 is an approximate to Standard
142 clients[client].motion.SetGyroThreshold(0.0001f);
143 }
138} 144}
139 145
140Client::~Client() { 146Client::~Client() {
141 socket->Stop(); 147 Reset();
142 thread.join(); 148}
149
150std::vector<Common::ParamPackage> Client::GetInputDevices() const {
151 std::vector<Common::ParamPackage> devices;
152 for (std::size_t client = 0; client < clients.size(); client++) {
153 if (!DeviceConnected(client)) {
154 continue;
155 }
156 std::string name = fmt::format("UDP Controller {}", client);
157 devices.emplace_back(Common::ParamPackage{
158 {"class", "cemuhookudp"},
159 {"display", std::move(name)},
160 {"port", std::to_string(client)},
161 });
162 }
163 return devices;
143} 164}
144 165
166bool Client::DeviceConnected(std::size_t pad) const {
167 // Use last timestamp to detect if the socket has stopped sending data
168 const auto now = std::chrono::system_clock::now();
169 u64 time_difference =
170 std::chrono::duration_cast<std::chrono::milliseconds>(now - clients[pad].last_motion_update)
171 .count();
172 return time_difference < 1000 && clients[pad].active == 1;
173}
174
175void Client::ReloadUDPClient() {
176 for (std::size_t client = 0; client < clients.size(); client++) {
177 ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port, client);
178 }
179}
145void Client::ReloadSocket(const std::string& host, u16 port, u8 pad_index, u32 client_id) { 180void Client::ReloadSocket(const std::string& host, u16 port, u8 pad_index, u32 client_id) {
146 socket->Stop(); 181 // client number must be determined from host / port and pad index
147 thread.join(); 182 std::size_t client = pad_index;
148 StartCommunication(host, port, pad_index, client_id); 183 clients[client].socket->Stop();
184 clients[client].thread.join();
185 StartCommunication(client, host, port, pad_index, client_id);
149} 186}
150 187
151void Client::OnVersion(Response::Version data) { 188void Client::OnVersion(Response::Version data) {
@@ -157,23 +194,39 @@ void Client::OnPortInfo(Response::PortInfo data) {
157} 194}
158 195
159void Client::OnPadData(Response::PadData data) { 196void Client::OnPadData(Response::PadData data) {
197 // client number must be determined from host / port and pad index
198 std::size_t client = data.info.id;
160 LOG_TRACE(Input, "PadData packet received"); 199 LOG_TRACE(Input, "PadData packet received");
161 if (data.packet_counter <= packet_sequence) { 200 if (data.packet_counter == clients[client].packet_sequence) {
162 LOG_WARNING( 201 LOG_WARNING(
163 Input, 202 Input,
164 "PadData packet dropped because its stale info. Current count: {} Packet count: {}", 203 "PadData packet dropped because its stale info. Current count: {} Packet count: {}",
165 packet_sequence, data.packet_counter); 204 clients[client].packet_sequence, data.packet_counter);
166 return; 205 return;
167 } 206 }
168 packet_sequence = data.packet_counter; 207 clients[client].active = data.info.is_pad_active;
169 // TODO: Check how the Switch handles motions and how the CemuhookUDP motion 208 clients[client].packet_sequence = data.packet_counter;
170 // directions correspond to the ones of the Switch 209 const auto now = std::chrono::system_clock::now();
171 Common::Vec3f accel = Common::MakeVec<float>(data.accel.x, data.accel.y, data.accel.z); 210 u64 time_difference = std::chrono::duration_cast<std::chrono::microseconds>(
172 Common::Vec3f gyro = Common::MakeVec<float>(data.gyro.pitch, data.gyro.yaw, data.gyro.roll); 211 now - clients[client].last_motion_update)
173 { 212 .count();
174 std::lock_guard guard(status->update_mutex); 213 clients[client].last_motion_update = now;
214 Common::Vec3f raw_gyroscope = {data.gyro.pitch, data.gyro.roll, -data.gyro.yaw};
215 clients[client].motion.SetAcceleration({data.accel.x, -data.accel.z, data.accel.y});
216 // Gyroscope values are not it the correct scale from better joy.
217 // Dividing by 312 allows us to make one full turn = 1 turn
218 // This must be a configurable valued called sensitivity
219 clients[client].motion.SetGyroscope(raw_gyroscope / 312.0f);
220 clients[client].motion.UpdateRotation(time_difference);
221 clients[client].motion.UpdateOrientation(time_difference);
222 Common::Vec3f gyroscope = clients[client].motion.GetGyroscope();
223 Common::Vec3f accelerometer = clients[client].motion.GetAcceleration();
224 Common::Vec3f rotation = clients[client].motion.GetRotations();
225 std::array<Common::Vec3f, 3> orientation = clients[client].motion.GetOrientation();
175 226
176 status->motion_status = {accel, gyro}; 227 {
228 std::lock_guard guard(clients[client].status.update_mutex);
229 clients[client].status.motion_status = {accelerometer, gyroscope, rotation, orientation};
177 230
178 // TODO: add a setting for "click" touch. Click touch refers to a device that differentiates 231 // TODO: add a setting for "click" touch. Click touch refers to a device that differentiates
179 // between a simple "tap" and a hard press that causes the touch screen to click. 232 // between a simple "tap" and a hard press that causes the touch screen to click.
@@ -182,11 +235,11 @@ void Client::OnPadData(Response::PadData data) {
182 float x = 0; 235 float x = 0;
183 float y = 0; 236 float y = 0;
184 237
185 if (is_active && status->touch_calibration) { 238 if (is_active && clients[client].status.touch_calibration) {
186 const u16 min_x = status->touch_calibration->min_x; 239 const u16 min_x = clients[client].status.touch_calibration->min_x;
187 const u16 max_x = status->touch_calibration->max_x; 240 const u16 max_x = clients[client].status.touch_calibration->max_x;
188 const u16 min_y = status->touch_calibration->min_y; 241 const u16 min_y = clients[client].status.touch_calibration->min_y;
189 const u16 max_y = status->touch_calibration->max_y; 242 const u16 max_y = clients[client].status.touch_calibration->max_y;
190 243
191 x = (std::clamp(static_cast<u16>(data.touch_1.x), min_x, max_x) - min_x) / 244 x = (std::clamp(static_cast<u16>(data.touch_1.x), min_x, max_x) - min_x) /
192 static_cast<float>(max_x - min_x); 245 static_cast<float>(max_x - min_x);
@@ -194,17 +247,80 @@ void Client::OnPadData(Response::PadData data) {
194 static_cast<float>(max_y - min_y); 247 static_cast<float>(max_y - min_y);
195 } 248 }
196 249
197 status->touch_status = {x, y, is_active}; 250 clients[client].status.touch_status = {x, y, is_active};
251
252 if (configuring) {
253 UpdateYuzuSettings(client, accelerometer, gyroscope, is_active);
254 }
198 } 255 }
199} 256}
200 257
201void Client::StartCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id) { 258void Client::StartCommunication(std::size_t client, const std::string& host, u16 port, u8 pad_index,
259 u32 client_id) {
202 SocketCallback callback{[this](Response::Version version) { OnVersion(version); }, 260 SocketCallback callback{[this](Response::Version version) { OnVersion(version); },
203 [this](Response::PortInfo info) { OnPortInfo(info); }, 261 [this](Response::PortInfo info) { OnPortInfo(info); },
204 [this](Response::PadData data) { OnPadData(data); }}; 262 [this](Response::PadData data) { OnPadData(data); }};
205 LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port); 263 LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port);
206 socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback); 264 clients[client].socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback);
207 thread = std::thread{SocketLoop, this->socket.get()}; 265 clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()};
266}
267
268void Client::Reset() {
269 for (std::size_t client = 0; client < clients.size(); client++) {
270 clients[client].socket->Stop();
271 clients[client].thread.join();
272 }
273}
274
275void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
276 const Common::Vec3<float>& gyro, bool touch) {
277 UDPPadStatus pad;
278 if (touch) {
279 pad.touch = PadTouch::Click;
280 pad_queue[client].Push(pad);
281 }
282 for (size_t i = 0; i < 3; ++i) {
283 if (gyro[i] > 6.0f || gyro[i] < -6.0f) {
284 pad.motion = static_cast<PadMotion>(i);
285 pad.motion_value = gyro[i];
286 pad_queue[client].Push(pad);
287 }
288 if (acc[i] > 2.0f || acc[i] < -2.0f) {
289 pad.motion = static_cast<PadMotion>(i + 3);
290 pad.motion_value = acc[i];
291 pad_queue[client].Push(pad);
292 }
293 }
294}
295
296void Client::BeginConfiguration() {
297 for (auto& pq : pad_queue) {
298 pq.Clear();
299 }
300 configuring = true;
301}
302
303void Client::EndConfiguration() {
304 for (auto& pq : pad_queue) {
305 pq.Clear();
306 }
307 configuring = false;
308}
309
310DeviceStatus& Client::GetPadState(std::size_t pad) {
311 return clients[pad].status;
312}
313
314const DeviceStatus& Client::GetPadState(std::size_t pad) const {
315 return clients[pad].status;
316}
317
318std::array<Common::SPSCQueue<UDPPadStatus>, 4>& Client::GetPadQueue() {
319 return pad_queue;
320}
321
322const std::array<Common::SPSCQueue<UDPPadStatus>, 4>& Client::GetPadQueue() const {
323 return pad_queue;
208} 324}
209 325
210void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id, 326void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id,
diff --git a/src/input_common/udp/client.h b/src/input_common/udp/client.h
index b8c654755..523dc6a7a 100644
--- a/src/input_common/udp/client.h
+++ b/src/input_common/udp/client.h
@@ -12,8 +12,12 @@
12#include <thread> 12#include <thread>
13#include <tuple> 13#include <tuple>
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/param_package.h"
15#include "common/thread.h" 16#include "common/thread.h"
17#include "common/threadsafe_queue.h"
16#include "common/vector_math.h" 18#include "common/vector_math.h"
19#include "core/frontend/input.h"
20#include "input_common/motion_input.h"
17 21
18namespace InputCommon::CemuhookUDP { 22namespace InputCommon::CemuhookUDP {
19 23
@@ -28,9 +32,30 @@ struct PortInfo;
28struct Version; 32struct Version;
29} // namespace Response 33} // namespace Response
30 34
35enum class PadMotion {
36 GyroX,
37 GyroY,
38 GyroZ,
39 AccX,
40 AccY,
41 AccZ,
42 Undefined,
43};
44
45enum class PadTouch {
46 Click,
47 Undefined,
48};
49
50struct UDPPadStatus {
51 PadTouch touch{PadTouch::Undefined};
52 PadMotion motion{PadMotion::Undefined};
53 f32 motion_value{0.0f};
54};
55
31struct DeviceStatus { 56struct DeviceStatus {
32 std::mutex update_mutex; 57 std::mutex update_mutex;
33 std::tuple<Common::Vec3<float>, Common::Vec3<float>> motion_status; 58 Input::MotionStatus motion_status;
34 std::tuple<float, float, bool> touch_status; 59 std::tuple<float, float, bool> touch_status;
35 60
36 // calibration data for scaling the device's touch area to 3ds 61 // calibration data for scaling the device's touch area to 3ds
@@ -45,22 +70,58 @@ struct DeviceStatus {
45 70
46class Client { 71class Client {
47public: 72public:
48 explicit Client(std::shared_ptr<DeviceStatus> status, const std::string& host = DEFAULT_ADDR, 73 // Initialize the UDP client capture and read sequence
49 u16 port = DEFAULT_PORT, u8 pad_index = 0, u32 client_id = 24872); 74 Client();
75
76 // Close and release the client
50 ~Client(); 77 ~Client();
78
79 // Used for polling
80 void BeginConfiguration();
81 void EndConfiguration();
82
83 std::vector<Common::ParamPackage> GetInputDevices() const;
84
85 bool DeviceConnected(std::size_t pad) const;
86 void ReloadUDPClient();
51 void ReloadSocket(const std::string& host = "127.0.0.1", u16 port = 26760, u8 pad_index = 0, 87 void ReloadSocket(const std::string& host = "127.0.0.1", u16 port = 26760, u8 pad_index = 0,
52 u32 client_id = 24872); 88 u32 client_id = 24872);
53 89
90 std::array<Common::SPSCQueue<UDPPadStatus>, 4>& GetPadQueue();
91 const std::array<Common::SPSCQueue<UDPPadStatus>, 4>& GetPadQueue() const;
92
93 DeviceStatus& GetPadState(std::size_t pad);
94 const DeviceStatus& GetPadState(std::size_t pad) const;
95
54private: 96private:
97 struct ClientData {
98 std::unique_ptr<Socket> socket;
99 DeviceStatus status;
100 std::thread thread;
101 u64 packet_sequence = 0;
102 u8 active;
103
104 // Realtime values
105 // motion is initalized with PID values for drift correction on joycons
106 InputCommon::MotionInput motion{0.3f, 0.005f, 0.0f};
107 std::chrono::time_point<std::chrono::system_clock> last_motion_update;
108 };
109
110 // For shutting down, clear all data, join all threads, release usb
111 void Reset();
112
55 void OnVersion(Response::Version); 113 void OnVersion(Response::Version);
56 void OnPortInfo(Response::PortInfo); 114 void OnPortInfo(Response::PortInfo);
57 void OnPadData(Response::PadData); 115 void OnPadData(Response::PadData);
58 void StartCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id); 116 void StartCommunication(std::size_t client, const std::string& host, u16 port, u8 pad_index,
117 u32 client_id);
118 void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
119 const Common::Vec3<float>& gyro, bool touch);
120
121 bool configuring = false;
59 122
60 std::unique_ptr<Socket> socket; 123 std::array<ClientData, 4> clients;
61 std::shared_ptr<DeviceStatus> status; 124 std::array<Common::SPSCQueue<UDPPadStatus>, 4> pad_queue;
62 std::thread thread;
63 u64 packet_sequence = 0;
64}; 125};
65 126
66/// An async job allowing configuration of the touchpad calibration. 127/// An async job allowing configuration of the touchpad calibration.
diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp
index 4b347e47e..eba077a36 100644
--- a/src/input_common/udp/udp.cpp
+++ b/src/input_common/udp/udp.cpp
@@ -1,105 +1,144 @@
1// Copyright 2018 Citra Emulator Project 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <atomic>
6#include <list>
5#include <mutex> 7#include <mutex>
6#include <optional> 8#include <utility>
7#include <tuple> 9#include "common/assert.h"
8 10#include "common/threadsafe_queue.h"
9#include "common/param_package.h"
10#include "core/frontend/input.h"
11#include "core/settings.h"
12#include "input_common/udp/client.h" 11#include "input_common/udp/client.h"
13#include "input_common/udp/udp.h" 12#include "input_common/udp/udp.h"
14 13
15namespace InputCommon::CemuhookUDP { 14namespace InputCommon {
16 15
17class UDPTouchDevice final : public Input::TouchDevice { 16class UDPMotion final : public Input::MotionDevice {
18public: 17public:
19 explicit UDPTouchDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} 18 UDPMotion(std::string ip_, int port_, int pad_, CemuhookUDP::Client* client_)
20 std::tuple<float, float, bool> GetStatus() const override { 19 : ip(ip_), port(port_), pad(pad_), client(client_) {}
21 std::lock_guard guard(status->update_mutex); 20
22 return status->touch_status; 21 Input::MotionStatus GetStatus() const override {
22 return client->GetPadState(pad).motion_status;
23 } 23 }
24 24
25private: 25private:
26 std::shared_ptr<DeviceStatus> status; 26 const std::string ip;
27 const int port;
28 const int pad;
29 CemuhookUDP::Client* client;
30 mutable std::mutex mutex;
27}; 31};
28 32
29class UDPMotionDevice final : public Input::MotionDevice { 33/// A motion device factory that creates motion devices from JC Adapter
30public: 34UDPMotionFactory::UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_)
31 explicit UDPMotionDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} 35 : client(std::move(client_)) {}
32 std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() const override { 36
33 std::lock_guard guard(status->update_mutex); 37/**
34 return status->motion_status; 38 * Creates motion device
35 } 39 * @param params contains parameters for creating the device:
40 * - "port": the nth jcpad on the adapter
41 */
42std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) {
43 const std::string ip = params.Get("ip", "127.0.0.1");
44 const int port = params.Get("port", 26760);
45 const int pad = params.Get("pad_index", 0);
46
47 return std::make_unique<UDPMotion>(ip, port, pad, client.get());
48}
36 49
37private: 50void UDPMotionFactory::BeginConfiguration() {
38 std::shared_ptr<DeviceStatus> status; 51 polling = true;
39}; 52 client->BeginConfiguration();
53}
40 54
41class UDPTouchFactory final : public Input::Factory<Input::TouchDevice> { 55void UDPMotionFactory::EndConfiguration() {
42public: 56 polling = false;
43 explicit UDPTouchFactory(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} 57 client->EndConfiguration();
44 58}
45 std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override { 59
46 { 60Common::ParamPackage UDPMotionFactory::GetNextInput() {
47 std::lock_guard guard(status->update_mutex); 61 Common::ParamPackage params;
48 status->touch_calibration = DeviceStatus::CalibrationData{}; 62 CemuhookUDP::UDPPadStatus pad;
49 // These default values work well for DS4 but probably not other touch inputs 63 auto& queue = client->GetPadQueue();
50 status->touch_calibration->min_x = params.Get("min_x", 100); 64 for (std::size_t pad_number = 0; pad_number < queue.size(); ++pad_number) {
51 status->touch_calibration->min_y = params.Get("min_y", 50); 65 while (queue[pad_number].Pop(pad)) {
52 status->touch_calibration->max_x = params.Get("max_x", 1800); 66 if (pad.motion == CemuhookUDP::PadMotion::Undefined || std::abs(pad.motion_value) < 1) {
53 status->touch_calibration->max_y = params.Get("max_y", 850); 67 continue;
68 }
69 params.Set("engine", "cemuhookudp");
70 params.Set("ip", "127.0.0.1");
71 params.Set("port", 26760);
72 params.Set("pad_index", static_cast<int>(pad_number));
73 params.Set("motion", static_cast<u16>(pad.motion));
74 return params;
54 } 75 }
55 return std::make_unique<UDPTouchDevice>(status);
56 } 76 }
77 return params;
78}
57 79
58private: 80class UDPTouch final : public Input::TouchDevice {
59 std::shared_ptr<DeviceStatus> status;
60};
61
62class UDPMotionFactory final : public Input::Factory<Input::MotionDevice> {
63public: 81public:
64 explicit UDPMotionFactory(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} 82 UDPTouch(std::string ip_, int port_, int pad_, CemuhookUDP::Client* client_)
83 : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {}
65 84
66 std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override { 85 std::tuple<float, float, bool> GetStatus() const override {
67 return std::make_unique<UDPMotionDevice>(status); 86 return client->GetPadState(pad).touch_status;
68 } 87 }
69 88
70private: 89private:
71 std::shared_ptr<DeviceStatus> status; 90 const std::string ip;
91 const int port;
92 const int pad;
93 CemuhookUDP::Client* client;
94 mutable std::mutex mutex;
72}; 95};
73 96
74State::State() { 97/// A motion device factory that creates motion devices from JC Adapter
75 auto status = std::make_shared<DeviceStatus>(); 98UDPTouchFactory::UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_)
76 client = 99 : client(std::move(client_)) {}
77 std::make_unique<Client>(status, Settings::values.udp_input_address, 100
78 Settings::values.udp_input_port, Settings::values.udp_pad_index); 101/**
79 102 * Creates motion device
80 motion_factory = std::make_shared<UDPMotionFactory>(status); 103 * @param params contains parameters for creating the device:
81 touch_factory = std::make_shared<UDPTouchFactory>(status); 104 * - "port": the nth jcpad on the adapter
82 105 */
83 Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", motion_factory); 106std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) {
84 Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", touch_factory); 107 const std::string ip = params.Get("ip", "127.0.0.1");
108 const int port = params.Get("port", 26760);
109 const int pad = params.Get("pad_index", 0);
110
111 return std::make_unique<UDPTouch>(ip, port, pad, client.get());
85} 112}
86 113
87State::~State() { 114void UDPTouchFactory::BeginConfiguration() {
88 Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp"); 115 polling = true;
89 Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp"); 116 client->BeginConfiguration();
90} 117}
91 118
92std::vector<Common::ParamPackage> State::GetInputDevices() const { 119void UDPTouchFactory::EndConfiguration() {
93 // TODO support binding udp devices 120 polling = false;
94 return {}; 121 client->EndConfiguration();
95} 122}
96 123
97void State::ReloadUDPClient() { 124Common::ParamPackage UDPTouchFactory::GetNextInput() {
98 client->ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port, 125 Common::ParamPackage params;
99 Settings::values.udp_pad_index); 126 CemuhookUDP::UDPPadStatus pad;
127 auto& queue = client->GetPadQueue();
128 for (std::size_t pad_number = 0; pad_number < queue.size(); ++pad_number) {
129 while (queue[pad_number].Pop(pad)) {
130 if (pad.touch == CemuhookUDP::PadTouch::Undefined) {
131 continue;
132 }
133 params.Set("engine", "cemuhookudp");
134 params.Set("ip", "127.0.0.1");
135 params.Set("port", 26760);
136 params.Set("pad_index", static_cast<int>(pad_number));
137 params.Set("touch", static_cast<u16>(pad.touch));
138 return params;
139 }
140 }
141 return params;
100} 142}
101 143
102std::unique_ptr<State> Init() { 144} // namespace InputCommon
103 return std::make_unique<State>();
104}
105} // namespace InputCommon::CemuhookUDP
diff --git a/src/input_common/udp/udp.h b/src/input_common/udp/udp.h
index 672a5c812..ea3fd4175 100644
--- a/src/input_common/udp/udp.h
+++ b/src/input_common/udp/udp.h
@@ -1,32 +1,57 @@
1// Copyright 2018 Citra Emulator Project 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <vector> 8#include "core/frontend/input.h"
9#include "common/param_package.h" 9#include "input_common/udp/client.h"
10 10
11namespace InputCommon::CemuhookUDP { 11namespace InputCommon {
12 12
13class Client; 13/// A motion device factory that creates motion devices from udp clients
14class UDPMotionFactory; 14class UDPMotionFactory final : public Input::Factory<Input::MotionDevice> {
15class UDPTouchFactory;
16
17class State {
18public: 15public:
19 State(); 16 explicit UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_);
20 ~State(); 17
21 void ReloadUDPClient(); 18 std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override;
22 std::vector<Common::ParamPackage> GetInputDevices() const; 19
20 Common::ParamPackage GetNextInput();
21
22 /// For device input configuration/polling
23 void BeginConfiguration();
24 void EndConfiguration();
25
26 bool IsPolling() const {
27 return polling;
28 }
23 29
24private: 30private:
25 std::unique_ptr<Client> client; 31 std::shared_ptr<CemuhookUDP::Client> client;
26 std::shared_ptr<UDPMotionFactory> motion_factory; 32 bool polling = false;
27 std::shared_ptr<UDPTouchFactory> touch_factory;
28}; 33};
29 34
30std::unique_ptr<State> Init(); 35/// A touch device factory that creates touch devices from udp clients
36class UDPTouchFactory final : public Input::Factory<Input::TouchDevice> {
37public:
38 explicit UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_);
39
40 std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override;
41
42 Common::ParamPackage GetNextInput();
43
44 /// For device input configuration/polling
45 void BeginConfiguration();
46 void EndConfiguration();
47
48 bool IsPolling() const {
49 return polling;
50 }
51
52private:
53 std::shared_ptr<CemuhookUDP::Client> client;
54 bool polling = false;
55};
31 56
32} // namespace InputCommon::CemuhookUDP 57} // namespace InputCommon
diff --git a/src/video_core/shader/decode/image.cpp b/src/video_core/shader/decode/image.cpp
index cd424aa91..618d309d2 100644
--- a/src/video_core/shader/decode/image.cpp
+++ b/src/video_core/shader/decode/image.cpp
@@ -470,6 +470,7 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
470 default: 470 default:
471 break; 471 break;
472 } 472 }
473 break;
473 default: 474 default:
474 break; 475 break;
475 } 476 }
diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp
index 29ebf65ba..a03b50e39 100644
--- a/src/video_core/shader/decode/texture.cpp
+++ b/src/video_core/shader/decode/texture.cpp
@@ -763,7 +763,7 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de
763 763
764Node4 ShaderIR::GetTldCode(Tegra::Shader::Instruction instr) { 764Node4 ShaderIR::GetTldCode(Tegra::Shader::Instruction instr) {
765 const auto texture_type{instr.tld.texture_type}; 765 const auto texture_type{instr.tld.texture_type};
766 const bool is_array{instr.tld.is_array}; 766 const bool is_array{instr.tld.is_array != 0};
767 const bool lod_enabled{instr.tld.GetTextureProcessMode() == TextureProcessMode::LL}; 767 const bool lod_enabled{instr.tld.GetTextureProcessMode() == TextureProcessMode::LL};
768 const std::size_t coord_count{GetCoordCount(texture_type)}; 768 const std::size_t coord_count{GetCoordCount(texture_type)};
769 769
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 2bc55a26a..d2913d613 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -36,6 +36,11 @@ const std::array<int, Settings::NativeButton::NumButtons> Config::default_button
36 Qt::Key_H, Qt::Key_G, Qt::Key_D, Qt::Key_C, Qt::Key_B, Qt::Key_V, 36 Qt::Key_H, Qt::Key_G, Qt::Key_D, Qt::Key_C, Qt::Key_B, Qt::Key_V,
37}; 37};
38 38
39const std::array<int, Settings::NativeMotion::NumMotions> Config::default_motions = {
40 Qt::Key_7,
41 Qt::Key_8,
42};
43
39const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{ 44const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
40 { 45 {
41 Qt::Key_Up, 46 Qt::Key_Up,
@@ -284,6 +289,22 @@ void Config::ReadPlayerValues() {
284 } 289 }
285 } 290 }
286 291
292 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
293 const std::string default_param =
294 InputCommon::GenerateKeyboardParam(default_motions[i]);
295 auto& player_motions = player.motions[i];
296
297 player_motions = qt_config
298 ->value(QStringLiteral("player_%1_").arg(p) +
299 QString::fromUtf8(Settings::NativeMotion::mapping[i]),
300 QString::fromStdString(default_param))
301 .toString()
302 .toStdString();
303 if (player_motions.empty()) {
304 player_motions = default_param;
305 }
306 }
307
287 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 308 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
288 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 309 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
289 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 310 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
@@ -424,6 +445,7 @@ void Config::ReadControlValues() {
424 445
425 Settings::values.vibration_enabled = 446 Settings::values.vibration_enabled =
426 ReadSetting(QStringLiteral("vibration_enabled"), true).toBool(); 447 ReadSetting(QStringLiteral("vibration_enabled"), true).toBool();
448 Settings::values.motion_enabled = ReadSetting(QStringLiteral("motion_enabled"), true).toBool();
427 Settings::values.use_docked_mode = 449 Settings::values.use_docked_mode =
428 ReadSetting(QStringLiteral("use_docked_mode"), false).toBool(); 450 ReadSetting(QStringLiteral("use_docked_mode"), false).toBool();
429 451
@@ -922,6 +944,14 @@ void Config::SavePlayerValues() {
922 QString::fromStdString(player.buttons[i]), 944 QString::fromStdString(player.buttons[i]),
923 QString::fromStdString(default_param)); 945 QString::fromStdString(default_param));
924 } 946 }
947 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
948 const std::string default_param =
949 InputCommon::GenerateKeyboardParam(default_motions[i]);
950 WriteSetting(QStringLiteral("player_%1_").arg(p) +
951 QString::fromStdString(Settings::NativeMotion::mapping[i]),
952 QString::fromStdString(player.motions[i]),
953 QString::fromStdString(default_param));
954 }
925 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 955 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
926 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 956 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
927 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 957 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
@@ -1062,6 +1092,7 @@ void Config::SaveControlValues() {
1062 SaveMotionTouchValues(); 1092 SaveMotionTouchValues();
1063 1093
1064 WriteSetting(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, true); 1094 WriteSetting(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, true);
1095 WriteSetting(QStringLiteral("motion_enabled"), Settings::values.motion_enabled, true);
1065 WriteSetting(QStringLiteral("motion_device"), 1096 WriteSetting(QStringLiteral("motion_device"),
1066 QString::fromStdString(Settings::values.motion_device), 1097 QString::fromStdString(Settings::values.motion_device),
1067 QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")); 1098 QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"));
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index ca0d29c6c..5d8e45d78 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -23,6 +23,7 @@ public:
23 void Save(); 23 void Save();
24 24
25 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; 25 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
26 static const std::array<int, Settings::NativeMotion::NumMotions> default_motions;
26 static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs; 27 static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
27 static const std::array<int, 2> default_stick_mod; 28 static const std::array<int, 2> default_stick_mod;
28 static const std::array<int, Settings::NativeMouseButton::NumMouseButtons> 29 static const std::array<int, Settings::NativeMouseButton::NumMouseButtons>
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 7ea17a4db..2725fcb2b 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -146,6 +146,10 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
146 CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem); 146 CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);
147 }); 147 });
148 148
149 connect(ui->motionButton, &QPushButton::clicked, [this, input_subsystem] {
150 CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);
151 });
152
149 connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); 153 connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); });
150 connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); }); 154 connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); });
151 155
@@ -172,6 +176,7 @@ void ConfigureInput::ApplyConfiguration() {
172 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode); 176 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
173 177
174 Settings::values.vibration_enabled = ui->vibrationGroup->isChecked(); 178 Settings::values.vibration_enabled = ui->vibrationGroup->isChecked();
179 Settings::values.motion_enabled = ui->motionGroup->isChecked();
175} 180}
176 181
177void ConfigureInput::changeEvent(QEvent* event) { 182void ConfigureInput::changeEvent(QEvent* event) {
@@ -191,6 +196,7 @@ void ConfigureInput::LoadConfiguration() {
191 UpdateDockedState(Settings::values.players[8].connected); 196 UpdateDockedState(Settings::values.players[8].connected);
192 197
193 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled); 198 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled);
199 ui->motionGroup->setChecked(Settings::values.motion_enabled);
194} 200}
195 201
196void ConfigureInput::LoadPlayerControllerIndices() { 202void ConfigureInput::LoadPlayerControllerIndices() {
@@ -217,6 +223,7 @@ void ConfigureInput::RestoreDefaults() {
217 ui->radioDocked->setChecked(true); 223 ui->radioDocked->setChecked(true);
218 ui->radioUndocked->setChecked(false); 224 ui->radioUndocked->setChecked(false);
219 ui->vibrationGroup->setChecked(true); 225 ui->vibrationGroup->setChecked(true);
226 ui->motionGroup->setChecked(true);
220} 227}
221 228
222void ConfigureInput::UpdateDockedState(bool is_handheld) { 229void ConfigureInput::UpdateDockedState(bool is_handheld) {
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index a53578837..9a61a6e15 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -18,6 +18,7 @@
18#include "core/hle/service/sm/sm.h" 18#include "core/hle/service/sm/sm.h"
19#include "input_common/gcadapter/gc_poller.h" 19#include "input_common/gcadapter/gc_poller.h"
20#include "input_common/main.h" 20#include "input_common/main.h"
21#include "input_common/udp/udp.h"
21#include "ui_configure_input_player.h" 22#include "ui_configure_input_player.h"
22#include "yuzu/configuration/config.h" 23#include "yuzu/configuration/config.h"
23#include "yuzu/configuration/configure_input_player.h" 24#include "yuzu/configuration/configure_input_player.h"
@@ -149,6 +150,14 @@ QString ButtonToText(const Common::ParamPackage& param) {
149 return GetKeyName(param.Get("code", 0)); 150 return GetKeyName(param.Get("code", 0));
150 } 151 }
151 152
153 if (param.Get("engine", "") == "cemuhookudp") {
154 if (param.Has("pad_index")) {
155 const QString motion_str = QString::fromStdString(param.Get("pad_index", ""));
156 return QObject::tr("Motion %1").arg(motion_str);
157 }
158 return GetKeyName(param.Get("code", 0));
159 }
160
152 if (param.Get("engine", "") == "sdl") { 161 if (param.Get("engine", "") == "sdl") {
153 if (param.Has("hat")) { 162 if (param.Has("hat")) {
154 const QString hat_str = QString::fromStdString(param.Get("hat", "")); 163 const QString hat_str = QString::fromStdString(param.Get("hat", ""));
@@ -262,6 +271,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
262 }, 271 },
263 }}; 272 }};
264 273
274 motion_map = {
275 ui->buttonMotionLeft,
276 ui->buttonMotionRight,
277 };
278
265 analog_map_deadzone_label = {ui->labelLStickDeadzone, ui->labelRStickDeadzone}; 279 analog_map_deadzone_label = {ui->labelLStickDeadzone, ui->labelRStickDeadzone};
266 analog_map_deadzone_slider = {ui->sliderLStickDeadzone, ui->sliderRStickDeadzone}; 280 analog_map_deadzone_slider = {ui->sliderLStickDeadzone, ui->sliderRStickDeadzone};
267 analog_map_modifier_groupbox = {ui->buttonLStickModGroup, ui->buttonRStickModGroup}; 281 analog_map_modifier_groupbox = {ui->buttonLStickModGroup, ui->buttonRStickModGroup};
@@ -304,6 +318,32 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
304 Config::default_buttons[button_id]); 318 Config::default_buttons[button_id]);
305 } 319 }
306 320
321 for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
322 auto* const button = motion_map[motion_id];
323 if (button == nullptr) {
324 continue;
325 }
326
327 button->setContextMenuPolicy(Qt::CustomContextMenu);
328 connect(button, &QPushButton::clicked, [=, this] {
329 HandleClick(
330 motion_map[motion_id],
331 [=, this](Common::ParamPackage params) {
332 motions_param[motion_id] = std::move(params);
333 },
334 InputCommon::Polling::DeviceType::Motion);
335 });
336 connect(button, &QPushButton::customContextMenuRequested,
337 [=, this](const QPoint& menu_location) {
338 QMenu context_menu;
339 context_menu.addAction(tr("Clear"), [&] {
340 motions_param[motion_id].Clear();
341 motion_map[motion_id]->setText(tr("[not set]"));
342 });
343 context_menu.exec(motion_map[motion_id]->mapToGlobal(menu_location));
344 });
345 }
346
307 // Handle clicks for the modifier buttons as well. 347 // Handle clicks for the modifier buttons as well.
308 ConfigureButtonClick(ui->buttonLStickMod, &lstick_mod, Config::default_stick_mod[0]); 348 ConfigureButtonClick(ui->buttonLStickMod, &lstick_mod, Config::default_stick_mod[0]);
309 ConfigureButtonClick(ui->buttonRStickMod, &rstick_mod, Config::default_stick_mod[1]); 349 ConfigureButtonClick(ui->buttonRStickMod, &rstick_mod, Config::default_stick_mod[1]);
@@ -385,9 +425,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
385 425
386 UpdateControllerIcon(); 426 UpdateControllerIcon();
387 UpdateControllerAvailableButtons(); 427 UpdateControllerAvailableButtons();
428 UpdateMotionButtons();
388 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [this](int) { 429 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [this](int) {
389 UpdateControllerIcon(); 430 UpdateControllerIcon();
390 UpdateControllerAvailableButtons(); 431 UpdateControllerAvailableButtons();
432 UpdateMotionButtons();
391 }); 433 });
392 434
393 connect(ui->comboDevices, qOverload<int>(&QComboBox::currentIndexChanged), this, 435 connect(ui->comboDevices, qOverload<int>(&QComboBox::currentIndexChanged), this,
@@ -417,6 +459,13 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
417 return; 459 return;
418 } 460 }
419 } 461 }
462 if (input_subsystem->GetUDPMotions()->IsPolling()) {
463 params = input_subsystem->GetUDPMotions()->GetNextInput();
464 if (params.Has("engine")) {
465 SetPollingResult(params, false);
466 return;
467 }
468 }
420 for (auto& poller : device_pollers) { 469 for (auto& poller : device_pollers) {
421 params = poller->GetNextInput(); 470 params = poller->GetNextInput();
422 if (params.Has("engine")) { 471 if (params.Has("engine")) {
@@ -448,6 +497,10 @@ void ConfigureInputPlayer::ApplyConfiguration() {
448 return; 497 return;
449 } 498 }
450 499
500 auto& motions = player.motions;
501 std::transform(motions_param.begin(), motions_param.end(), motions.begin(),
502 [](const Common::ParamPackage& param) { return param.Serialize(); });
503
451 player.controller_type = 504 player.controller_type =
452 static_cast<Settings::ControllerType>(ui->comboControllerType->currentIndex()); 505 static_cast<Settings::ControllerType>(ui->comboControllerType->currentIndex());
453 player.connected = ui->groupConnectedController->isChecked(); 506 player.connected = ui->groupConnectedController->isChecked();
@@ -501,6 +554,8 @@ void ConfigureInputPlayer::LoadConfiguration() {
501 [](const std::string& str) { return Common::ParamPackage(str); }); 554 [](const std::string& str) { return Common::ParamPackage(str); });
502 std::transform(player.analogs.begin(), player.analogs.end(), analogs_param.begin(), 555 std::transform(player.analogs.begin(), player.analogs.end(), analogs_param.begin(),
503 [](const std::string& str) { return Common::ParamPackage(str); }); 556 [](const std::string& str) { return Common::ParamPackage(str); });
557 std::transform(player.motions.begin(), player.motions.end(), motions_param.begin(),
558 [](const std::string& str) { return Common::ParamPackage(str); });
504 } 559 }
505 560
506 UpdateUI(); 561 UpdateUI();
@@ -544,6 +599,12 @@ void ConfigureInputPlayer::RestoreDefaults() {
544 SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]); 599 SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
545 } 600 }
546 } 601 }
602
603 for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
604 motions_param[motion_id] = Common::ParamPackage{
605 InputCommon::GenerateKeyboardParam(Config::default_motions[motion_id])};
606 }
607
547 UpdateUI(); 608 UpdateUI();
548 UpdateInputDevices(); 609 UpdateInputDevices();
549 ui->comboControllerType->setCurrentIndex(0); 610 ui->comboControllerType->setCurrentIndex(0);
@@ -573,6 +634,15 @@ void ConfigureInputPlayer::ClearAll() {
573 } 634 }
574 } 635 }
575 636
637 for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
638 const auto* const button = motion_map[motion_id];
639 if (button == nullptr || !button->isEnabled()) {
640 continue;
641 }
642
643 motions_param[motion_id].Clear();
644 }
645
576 UpdateUI(); 646 UpdateUI();
577 UpdateInputDevices(); 647 UpdateInputDevices();
578} 648}
@@ -582,6 +652,10 @@ void ConfigureInputPlayer::UpdateUI() {
582 button_map[button]->setText(ButtonToText(buttons_param[button])); 652 button_map[button]->setText(ButtonToText(buttons_param[button]));
583 } 653 }
584 654
655 for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
656 motion_map[motion_id]->setText(ButtonToText(motions_param[motion_id]));
657 }
658
585 ui->buttonLStickMod->setText(ButtonToText(lstick_mod)); 659 ui->buttonLStickMod->setText(ButtonToText(lstick_mod));
586 ui->buttonRStickMod->setText(ButtonToText(rstick_mod)); 660 ui->buttonRStickMod->setText(ButtonToText(rstick_mod));
587 661
@@ -659,7 +733,11 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
659void ConfigureInputPlayer::HandleClick( 733void ConfigureInputPlayer::HandleClick(
660 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter, 734 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
661 InputCommon::Polling::DeviceType type) { 735 InputCommon::Polling::DeviceType type) {
662 button->setText(tr("[waiting]")); 736 if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) {
737 button->setText(tr("Shake!"));
738 } else {
739 button->setText(tr("[waiting]"));
740 }
663 button->setFocus(); 741 button->setFocus();
664 742
665 // The first two input devices are always Any and Keyboard/Mouse. If the user filtered to a 743 // The first two input devices are always Any and Keyboard/Mouse. If the user filtered to a
@@ -683,6 +761,10 @@ void ConfigureInputPlayer::HandleClick(
683 input_subsystem->GetGCAnalogs()->BeginConfiguration(); 761 input_subsystem->GetGCAnalogs()->BeginConfiguration();
684 } 762 }
685 763
764 if (type == InputCommon::Polling::DeviceType::Motion) {
765 input_subsystem->GetUDPMotions()->BeginConfiguration();
766 }
767
686 timeout_timer->start(2500); // Cancel after 2.5 seconds 768 timeout_timer->start(2500); // Cancel after 2.5 seconds
687 poll_timer->start(50); // Check for new inputs every 50ms 769 poll_timer->start(50); // Check for new inputs every 50ms
688} 770}
@@ -700,6 +782,8 @@ void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params,
700 input_subsystem->GetGCButtons()->EndConfiguration(); 782 input_subsystem->GetGCButtons()->EndConfiguration();
701 input_subsystem->GetGCAnalogs()->EndConfiguration(); 783 input_subsystem->GetGCAnalogs()->EndConfiguration();
702 784
785 input_subsystem->GetUDPMotions()->EndConfiguration();
786
703 if (!abort) { 787 if (!abort) {
704 (*input_setter)(params); 788 (*input_setter)(params);
705 } 789 }
@@ -832,6 +916,37 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
832 } 916 }
833} 917}
834 918
919void ConfigureInputPlayer::UpdateMotionButtons() {
920 if (debug) {
921 // Motion isn't used with the debug controller, hide both groupboxes.
922 ui->buttonMotionLeftGroup->hide();
923 ui->buttonMotionRightGroup->hide();
924 return;
925 }
926
927 // Show/hide the "Motion 1/2" groupboxes depending on the currently selected controller.
928 switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
929 case Settings::ControllerType::ProController:
930 case Settings::ControllerType::LeftJoycon:
931 case Settings::ControllerType::Handheld:
932 // Show "Motion 1" and hide "Motion 2".
933 ui->buttonMotionLeftGroup->show();
934 ui->buttonMotionRightGroup->hide();
935 break;
936 case Settings::ControllerType::RightJoycon:
937 // Show "Motion 2" and hide "Motion 1".
938 ui->buttonMotionLeftGroup->hide();
939 ui->buttonMotionRightGroup->show();
940 break;
941 case Settings::ControllerType::DualJoyconDetached:
942 default:
943 // Show both "Motion 1/2".
944 ui->buttonMotionLeftGroup->show();
945 ui->buttonMotionRightGroup->show();
946 break;
947 }
948}
949
835void ConfigureInputPlayer::showEvent(QShowEvent* event) { 950void ConfigureInputPlayer::showEvent(QShowEvent* event) {
836 if (bottom_row == nullptr) { 951 if (bottom_row == nullptr) {
837 return; 952 return;
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index a25bc3bd9..b343f2c1d 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -107,6 +107,9 @@ private:
107 /// Hides and disables controller settings based on the current controller type. 107 /// Hides and disables controller settings based on the current controller type.
108 void UpdateControllerAvailableButtons(); 108 void UpdateControllerAvailableButtons();
109 109
110 /// Shows or hides motion groupboxes based on the current controller type.
111 void UpdateMotionButtons();
112
110 /// Gets the default controller mapping for this device and auto configures the input to match. 113 /// Gets the default controller mapping for this device and auto configures the input to match.
111 void UpdateMappingWithDefaults(); 114 void UpdateMappingWithDefaults();
112 115
@@ -128,11 +131,14 @@ private:
128 131
129 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param; 132 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
130 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param; 133 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
134 std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions> motions_param;
131 135
132 static constexpr int ANALOG_SUB_BUTTONS_NUM = 4; 136 static constexpr int ANALOG_SUB_BUTTONS_NUM = 4;
133 137
134 /// Each button input is represented by a QPushButton. 138 /// Each button input is represented by a QPushButton.
135 std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map; 139 std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
140 /// Each motion input is represented by a QPushButton.
141 std::array<QPushButton*, Settings::NativeMotion::NumMotions> motion_map;
136 /// Extra buttons for the modifiers. 142 /// Extra buttons for the modifiers.
137 Common::ParamPackage lstick_mod; 143 Common::ParamPackage lstick_mod;
138 Common::ParamPackage rstick_mod; 144 Common::ParamPackage rstick_mod;
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
index 9bc681894..e03461d9d 100644
--- a/src/yuzu/configuration/configure_input_player.ui
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -1983,6 +1983,9 @@
1983 <property name="spacing"> 1983 <property name="spacing">
1984 <number>3</number> 1984 <number>3</number>
1985 </property> 1985 </property>
1986 <property name="topMargin">
1987 <number>0</number>
1988 </property>
1986 <item> 1989 <item>
1987 <spacer name="horizontalSpacerMiscButtons1"> 1990 <spacer name="horizontalSpacerMiscButtons1">
1988 <property name="orientation"> 1991 <property name="orientation">
@@ -1990,21 +1993,119 @@
1990 </property> 1993 </property>
1991 <property name="sizeHint" stdset="0"> 1994 <property name="sizeHint" stdset="0">
1992 <size> 1995 <size>
1993 <width>40</width> 1996 <width>20</width>
1994 <height>0</height> 1997 <height>20</height>
1995 </size> 1998 </size>
1996 </property> 1999 </property>
1997 </spacer> 2000 </spacer>
1998 </item> 2001 </item>
1999 <item> 2002 <item>
2003 <widget class="QGroupBox" name="buttonMotionLeftGroup">
2004 <property name="title">
2005 <string>Motion 1</string>
2006 </property>
2007 <property name="alignment">
2008 <set>Qt::AlignCenter</set>
2009 </property>
2010 <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout_2">
2011 <property name="spacing">
2012 <number>3</number>
2013 </property>
2014 <property name="leftMargin">
2015 <number>3</number>
2016 </property>
2017 <property name="topMargin">
2018 <number>3</number>
2019 </property>
2020 <property name="rightMargin">
2021 <number>3</number>
2022 </property>
2023 <property name="bottomMargin">
2024 <number>3</number>
2025 </property>
2026 <item>
2027 <widget class="QPushButton" name="buttonMotionLeft">
2028 <property name="minimumSize">
2029 <size>
2030 <width>57</width>
2031 <height>0</height>
2032 </size>
2033 </property>
2034 <property name="maximumSize">
2035 <size>
2036 <width>55</width>
2037 <height>16777215</height>
2038 </size>
2039 </property>
2040 <property name="styleSheet">
2041 <string notr="true">min-width: 55px;</string>
2042 </property>
2043 <property name="text">
2044 <string>Left</string>
2045 </property>
2046 </widget>
2047 </item>
2048 </layout>
2049 </widget>
2050 </item>
2051 <item>
2052 <widget class="QGroupBox" name="buttonMotionRightGroup">
2053 <property name="title">
2054 <string>Motion 2</string>
2055 </property>
2056 <property name="alignment">
2057 <set>Qt::AlignCenter</set>
2058 </property>
2059 <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout_2">
2060 <property name="spacing">
2061 <number>3</number>
2062 </property>
2063 <property name="leftMargin">
2064 <number>3</number>
2065 </property>
2066 <property name="topMargin">
2067 <number>3</number>
2068 </property>
2069 <property name="rightMargin">
2070 <number>3</number>
2071 </property>
2072 <property name="bottomMargin">
2073 <number>3</number>
2074 </property>
2075 <item>
2076 <widget class="QPushButton" name="buttonMotionRight">
2077 <property name="minimumSize">
2078 <size>
2079 <width>57</width>
2080 <height>0</height>
2081 </size>
2082 </property>
2083 <property name="maximumSize">
2084 <size>
2085 <width>55</width>
2086 <height>16777215</height>
2087 </size>
2088 </property>
2089 <property name="styleSheet">
2090 <string notr="true">min-width: 55px;</string>
2091 </property>
2092 <property name="text">
2093 <string>Right</string>
2094 </property>
2095 </widget>
2096 </item>
2097 </layout>
2098 </widget>
2099 </item>
2100 <item>
2000 <spacer name="horizontalSpacerMiscButtons4"> 2101 <spacer name="horizontalSpacerMiscButtons4">
2001 <property name="orientation"> 2102 <property name="orientation">
2002 <enum>Qt::Horizontal</enum> 2103 <enum>Qt::Horizontal</enum>
2003 </property> 2104 </property>
2004 <property name="sizeHint" stdset="0"> 2105 <property name="sizeHint" stdset="0">
2005 <size> 2106 <size>
2006 <width>40</width> 2107 <width>20</width>
2007 <height>0</height> 2108 <height>20</height>
2008 </size> 2109 </size>
2009 </property> 2110 </property>
2010 </spacer> 2111 </spacer>
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index e9f1c6500..23448e747 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -290,6 +290,8 @@ void Config::ReadValues() {
290 290
291 Settings::values.vibration_enabled = 291 Settings::values.vibration_enabled =
292 sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true); 292 sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true);
293 Settings::values.motion_enabled =
294 sdl2_config->GetBoolean("ControlsGeneral", "motion_enabled", true);
293 Settings::values.touchscreen.enabled = 295 Settings::values.touchscreen.enabled =
294 sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true); 296 sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true);
295 Settings::values.touchscreen.device = 297 Settings::values.touchscreen.device =
diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp
index aaf59129a..bc273fb51 100644
--- a/src/yuzu_tester/config.cpp
+++ b/src/yuzu_tester/config.cpp
@@ -76,6 +76,7 @@ void Config::ReadValues() {
76 } 76 }
77 77
78 Settings::values.vibration_enabled = true; 78 Settings::values.vibration_enabled = true;
79 Settings::values.motion_enabled = true;
79 Settings::values.touchscreen.enabled = ""; 80 Settings::values.touchscreen.enabled = "";
80 Settings::values.touchscreen.device = ""; 81 Settings::values.touchscreen.device = "";
81 Settings::values.touchscreen.finger = 0; 82 Settings::values.touchscreen.finger = 0;