mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	Artic Base: Add Artic Controller support (#195)
This commit is contained in:
		
							parent
							
								
									9de19ff7a1
								
							
						
					
					
						commit
						55748d7d1a
					
				
					 24 changed files with 741 additions and 158 deletions
				
			
		|  | @ -20,6 +20,8 @@ | |||
| #include "core/hle/service/hid/hid.h" | ||||
| #include "core/hle/service/hid/hid_spvr.h" | ||||
| #include "core/hle/service/hid/hid_user.h" | ||||
| #include "core/hle/service/ir/ir_rst.h" | ||||
| #include "core/hle/service/ir/ir_user.h" | ||||
| #include "core/hle/service/service.h" | ||||
| #include "core/movie.h" | ||||
| 
 | ||||
|  | @ -53,6 +55,32 @@ void Module::serialize(Archive& ar, const unsigned int file_version) { | |||
| } | ||||
| SERIALIZE_IMPL(Module) | ||||
| 
 | ||||
| ArticBaseController::ArticBaseController( | ||||
|     const std::shared_ptr<Network::ArticBase::Client>& client) { | ||||
| 
 | ||||
|     udp_stream = | ||||
|         client->NewUDPStream("ArticController", sizeof(ArticBaseController::ControllerData), | ||||
|                              std::chrono::milliseconds(2)); | ||||
|     if (udp_stream.get()) { | ||||
|         udp_stream->Start(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| ArticBaseController::ControllerData ArticBaseController::GetControllerData() { | ||||
| 
 | ||||
|     if (udp_stream.get() && udp_stream->IsReady()) { | ||||
|         auto data = udp_stream->GetLastPacket(); | ||||
|         if (data.size() == sizeof(ControllerData)) { | ||||
|             u32 id = *reinterpret_cast<u32*>(data.data()); | ||||
|             if ((id - last_packet_id) < (std::numeric_limits<u32>::max() / 2)) { | ||||
|                 last_packet_id = id; | ||||
|                 memcpy(&last_controller_data, data.data(), data.size()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return last_controller_data; | ||||
| } | ||||
| 
 | ||||
| constexpr float accelerometer_coef = 512.0f; // measured from hw test result
 | ||||
| constexpr float gyroscope_coef = 14.375f; // got from hwtest GetGyroscopeLowRawToDpsCoefficient call
 | ||||
| 
 | ||||
|  | @ -111,96 +139,151 @@ void Module::UpdatePadCallback(std::uintptr_t user_data, s64 cycles_late) { | |||
|         LoadInputDevices(); | ||||
| 
 | ||||
|     using namespace Settings::NativeButton; | ||||
|     state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|     state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|     state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|     state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|     state.right.Assign(buttons[Right - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|     state.left.Assign(buttons[Left - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|     state.up.Assign(buttons[Up - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|     state.down.Assign(buttons[Down - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|     state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|     state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|     state.start.Assign(buttons[Start - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|     state.select.Assign(buttons[Select - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|     state.debug.Assign(buttons[Debug - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|     state.gpio14.Assign(buttons[Gpio14 - BUTTON_HID_BEGIN]->GetStatus()); | ||||
| 
 | ||||
|     // Get current circle pad position and update circle pad direction
 | ||||
|     float circle_pad_x_f, circle_pad_y_f; | ||||
|     std::tie(circle_pad_x_f, circle_pad_y_f) = circle_pad->GetStatus(); | ||||
|     if (artic_controller.get() && artic_controller->IsReady()) { | ||||
|         constexpr u32 HID_VALID_KEYS = 0xF0003FFF; | ||||
|         constexpr u32 LIBCTRU_TOUCH_KEY = (1 << 20); | ||||
| 
 | ||||
|     // xperia64: 0x9A seems to be the calibrated limit of the circle pad
 | ||||
|     // Verified by using Input Redirector with very large-value digital inputs
 | ||||
|     // on the circle pad and calibrating using the system settings application
 | ||||
|     constexpr int MAX_CIRCLEPAD_POS = 0x9A; // Max value for a circle pad position
 | ||||
|         ArticBaseController::ControllerData data = artic_controller->GetControllerData(); | ||||
| 
 | ||||
|     // These are rounded rather than truncated on actual hardware
 | ||||
|     s16 circle_pad_new_x = static_cast<s16>(std::roundf(circle_pad_x_f * MAX_CIRCLEPAD_POS)); | ||||
|     s16 circle_pad_new_y = static_cast<s16>(std::roundf(circle_pad_y_f * MAX_CIRCLEPAD_POS)); | ||||
|     s16 circle_pad_x = | ||||
|         (circle_pad_new_x + std::accumulate(circle_pad_old_x.begin(), circle_pad_old_x.end(), 0)) / | ||||
|         CIRCLE_PAD_AVERAGING; | ||||
|     s16 circle_pad_y = | ||||
|         (circle_pad_new_y + std::accumulate(circle_pad_old_y.begin(), circle_pad_old_y.end(), 0)) / | ||||
|         CIRCLE_PAD_AVERAGING; | ||||
|     circle_pad_old_x.erase(circle_pad_old_x.begin()); | ||||
|     circle_pad_old_x.push_back(circle_pad_new_x); | ||||
|     circle_pad_old_y.erase(circle_pad_old_y.begin()); | ||||
|     circle_pad_old_y.push_back(circle_pad_new_y); | ||||
|         state.hex = data.pad & HID_VALID_KEYS; | ||||
| 
 | ||||
|     system.Movie().HandlePadAndCircleStatus(state, circle_pad_x, circle_pad_y); | ||||
|         s16 circle_pad_x = data.c_pad_x; | ||||
|         s16 circle_pad_y = data.c_pad_y; | ||||
| 
 | ||||
|     const DirectionState direction = GetStickDirectionState(circle_pad_x, circle_pad_y); | ||||
|     state.circle_up.Assign(direction.up); | ||||
|     state.circle_down.Assign(direction.down); | ||||
|     state.circle_left.Assign(direction.left); | ||||
|     state.circle_right.Assign(direction.right); | ||||
|         system.Movie().HandlePadAndCircleStatus(state, circle_pad_x, circle_pad_y); | ||||
| 
 | ||||
|     mem->pad.current_state.hex = state.hex; | ||||
|     mem->pad.index = next_pad_index; | ||||
|     next_pad_index = (next_pad_index + 1) % mem->pad.entries.size(); | ||||
|         mem->pad.current_state.hex = state.hex; | ||||
|         mem->pad.index = next_pad_index; | ||||
|         next_pad_index = (next_pad_index + 1) % mem->pad.entries.size(); | ||||
| 
 | ||||
|     // Get the previous Pad state
 | ||||
|     u32 last_entry_index = (mem->pad.index - 1) % mem->pad.entries.size(); | ||||
|     PadState old_state = mem->pad.entries[last_entry_index].current_state; | ||||
|         // Get the previous Pad state
 | ||||
|         u32 last_entry_index = (mem->pad.index - 1) % mem->pad.entries.size(); | ||||
|         PadState old_state = mem->pad.entries[last_entry_index].current_state; | ||||
| 
 | ||||
|     // Compute bitmask with 1s for bits different from the old state
 | ||||
|     PadState changed = {{(state.hex ^ old_state.hex)}}; | ||||
|         // Compute bitmask with 1s for bits different from the old state
 | ||||
|         PadState changed = {{(state.hex ^ old_state.hex)}}; | ||||
| 
 | ||||
|     // Get the current Pad entry
 | ||||
|     PadDataEntry& pad_entry = mem->pad.entries[mem->pad.index]; | ||||
|         // Get the current Pad entry
 | ||||
|         PadDataEntry& pad_entry = mem->pad.entries[mem->pad.index]; | ||||
| 
 | ||||
|     // Update entry properties
 | ||||
|     pad_entry.current_state.hex = state.hex; | ||||
|     pad_entry.delta_additions.hex = changed.hex & state.hex; | ||||
|     pad_entry.delta_removals.hex = changed.hex & old_state.hex; | ||||
|     pad_entry.circle_pad_x = circle_pad_x; | ||||
|     pad_entry.circle_pad_y = circle_pad_y; | ||||
|         // Update entry properties
 | ||||
|         pad_entry.current_state.hex = state.hex; | ||||
|         pad_entry.delta_additions.hex = changed.hex & state.hex; | ||||
|         pad_entry.delta_removals.hex = changed.hex & old_state.hex; | ||||
|         pad_entry.circle_pad_x = circle_pad_x; | ||||
|         pad_entry.circle_pad_y = circle_pad_y; | ||||
| 
 | ||||
|     // If we just updated index 0, provide a new timestamp
 | ||||
|     if (mem->pad.index == 0) { | ||||
|         mem->pad.index_reset_ticks_previous = mem->pad.index_reset_ticks; | ||||
|         mem->pad.index_reset_ticks = (s64)system.CoreTiming().GetTicks(); | ||||
|         // If we just updated index 0, provide a new timestamp
 | ||||
|         if (mem->pad.index == 0) { | ||||
|             mem->pad.index_reset_ticks_previous = mem->pad.index_reset_ticks; | ||||
|             mem->pad.index_reset_ticks = (s64)system.CoreTiming().GetTicks(); | ||||
|         } | ||||
| 
 | ||||
|         mem->touch.index = next_touch_index; | ||||
|         next_touch_index = (next_touch_index + 1) % mem->touch.entries.size(); | ||||
| 
 | ||||
|         // Get the current touch entry
 | ||||
|         TouchDataEntry& touch_entry = mem->touch.entries[mem->touch.index]; | ||||
|         bool pressed = (data.pad & LIBCTRU_TOUCH_KEY) != 0; | ||||
| 
 | ||||
|         touch_entry.x = static_cast<u16>(data.touch_x); | ||||
|         touch_entry.y = static_cast<u16>(data.touch_y); | ||||
|         touch_entry.valid.Assign(pressed ? 1 : 0); | ||||
| 
 | ||||
|         system.Movie().HandleTouchStatus(touch_entry); | ||||
|     } else { | ||||
|         state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|         state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|         state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|         state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|         state.right.Assign(buttons[Right - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|         state.left.Assign(buttons[Left - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|         state.up.Assign(buttons[Up - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|         state.down.Assign(buttons[Down - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|         state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|         state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|         state.start.Assign(buttons[Start - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|         state.select.Assign(buttons[Select - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|         state.debug.Assign(buttons[Debug - BUTTON_HID_BEGIN]->GetStatus()); | ||||
|         state.gpio14.Assign(buttons[Gpio14 - BUTTON_HID_BEGIN]->GetStatus()); | ||||
| 
 | ||||
|         // Get current circle pad position and update circle pad direction
 | ||||
|         float circle_pad_x_f, circle_pad_y_f; | ||||
|         std::tie(circle_pad_x_f, circle_pad_y_f) = circle_pad->GetStatus(); | ||||
| 
 | ||||
|         // xperia64: 0x9A seems to be the calibrated limit of the circle pad
 | ||||
|         // Verified by using Input Redirector with very large-value digital inputs
 | ||||
|         // on the circle pad and calibrating using the system settings application
 | ||||
|         constexpr int MAX_CIRCLEPAD_POS = 0x9A; // Max value for a circle pad position
 | ||||
| 
 | ||||
|         // These are rounded rather than truncated on actual hardware
 | ||||
|         s16 circle_pad_new_x = static_cast<s16>(std::roundf(circle_pad_x_f * MAX_CIRCLEPAD_POS)); | ||||
|         s16 circle_pad_new_y = static_cast<s16>(std::roundf(circle_pad_y_f * MAX_CIRCLEPAD_POS)); | ||||
|         s16 circle_pad_x = (circle_pad_new_x + | ||||
|                             std::accumulate(circle_pad_old_x.begin(), circle_pad_old_x.end(), 0)) / | ||||
|                            CIRCLE_PAD_AVERAGING; | ||||
|         s16 circle_pad_y = (circle_pad_new_y + | ||||
|                             std::accumulate(circle_pad_old_y.begin(), circle_pad_old_y.end(), 0)) / | ||||
|                            CIRCLE_PAD_AVERAGING; | ||||
|         circle_pad_old_x.erase(circle_pad_old_x.begin()); | ||||
|         circle_pad_old_x.push_back(circle_pad_new_x); | ||||
|         circle_pad_old_y.erase(circle_pad_old_y.begin()); | ||||
|         circle_pad_old_y.push_back(circle_pad_new_y); | ||||
| 
 | ||||
|         system.Movie().HandlePadAndCircleStatus(state, circle_pad_x, circle_pad_y); | ||||
| 
 | ||||
|         const DirectionState direction = GetStickDirectionState(circle_pad_x, circle_pad_y); | ||||
|         state.circle_up.Assign(direction.up); | ||||
|         state.circle_down.Assign(direction.down); | ||||
|         state.circle_left.Assign(direction.left); | ||||
|         state.circle_right.Assign(direction.right); | ||||
| 
 | ||||
|         mem->pad.current_state.hex = state.hex; | ||||
|         mem->pad.index = next_pad_index; | ||||
|         next_pad_index = (next_pad_index + 1) % mem->pad.entries.size(); | ||||
| 
 | ||||
|         // Get the previous Pad state
 | ||||
|         u32 last_entry_index = (mem->pad.index - 1) % mem->pad.entries.size(); | ||||
|         PadState old_state = mem->pad.entries[last_entry_index].current_state; | ||||
| 
 | ||||
|         // Compute bitmask with 1s for bits different from the old state
 | ||||
|         PadState changed = {{(state.hex ^ old_state.hex)}}; | ||||
| 
 | ||||
|         // Get the current Pad entry
 | ||||
|         PadDataEntry& pad_entry = mem->pad.entries[mem->pad.index]; | ||||
| 
 | ||||
|         // Update entry properties
 | ||||
|         pad_entry.current_state.hex = state.hex; | ||||
|         pad_entry.delta_additions.hex = changed.hex & state.hex; | ||||
|         pad_entry.delta_removals.hex = changed.hex & old_state.hex; | ||||
|         pad_entry.circle_pad_x = circle_pad_x; | ||||
|         pad_entry.circle_pad_y = circle_pad_y; | ||||
| 
 | ||||
|         // If we just updated index 0, provide a new timestamp
 | ||||
|         if (mem->pad.index == 0) { | ||||
|             mem->pad.index_reset_ticks_previous = mem->pad.index_reset_ticks; | ||||
|             mem->pad.index_reset_ticks = (s64)system.CoreTiming().GetTicks(); | ||||
|         } | ||||
| 
 | ||||
|         mem->touch.index = next_touch_index; | ||||
|         next_touch_index = (next_touch_index + 1) % mem->touch.entries.size(); | ||||
| 
 | ||||
|         // Get the current touch entry
 | ||||
|         TouchDataEntry& touch_entry = mem->touch.entries[mem->touch.index]; | ||||
|         bool pressed = false; | ||||
|         float x, y; | ||||
|         std::tie(x, y, pressed) = touch_device->GetStatus(); | ||||
|         if (!pressed && touch_btn_device) { | ||||
|             std::tie(x, y, pressed) = touch_btn_device->GetStatus(); | ||||
|         } | ||||
|         touch_entry.x = static_cast<u16>(x * Core::kScreenBottomWidth); | ||||
|         touch_entry.y = static_cast<u16>(y * Core::kScreenBottomHeight); | ||||
|         touch_entry.valid.Assign(pressed ? 1 : 0); | ||||
| 
 | ||||
|         system.Movie().HandleTouchStatus(touch_entry); | ||||
|     } | ||||
| 
 | ||||
|     mem->touch.index = next_touch_index; | ||||
|     next_touch_index = (next_touch_index + 1) % mem->touch.entries.size(); | ||||
| 
 | ||||
|     // Get the current touch entry
 | ||||
|     TouchDataEntry& touch_entry = mem->touch.entries[mem->touch.index]; | ||||
|     bool pressed = false; | ||||
|     float x, y; | ||||
|     std::tie(x, y, pressed) = touch_device->GetStatus(); | ||||
|     if (!pressed && touch_btn_device) { | ||||
|         std::tie(x, y, pressed) = touch_btn_device->GetStatus(); | ||||
|     } | ||||
|     touch_entry.x = static_cast<u16>(x * Core::kScreenBottomWidth); | ||||
|     touch_entry.y = static_cast<u16>(y * Core::kScreenBottomHeight); | ||||
|     touch_entry.valid.Assign(pressed ? 1 : 0); | ||||
| 
 | ||||
|     system.Movie().HandleTouchStatus(touch_entry); | ||||
| 
 | ||||
|     // TODO(bunnei): We're not doing anything with offset 0xA8 + 0x18 of HID SharedMemory, which
 | ||||
|     // supposedly is "Touch-screen entry, which contains the raw coordinate data prior to being
 | ||||
|     // converted to pixel coordinates." (http://3dbrew.org/wiki/HID_Shared_Memory#Offset_0xA8).
 | ||||
|  | @ -231,19 +314,27 @@ void Module::UpdateAccelerometerCallback(std::uintptr_t user_data, s64 cycles_la | |||
|     mem->accelerometer.index = next_accelerometer_index; | ||||
|     next_accelerometer_index = (next_accelerometer_index + 1) % mem->accelerometer.entries.size(); | ||||
| 
 | ||||
|     Common::Vec3<float> accel; | ||||
|     std::tie(accel, std::ignore) = motion_device->GetStatus(); | ||||
|     accel *= accelerometer_coef; | ||||
|     // TODO(wwylele): do a time stretch like the one in UpdateGyroscopeCallback
 | ||||
|     // The time stretch formula should be like
 | ||||
|     // stretched_vector = (raw_vector - gravity) * stretch_ratio + gravity
 | ||||
| 
 | ||||
|     AccelerometerDataEntry& accelerometer_entry = | ||||
|         mem->accelerometer.entries[mem->accelerometer.index]; | ||||
| 
 | ||||
|     accelerometer_entry.x = static_cast<s16>(accel.x); | ||||
|     accelerometer_entry.y = static_cast<s16>(accel.y); | ||||
|     accelerometer_entry.z = static_cast<s16>(accel.z); | ||||
|     if (artic_controller.get() && artic_controller->IsReady()) { | ||||
|         ArticBaseController::ControllerData data = artic_controller->GetControllerData(); | ||||
| 
 | ||||
|         accelerometer_entry.x = data.accel_x; | ||||
|         accelerometer_entry.y = data.accel_y; | ||||
|         accelerometer_entry.z = data.accel_z; | ||||
|     } else { | ||||
|         Common::Vec3<float> accel; | ||||
|         std::tie(accel, std::ignore) = motion_device->GetStatus(); | ||||
|         accel *= accelerometer_coef; | ||||
|         // TODO(wwylele): do a time stretch like the one in UpdateGyroscopeCallback
 | ||||
|         // The time stretch formula should be like
 | ||||
|         // stretched_vector = (raw_vector - gravity) * stretch_ratio + gravity
 | ||||
| 
 | ||||
|         accelerometer_entry.x = static_cast<s16>(accel.x); | ||||
|         accelerometer_entry.y = static_cast<s16>(accel.y); | ||||
|         accelerometer_entry.z = static_cast<s16>(accel.z); | ||||
|     } | ||||
| 
 | ||||
|     system.Movie().HandleAccelerometerStatus(accelerometer_entry); | ||||
| 
 | ||||
|  | @ -278,13 +369,21 @@ void Module::UpdateGyroscopeCallback(std::uintptr_t user_data, s64 cycles_late) | |||
| 
 | ||||
|     GyroscopeDataEntry& gyroscope_entry = mem->gyroscope.entries[mem->gyroscope.index]; | ||||
| 
 | ||||
|     Common::Vec3<float> gyro; | ||||
|     std::tie(std::ignore, gyro) = motion_device->GetStatus(); | ||||
|     double stretch = system.perf_stats->GetLastFrameTimeScale(); | ||||
|     gyro *= gyroscope_coef * static_cast<float>(stretch); | ||||
|     gyroscope_entry.x = static_cast<s16>(gyro.x); | ||||
|     gyroscope_entry.y = static_cast<s16>(gyro.y); | ||||
|     gyroscope_entry.z = static_cast<s16>(gyro.z); | ||||
|     if (artic_controller.get() && artic_controller->IsReady()) { | ||||
|         ArticBaseController::ControllerData data = artic_controller->GetControllerData(); | ||||
| 
 | ||||
|         gyroscope_entry.x = data.gyro_x; | ||||
|         gyroscope_entry.y = data.gyro_y; | ||||
|         gyroscope_entry.z = data.gyro_z; | ||||
|     } else { | ||||
|         Common::Vec3<float> gyro; | ||||
|         std::tie(std::ignore, gyro) = motion_device->GetStatus(); | ||||
|         double stretch = system.perf_stats->GetLastFrameTimeScale(); | ||||
|         gyro *= gyroscope_coef * static_cast<float>(stretch); | ||||
|         gyroscope_entry.x = static_cast<s16>(gyro.x); | ||||
|         gyroscope_entry.y = static_cast<s16>(gyro.y); | ||||
|         gyroscope_entry.z = static_cast<s16>(gyro.z); | ||||
|     } | ||||
| 
 | ||||
|     system.Movie().HandleGyroscopeStatus(gyroscope_entry); | ||||
| 
 | ||||
|  | @ -316,6 +415,23 @@ void Module::Interface::GetIPCHandles(Kernel::HLERequestContext& ctx) { | |||
| void Module::Interface::EnableAccelerometer(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
| 
 | ||||
|     auto& artic_client = GetModule()->artic_client; | ||||
|     if (artic_client.get()) { | ||||
|         auto req = artic_client->NewRequest("HIDUSER_EnableAccelerometer"); | ||||
| 
 | ||||
|         auto resp = artic_client->Send(req); | ||||
| 
 | ||||
|         if (!resp.has_value()) { | ||||
|             rb.Push(ResultUnknown); | ||||
|         } else { | ||||
|             rb.Push(Result{static_cast<u32>(resp->GetMethodResult())}); | ||||
|         } | ||||
|     } else { | ||||
|         rb.Push(ResultSuccess); | ||||
|     } | ||||
| 
 | ||||
|     ++hid->enable_accelerometer_count; | ||||
| 
 | ||||
|     // Schedules the accelerometer update event if the accelerometer was just enabled
 | ||||
|  | @ -324,15 +440,29 @@ void Module::Interface::EnableAccelerometer(Kernel::HLERequestContext& ctx) { | |||
|                                                hid->accelerometer_update_event); | ||||
|     } | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(ResultSuccess); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_HID, "called"); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::DisableAccelerometer(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
| 
 | ||||
|     auto& artic_client = GetModule()->artic_client; | ||||
|     if (artic_client.get()) { | ||||
|         auto req = artic_client->NewRequest("HIDUSER_DisableAccelerometer"); | ||||
| 
 | ||||
|         auto resp = artic_client->Send(req); | ||||
| 
 | ||||
|         if (!resp.has_value()) { | ||||
|             rb.Push(ResultUnknown); | ||||
|         } else { | ||||
|             rb.Push(Result{static_cast<u32>(resp->GetMethodResult())}); | ||||
|         } | ||||
|     } else { | ||||
|         rb.Push(ResultSuccess); | ||||
|     } | ||||
| 
 | ||||
|     --hid->enable_accelerometer_count; | ||||
| 
 | ||||
|     // Unschedules the accelerometer update event if the accelerometer was just disabled
 | ||||
|  | @ -340,15 +470,29 @@ void Module::Interface::DisableAccelerometer(Kernel::HLERequestContext& ctx) { | |||
|         hid->system.CoreTiming().UnscheduleEvent(hid->accelerometer_update_event, 0); | ||||
|     } | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(ResultSuccess); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_HID, "called"); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::EnableGyroscopeLow(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
| 
 | ||||
|     auto& artic_client = GetModule()->artic_client; | ||||
|     if (artic_client.get()) { | ||||
|         auto req = artic_client->NewRequest("HIDUSER_EnableGyroscope"); | ||||
| 
 | ||||
|         auto resp = artic_client->Send(req); | ||||
| 
 | ||||
|         if (!resp.has_value()) { | ||||
|             rb.Push(ResultUnknown); | ||||
|         } else { | ||||
|             rb.Push(Result{static_cast<u32>(resp->GetMethodResult())}); | ||||
|         } | ||||
|     } else { | ||||
|         rb.Push(ResultSuccess); | ||||
|     } | ||||
| 
 | ||||
|     ++hid->enable_gyroscope_count; | ||||
| 
 | ||||
|     // Schedules the gyroscope update event if the gyroscope was just enabled
 | ||||
|  | @ -356,15 +500,29 @@ void Module::Interface::EnableGyroscopeLow(Kernel::HLERequestContext& ctx) { | |||
|         hid->system.CoreTiming().ScheduleEvent(gyroscope_update_ticks, hid->gyroscope_update_event); | ||||
|     } | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(ResultSuccess); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_HID, "called"); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::DisableGyroscopeLow(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
| 
 | ||||
|     auto& artic_client = GetModule()->artic_client; | ||||
|     if (artic_client.get()) { | ||||
|         auto req = artic_client->NewRequest("HIDUSER_DisableGyroscope"); | ||||
| 
 | ||||
|         auto resp = artic_client->Send(req); | ||||
| 
 | ||||
|         if (!resp.has_value()) { | ||||
|             rb.Push(ResultUnknown); | ||||
|         } else { | ||||
|             rb.Push(Result{static_cast<u32>(resp->GetMethodResult())}); | ||||
|         } | ||||
|     } else { | ||||
|         rb.Push(ResultSuccess); | ||||
|     } | ||||
| 
 | ||||
|     --hid->enable_gyroscope_count; | ||||
| 
 | ||||
|     // Unschedules the gyroscope update event if the gyroscope was just disabled
 | ||||
|  | @ -372,9 +530,6 @@ void Module::Interface::DisableGyroscopeLow(Kernel::HLERequestContext& ctx) { | |||
|         hid->system.CoreTiming().UnscheduleEvent(hid->gyroscope_update_event, 0); | ||||
|     } | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(ResultSuccess); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_HID, "called"); | ||||
| } | ||||
| 
 | ||||
|  | @ -382,25 +537,90 @@ void Module::Interface::GetGyroscopeLowRawToDpsCoefficient(Kernel::HLERequestCon | |||
|     IPC::RequestParser rp(ctx); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.Push(gyroscope_coef); | ||||
| 
 | ||||
|     auto& artic_client = GetModule()->artic_client; | ||||
|     if (artic_client.get()) { | ||||
|         auto req = artic_client->NewRequest("HIDUSER_GetGyroRawToDpsCoef"); | ||||
| 
 | ||||
|         auto resp = artic_client->Send(req); | ||||
| 
 | ||||
|         if (!resp.has_value()) { | ||||
|             rb.Push(ResultUnknown); | ||||
|             rb.Push(0.f); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         Result res = Result{static_cast<u32>(resp->GetMethodResult())}; | ||||
|         if (res.IsError()) { | ||||
|             rb.Push(res); | ||||
|             rb.Push(0.f); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         auto coef = resp->GetResponseFloat(0); | ||||
|         if (!coef.has_value()) { | ||||
|             rb.Push(ResultUnknown); | ||||
|             rb.Push(0.f); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         rb.Push(res); | ||||
|         rb.Push(*coef); | ||||
|     } else { | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push(gyroscope_coef); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::GetGyroscopeLowCalibrateParam(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(6, 0); | ||||
|     rb.Push(ResultSuccess); | ||||
| 
 | ||||
|     const s16 param_unit = 6700; // an approximate value taken from hw
 | ||||
|     GyroscopeCalibrateParam param = { | ||||
|         {0, param_unit, -param_unit}, | ||||
|         {0, param_unit, -param_unit}, | ||||
|         {0, param_unit, -param_unit}, | ||||
|     }; | ||||
|     rb.PushRaw(param); | ||||
|     auto& artic_client = GetModule()->artic_client; | ||||
|     if (artic_client.get()) { | ||||
|         GyroscopeCalibrateParam param; | ||||
| 
 | ||||
|     LOG_WARNING(Service_HID, "(STUBBED) called"); | ||||
|         auto req = artic_client->NewRequest("HIDUSER_GetGyroCalibrateParam"); | ||||
| 
 | ||||
|         auto resp = artic_client->Send(req); | ||||
| 
 | ||||
|         if (!resp.has_value()) { | ||||
|             rb.Push(ResultUnknown); | ||||
|             rb.PushRaw(param); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         Result res = Result{static_cast<u32>(resp->GetMethodResult())}; | ||||
|         if (res.IsError()) { | ||||
|             rb.Push(res); | ||||
|             rb.PushRaw(param); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         auto param_buf = resp->GetResponseBuffer(0); | ||||
|         if (!param_buf.has_value() || param_buf->second != sizeof(param)) { | ||||
|             rb.Push(ResultUnknown); | ||||
|             rb.PushRaw(param); | ||||
|             return; | ||||
|         } | ||||
|         memcpy(¶m, param_buf->first, sizeof(param)); | ||||
| 
 | ||||
|         rb.Push(res); | ||||
|         rb.PushRaw(param); | ||||
|     } else { | ||||
|         rb.Push(ResultSuccess); | ||||
| 
 | ||||
|         const s16 param_unit = 6700; // an approximate value taken from hw
 | ||||
|         GyroscopeCalibrateParam param = { | ||||
|             {0, param_unit, -param_unit}, | ||||
|             {0, param_unit, -param_unit}, | ||||
|             {0, param_unit, -param_unit}, | ||||
|         }; | ||||
|         rb.PushRaw(param); | ||||
| 
 | ||||
|         LOG_WARNING(Service_HID, "(STUBBED) called"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::GetSoundVolume(Kernel::HLERequestContext& ctx) { | ||||
|  | @ -454,6 +674,24 @@ Module::Module(Core::System& system) : system(system) { | |||
|     timing.ScheduleEvent(pad_update_ticks, pad_update_event); | ||||
| } | ||||
| 
 | ||||
| void Module::UseArticClient(const std::shared_ptr<Network::ArticBase::Client>& client) { | ||||
|     artic_client = client; | ||||
|     artic_controller = std::make_shared<ArticBaseController>(client); | ||||
|     if (!artic_controller->IsCreated()) { | ||||
|         artic_controller.reset(); | ||||
|     } else { | ||||
|         auto ir_user = system.ServiceManager().GetService<Service::IR::IR_USER>("ir:USER"); | ||||
|         if (ir_user.get()) { | ||||
|             ir_user->UseArticController(artic_controller); | ||||
|         } | ||||
| 
 | ||||
|         auto ir_rst = system.ServiceManager().GetService<Service::IR::IR_RST>("ir:rst"); | ||||
|         if (ir_rst.get()) { | ||||
|             ir_rst->UseArticController(artic_controller); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void Module::ReloadInputDevices() { | ||||
|     is_device_reload_pending.store(true); | ||||
| } | ||||
|  |  | |||
|  | @ -14,13 +14,11 @@ | |||
| #include "common/common_funcs.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/settings.h" | ||||
| #include "core/core.h" | ||||
| #include "core/core_timing.h" | ||||
| #include "core/frontend/input.h" | ||||
| #include "core/hle/service/service.h" | ||||
| 
 | ||||
| namespace Core { | ||||
| class System; | ||||
| } | ||||
| #include "network/artic_base/artic_base_client.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| class Event; | ||||
|  | @ -199,6 +197,44 @@ struct DirectionState { | |||
| /// Translates analog stick axes to directions. This is exposed for ir_rst module to use.
 | ||||
| DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y); | ||||
| 
 | ||||
| class ArticBaseController { | ||||
| public: | ||||
|     struct ControllerData { | ||||
|         u32 index{}; | ||||
|         u32 pad{}; | ||||
|         s16 c_pad_x{}; | ||||
|         s16 c_pad_y{}; | ||||
|         u16 touch_x{}; | ||||
|         u16 touch_y{}; | ||||
|         s16 c_stick_x{}; | ||||
|         s16 c_stick_y{}; | ||||
|         s16 accel_x{}; | ||||
|         s16 accel_y{}; | ||||
|         s16 accel_z{}; | ||||
|         s16 gyro_x{}; | ||||
|         s16 gyro_y{}; | ||||
|         s16 gyro_z{}; | ||||
|     }; | ||||
|     static_assert(sizeof(ControllerData) == 0x20, "Incorrect ControllerData size"); | ||||
| 
 | ||||
|     ArticBaseController(const std::shared_ptr<Network::ArticBase::Client>& client); | ||||
| 
 | ||||
|     bool IsCreated() { | ||||
|         return udp_stream.get(); | ||||
|     } | ||||
| 
 | ||||
|     bool IsReady() { | ||||
|         return udp_stream.get() ? udp_stream->IsReady() : false; | ||||
|     } | ||||
| 
 | ||||
|     ControllerData GetControllerData(); | ||||
| 
 | ||||
| private: | ||||
|     std::shared_ptr<Network::ArticBase::Client::UDPStream> udp_stream; | ||||
|     u32 last_packet_id{}; | ||||
|     ControllerData last_controller_data{}; | ||||
| }; | ||||
| 
 | ||||
| class Module final { | ||||
| public: | ||||
|     explicit Module(Core::System& system); | ||||
|  | @ -296,6 +332,8 @@ public: | |||
|         std::shared_ptr<Module> hid; | ||||
|     }; | ||||
| 
 | ||||
|     void UseArticClient(const std::shared_ptr<Network::ArticBase::Client>& client); | ||||
| 
 | ||||
|     void ReloadInputDevices(); | ||||
| 
 | ||||
|     const PadState& GetState() const; | ||||
|  | @ -355,6 +393,9 @@ private: | |||
|     std::unique_ptr<Input::TouchDevice> touch_device; | ||||
|     std::unique_ptr<Input::TouchDevice> touch_btn_device; | ||||
| 
 | ||||
|     std::shared_ptr<ArticBaseController> artic_controller; | ||||
|     std::shared_ptr<Network::ArticBase::Client> artic_client; | ||||
| 
 | ||||
|     template <class Archive> | ||||
|     void serialize(Archive& ar, const unsigned int); | ||||
|     friend class boost::serialization::access; | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ | |||
| #include "common/alignment.h" | ||||
| #include "common/settings.h" | ||||
| #include "core/core_timing.h" | ||||
| #include "core/hle/service/hid/hid.h" | ||||
| #include "core/hle/service/ir/extra_hid.h" | ||||
| #include "core/movie.h" | ||||
| 
 | ||||
|  | @ -230,23 +231,47 @@ void ExtraHID::SendHIDStatus() { | |||
|     if (is_device_reload_pending.exchange(false)) | ||||
|         LoadInputDevices(); | ||||
| 
 | ||||
|     constexpr u32 ZL_BUTTON = (1 << 14); | ||||
|     constexpr u32 ZR_BUTTON = (1 << 15); | ||||
| 
 | ||||
|     constexpr int C_STICK_CENTER = 0x800; | ||||
|     // TODO(wwylele): this value is not accurately measured. We currently assume that the axis can
 | ||||
|     // take values in the whole range of a 12-bit integer.
 | ||||
|     constexpr int C_STICK_RADIUS = 0x7FF; | ||||
| 
 | ||||
|     float x, y; | ||||
|     std::tie(x, y) = c_stick->GetStatus(); | ||||
| 
 | ||||
|     ExtraHIDResponse response{}; | ||||
|     response.c_stick.header.Assign(static_cast<u8>(ResponseID::PollHID)); | ||||
|     response.c_stick.c_stick_x.Assign(static_cast<u32>(C_STICK_CENTER + C_STICK_RADIUS * x)); | ||||
|     response.c_stick.c_stick_y.Assign(static_cast<u32>(C_STICK_CENTER + C_STICK_RADIUS * y)); | ||||
|     response.buttons.battery_level.Assign(0x1F); | ||||
|     response.buttons.zl_not_held.Assign(!zl->GetStatus()); | ||||
|     response.buttons.zr_not_held.Assign(!zr->GetStatus()); | ||||
|     response.buttons.r_not_held.Assign(1); | ||||
|     response.unknown = 0; | ||||
| 
 | ||||
|     if (artic_controller.get() && artic_controller->IsReady()) { | ||||
|         Service::HID::ArticBaseController::ControllerData data = | ||||
|             artic_controller->GetControllerData(); | ||||
| 
 | ||||
|         constexpr int MAX_CSTICK_RADIUS = 0x9C; // Max value for a c-stick radius
 | ||||
| 
 | ||||
|         response.c_stick.header.Assign(static_cast<u8>(ResponseID::PollHID)); | ||||
|         response.c_stick.c_stick_x.Assign(static_cast<u32>( | ||||
|             (static_cast<float>(data.c_stick_x) / MAX_CSTICK_RADIUS) * C_STICK_RADIUS + | ||||
|             C_STICK_CENTER)); | ||||
|         response.c_stick.c_stick_y.Assign(static_cast<u32>( | ||||
|             (static_cast<float>(data.c_stick_y) / MAX_CSTICK_RADIUS) * C_STICK_RADIUS + | ||||
|             C_STICK_CENTER)); | ||||
|         response.buttons.battery_level.Assign(0x1F); | ||||
|         response.buttons.zl_not_held.Assign((data.pad & ZL_BUTTON) == 0); | ||||
|         response.buttons.zr_not_held.Assign((data.pad & ZR_BUTTON) == 0); | ||||
|         response.buttons.r_not_held.Assign(1); | ||||
|         response.unknown = 0; | ||||
|     } else { | ||||
|         float x, y; | ||||
|         std::tie(x, y) = c_stick->GetStatus(); | ||||
| 
 | ||||
|         response.c_stick.header.Assign(static_cast<u8>(ResponseID::PollHID)); | ||||
|         response.c_stick.c_stick_x.Assign(static_cast<u32>(C_STICK_CENTER + C_STICK_RADIUS * x)); | ||||
|         response.c_stick.c_stick_y.Assign(static_cast<u32>(C_STICK_CENTER + C_STICK_RADIUS * y)); | ||||
|         response.buttons.battery_level.Assign(0x1F); | ||||
|         response.buttons.zl_not_held.Assign(!zl->GetStatus()); | ||||
|         response.buttons.zr_not_held.Assign(!zr->GetStatus()); | ||||
|         response.buttons.r_not_held.Assign(1); | ||||
|         response.unknown = 0; | ||||
|     } | ||||
| 
 | ||||
|     movie.HandleExtraHidResponse(response); | ||||
| 
 | ||||
|  |  | |||
|  | @ -19,6 +19,10 @@ class Timing; | |||
| class Movie; | ||||
| } // namespace Core
 | ||||
| 
 | ||||
| namespace Service::HID { | ||||
| class ArticBaseController; | ||||
| }; | ||||
| 
 | ||||
| namespace Service::IR { | ||||
| 
 | ||||
| struct ExtraHIDResponse { | ||||
|  | @ -54,6 +58,10 @@ public: | |||
|     /// Requests input devices reload from current settings. Called when the input settings change.
 | ||||
|     void RequestInputDevicesReload(); | ||||
| 
 | ||||
|     void UseArticController(const std::shared_ptr<Service::HID::ArticBaseController>& ac) { | ||||
|         artic_controller = ac; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     void SendHIDStatus(); | ||||
|     void HandleConfigureHIDPollingRequest(std::span<const u8> request); | ||||
|  | @ -70,6 +78,8 @@ private: | |||
|     std::unique_ptr<Input::AnalogDevice> c_stick; | ||||
|     std::atomic<bool> is_device_reload_pending; | ||||
| 
 | ||||
|     std::shared_ptr<Service::HID::ArticBaseController> artic_controller = nullptr; | ||||
| 
 | ||||
|     template <class Archive> | ||||
|     void serialize(Archive& ar, const unsigned int) { | ||||
|         ar& hid_period; | ||||
|  |  | |||
|  | @ -72,25 +72,41 @@ void IR_RST::UpdateCallback(std::uintptr_t user_data, s64 cycles_late) { | |||
|     if (is_device_reload_pending.exchange(false)) | ||||
|         LoadInputDevices(); | ||||
| 
 | ||||
|     constexpr u32 VALID_EXTRAHID_KEYS = 0xF00C000; | ||||
| 
 | ||||
|     PadState state; | ||||
|     state.zl.Assign(zl_button->GetStatus()); | ||||
|     state.zr.Assign(zr_button->GetStatus()); | ||||
|     s16 c_stick_x, c_stick_y; | ||||
| 
 | ||||
|     // Get current c-stick position and update c-stick direction
 | ||||
|     float c_stick_x_f, c_stick_y_f; | ||||
|     std::tie(c_stick_x_f, c_stick_y_f) = c_stick->GetStatus(); | ||||
|     constexpr int MAX_CSTICK_RADIUS = 0x9C; // Max value for a c-stick radius
 | ||||
|     s16 c_stick_x = static_cast<s16>(c_stick_x_f * MAX_CSTICK_RADIUS); | ||||
|     s16 c_stick_y = static_cast<s16>(c_stick_y_f * MAX_CSTICK_RADIUS); | ||||
|     if (artic_controller.get() && artic_controller->IsReady()) { | ||||
|         Service::HID::ArticBaseController::ControllerData data = | ||||
|             artic_controller->GetControllerData(); | ||||
| 
 | ||||
|     system.Movie().HandleIrRst(state, c_stick_x, c_stick_y); | ||||
|         state.hex = data.pad & VALID_EXTRAHID_KEYS; | ||||
| 
 | ||||
|     if (!raw_c_stick) { | ||||
|         const HID::DirectionState direction = HID::GetStickDirectionState(c_stick_x, c_stick_y); | ||||
|         state.c_stick_up.Assign(direction.up); | ||||
|         state.c_stick_down.Assign(direction.down); | ||||
|         state.c_stick_left.Assign(direction.left); | ||||
|         state.c_stick_right.Assign(direction.right); | ||||
|         c_stick_x = data.c_stick_x; | ||||
|         c_stick_y = data.c_stick_y; | ||||
| 
 | ||||
|         system.Movie().HandleIrRst(state, c_stick_x, c_stick_y); | ||||
|     } else { | ||||
|         state.zl.Assign(zl_button->GetStatus()); | ||||
|         state.zr.Assign(zr_button->GetStatus()); | ||||
| 
 | ||||
|         // Get current c-stick position and update c-stick direction
 | ||||
|         float c_stick_x_f, c_stick_y_f; | ||||
|         std::tie(c_stick_x_f, c_stick_y_f) = c_stick->GetStatus(); | ||||
|         constexpr int MAX_CSTICK_RADIUS = 0x9C; // Max value for a c-stick radius
 | ||||
|         c_stick_x = static_cast<s16>(c_stick_x_f * MAX_CSTICK_RADIUS); | ||||
|         c_stick_y = static_cast<s16>(c_stick_y_f * MAX_CSTICK_RADIUS); | ||||
| 
 | ||||
|         system.Movie().HandleIrRst(state, c_stick_x, c_stick_y); | ||||
| 
 | ||||
|         if (!raw_c_stick) { | ||||
|             const HID::DirectionState direction = HID::GetStickDirectionState(c_stick_x, c_stick_y); | ||||
|             state.c_stick_up.Assign(direction.up); | ||||
|             state.c_stick_down.Assign(direction.down); | ||||
|             state.c_stick_left.Assign(direction.left); | ||||
|             state.c_stick_right.Assign(direction.right); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // TODO (wwylele): implement raw C-stick data for raw_c_stick = true
 | ||||
|  |  | |||
|  | @ -21,6 +21,10 @@ namespace Core { | |||
| struct TimingEventType; | ||||
| }; | ||||
| 
 | ||||
| namespace Service::HID { | ||||
| class ArticBaseController; | ||||
| }; | ||||
| 
 | ||||
| namespace Service::IR { | ||||
| 
 | ||||
| union PadState { | ||||
|  | @ -42,6 +46,10 @@ public: | |||
|     ~IR_RST(); | ||||
|     void ReloadInputDevices(); | ||||
| 
 | ||||
|     void UseArticController(const std::shared_ptr<Service::HID::ArticBaseController>& ac) { | ||||
|         artic_controller = ac; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     /**
 | ||||
|      * GetHandles service function | ||||
|  | @ -88,6 +96,8 @@ private: | |||
|     bool raw_c_stick{false}; | ||||
|     int update_period{0}; | ||||
| 
 | ||||
|     std::shared_ptr<Service::HID::ArticBaseController> artic_controller = nullptr; | ||||
| 
 | ||||
|     template <class Archive> | ||||
|     void serialize(Archive& ar, const unsigned int); | ||||
|     friend class boost::serialization::access; | ||||
|  |  | |||
|  | @ -480,6 +480,12 @@ void IR_USER::ReloadInputDevices() { | |||
|     extra_hid->RequestInputDevicesReload(); | ||||
| } | ||||
| 
 | ||||
| void IR_USER::UseArticController(const std::shared_ptr<Service::HID::ArticBaseController>& ac) { | ||||
|     if (extra_hid.get()) { | ||||
|         extra_hid->UseArticController(ac); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| IRDevice::IRDevice(SendFunc send_func_) : send_func(send_func_) {} | ||||
| IRDevice::~IRDevice() = default; | ||||
| 
 | ||||
|  |  | |||
|  | @ -14,6 +14,10 @@ class Event; | |||
| class SharedMemory; | ||||
| } // namespace Kernel
 | ||||
| 
 | ||||
| namespace Service::HID { | ||||
| class ArticBaseController; | ||||
| }; | ||||
| 
 | ||||
| namespace Service::IR { | ||||
| 
 | ||||
| class BufferManager; | ||||
|  | @ -57,6 +61,8 @@ public: | |||
| 
 | ||||
|     void ReloadInputDevices(); | ||||
| 
 | ||||
|     void UseArticController(const std::shared_ptr<Service::HID::ArticBaseController>& ac); | ||||
| 
 | ||||
| private: | ||||
|     /**
 | ||||
|      * InitializeIrNopShared service function | ||||
|  |  | |||
|  | @ -27,6 +27,7 @@ | |||
| #include "core/hle/service/cfg/cfg_u.h" | ||||
| #include "core/hle/service/fs/archive.h" | ||||
| #include "core/hle/service/fs/fs_user.h" | ||||
| #include "core/hle/service/hid/hid_user.h" | ||||
| #include "core/loader/artic.h" | ||||
| #include "core/loader/smdh.h" | ||||
| #include "core/memory.h" | ||||
|  | @ -361,6 +362,13 @@ ResultStatus Apploader_Artic::Load(std::shared_ptr<Kernel::Process>& process) { | |||
|         amapp->UseArticClient(client); | ||||
|     } | ||||
| 
 | ||||
|     if (Settings::values.use_artic_base_controller.GetValue()) { | ||||
|         auto hid_user = system.ServiceManager().GetService<Service::HID::User>("hid:USER"); | ||||
|         if (hid_user.get()) { | ||||
|             hid_user->GetModule()->UseArticClient(client); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ParseRegionLockoutInfo(ncch_program_id); | ||||
| 
 | ||||
|     return ResultStatus::Success; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue