diff options
-rw-r--r-- | src/citra/emu_window/emu_window_glfw.cpp | 30 | ||||
-rw-r--r-- | src/citra/emu_window/emu_window_glfw.h | 4 | ||||
-rw-r--r-- | src/citra_qt/bootmanager.cpp | 27 | ||||
-rw-r--r-- | src/citra_qt/bootmanager.h | 4 | ||||
-rw-r--r-- | src/common/emu_window.cpp | 55 | ||||
-rw-r--r-- | src/common/emu_window.h | 65 | ||||
-rw-r--r-- | src/core/hle/service/hid/hid.cpp | 180 | ||||
-rw-r--r-- | src/core/hle/service/hid/hid.h | 84 | ||||
-rw-r--r-- | src/core/hle/service/hid/hid_user.cpp | 2 | ||||
-rw-r--r-- | src/core/hw/gpu.cpp | 4 |
10 files changed, 296 insertions, 159 deletions
diff --git a/src/citra/emu_window/emu_window_glfw.cpp b/src/citra/emu_window/emu_window_glfw.cpp index 81231e1e5..997e3bc7d 100644 --- a/src/citra/emu_window/emu_window_glfw.cpp +++ b/src/citra/emu_window/emu_window_glfw.cpp @@ -16,18 +16,34 @@ EmuWindow_GLFW* EmuWindow_GLFW::GetEmuWindow(GLFWwindow* win) { return static_cast<EmuWindow_GLFW*>(glfwGetWindowUserPointer(win)); } +void EmuWindow_GLFW::OnMouseButtonEvent(GLFWwindow* win, int button, int action, int mods) { + if (button == GLFW_MOUSE_BUTTON_LEFT) { + auto emu_window = GetEmuWindow(win); + auto layout = emu_window->GetFramebufferLayout(); + double x, y; + glfwGetCursorPos(win, &x, &y); + + if (action == GLFW_PRESS) + emu_window->TouchPressed(static_cast<unsigned>(x), static_cast<unsigned>(y)); + else if (action == GLFW_RELEASE) + emu_window->TouchReleased(); + } +} + +void EmuWindow_GLFW::OnCursorPosEvent(GLFWwindow* win, double x, double y) { + GetEmuWindow(win)->TouchMoved(static_cast<unsigned>(x), static_cast<unsigned>(y)); +} + /// Called by GLFW when a key event occurs void EmuWindow_GLFW::OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods) { - - int keyboard_id = GetEmuWindow(win)->keyboard_id; + auto emu_window = GetEmuWindow(win); + int keyboard_id = emu_window->keyboard_id; if (action == GLFW_PRESS) { - EmuWindow::KeyPressed({key, keyboard_id}); + emu_window->KeyPressed({key, keyboard_id}); } else if (action == GLFW_RELEASE) { - EmuWindow::KeyReleased({key, keyboard_id}); + emu_window->KeyReleased({key, keyboard_id}); } - - Service::HID::PadUpdateComplete(); } /// Whether the window is still open, and a close request hasn't yet been sent @@ -88,6 +104,8 @@ EmuWindow_GLFW::EmuWindow_GLFW() { // Setup callbacks glfwSetKeyCallback(m_render_window, OnKeyEvent); + glfwSetMouseButtonCallback(m_render_window, OnMouseButtonEvent); + glfwSetCursorPosCallback(m_render_window, OnCursorPosEvent); glfwSetFramebufferSizeCallback(m_render_window, OnFramebufferResizeEvent); glfwSetWindowSizeCallback(m_render_window, OnClientAreaResizeEvent); diff --git a/src/citra/emu_window/emu_window_glfw.h b/src/citra/emu_window/emu_window_glfw.h index 5252fccc8..16c109b79 100644 --- a/src/citra/emu_window/emu_window_glfw.h +++ b/src/citra/emu_window/emu_window_glfw.h @@ -27,6 +27,10 @@ public: static void OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods); + static void OnMouseButtonEvent(GLFWwindow* window, int button, int action, int mods); + + static void OnCursorPosEvent(GLFWwindow* window, double x, double y); + /// Whether the window is still open, and a close request hasn't yet been sent const bool IsOpen(); diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index a040e75c1..b81bd6167 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -268,14 +268,33 @@ QByteArray GRenderWindow::saveGeometry() void GRenderWindow::keyPressEvent(QKeyEvent* event) { - EmuWindow::KeyPressed({event->key(), keyboard_id}); - Service::HID::PadUpdateComplete(); + this->KeyPressed({event->key(), keyboard_id}); } void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { - EmuWindow::KeyReleased({event->key(), keyboard_id}); - Service::HID::PadUpdateComplete(); + this->KeyReleased({event->key(), keyboard_id}); +} + +void GRenderWindow::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) + { + auto pos = event->pos(); + this->TouchPressed(static_cast<unsigned>(pos.x()), static_cast<unsigned>(pos.y())); + } +} + +void GRenderWindow::mouseMoveEvent(QMouseEvent *event) +{ + auto pos = event->pos(); + this->TouchMoved(static_cast<unsigned>(pos.x()), static_cast<unsigned>(pos.y())); +} + +void GRenderWindow::mouseReleaseEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) + this->TouchReleased(); } void GRenderWindow::ReloadSetKeymaps() diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h index a55db682a..288da45a1 100644 --- a/src/citra_qt/bootmanager.h +++ b/src/citra_qt/bootmanager.h @@ -121,6 +121,10 @@ public: void keyPressEvent(QKeyEvent* event) override; void keyReleaseEvent(QKeyEvent* event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void ReloadSetKeymaps() override; void OnClientAreaResized(unsigned width, unsigned height); diff --git a/src/common/emu_window.cpp b/src/common/emu_window.cpp index 6459d2f32..6516fc633 100644 --- a/src/common/emu_window.cpp +++ b/src/common/emu_window.cpp @@ -6,18 +6,61 @@ #include "video_core/video_core.h" void EmuWindow::KeyPressed(KeyMap::HostDeviceKey key) { - Service::HID::PadState mapped_key = KeyMap::GetPadKey(key); - - Service::HID::PadButtonPress(mapped_key); + pad_state.hex |= KeyMap::GetPadKey(key).hex; } void EmuWindow::KeyReleased(KeyMap::HostDeviceKey key) { - Service::HID::PadState mapped_key = KeyMap::GetPadKey(key); + pad_state.hex &= ~KeyMap::GetPadKey(key).hex; +} + +/** + * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout + * @param layout FramebufferLayout object describing the framebuffer size and screen positions + * @param framebuffer_x Framebuffer x-coordinate to check + * @param framebuffer_y Framebuffer y-coordinate to check + * @return True if the coordinates are within the touchpad, otherwise false + */ +static bool IsWithinTouchscreen(const EmuWindow::FramebufferLayout& layout, unsigned framebuffer_x, + unsigned framebuffer_y) { + return (framebuffer_y >= layout.bottom_screen.top && + framebuffer_y < layout.bottom_screen.bottom && + framebuffer_x >= layout.bottom_screen.left && + framebuffer_x < layout.bottom_screen.right); +} + +void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) { + if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) + return; + + touch_x = VideoCore::kScreenBottomWidth * (framebuffer_x - framebuffer_layout.bottom_screen.left) / + (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left); + touch_y = VideoCore::kScreenBottomHeight * (framebuffer_y - framebuffer_layout.bottom_screen.top) / + (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top); - Service::HID::PadButtonRelease(mapped_key); + touch_pressed = true; + pad_state.touch = 1; } -EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(unsigned width, unsigned height) { +void EmuWindow::TouchReleased() { + touch_pressed = false; + touch_x = 0; + touch_y = 0; + pad_state.touch = 0; +} + +void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) { + if (!touch_pressed) + return; + + if (IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) + TouchPressed(framebuffer_x, framebuffer_y); + else + TouchReleased(); +} + +EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(unsigned width, + unsigned height) { + ASSERT(width > 0); ASSERT(height > 0); diff --git a/src/common/emu_window.h b/src/common/emu_window.h index f6099fdb6..2be7517bc 100644 --- a/src/common/emu_window.h +++ b/src/common/emu_window.h @@ -71,10 +71,48 @@ public: virtual void ReloadSetKeymaps() = 0; /// Signals a key press action to the HID module - static void KeyPressed(KeyMap::HostDeviceKey key); + void KeyPressed(KeyMap::HostDeviceKey key); /// Signals a key release action to the HID module - static void KeyReleased(KeyMap::HostDeviceKey key); + void KeyReleased(KeyMap::HostDeviceKey key); + + /** + * Signal that a touch pressed event has occurred (e.g. mouse click pressed) + * @param framebuffer_x Framebuffer x-coordinate that was pressed + * @param framebuffer_y Framebuffer y-coordinate that was pressed + */ + void TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y); + + /// Signal that a touch released event has occurred (e.g. mouse click released) + void TouchReleased(); + + /** + * Signal that a touch movement event has occurred (e.g. mouse was moved over the emu window) + * @param framebuffer_x Framebuffer x-coordinate + * @param framebuffer_y Framebuffer y-coordinate + */ + void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y); + + /** + * Gets the current pad state (which buttons are pressed and the circle pad direction). + * @note This should be called by the core emu thread to get a state set by the window thread. + * @todo Fix this function to be thread-safe. + * @return PadState object indicating the current pad state + */ + const Service::HID::PadState GetPadState() const { + return pad_state; + } + + /** + * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed). + * @note This should be called by the core emu thread to get a state set by the window thread. + * @todo Fix this function to be thread-safe. + * @return std::tuple of (x, y, pressed) where `x` and `y` are the touch coordinates and + * `pressed` is true if the touch screen is currently being pressed + */ + const std::tuple<u16, u16, bool>& GetTouchState() const { + return std::make_tuple(touch_x, touch_y, touch_pressed); + } /** * Returns currently active configuration. @@ -100,21 +138,15 @@ public: return framebuffer_layout; } - /** - * Gets window client area width in logical coordinates. - * @note For high-DPI systems, this is smaller than the framebuffer size. - * @note This method is thread-safe - */ - std::pair<unsigned,unsigned> GetClientAreaSize() const { - return std::make_pair(client_area_width, client_area_height); - } - protected: - EmuWindow() - { + EmuWindow() { // TODO: Find a better place to set this. config.min_client_area_size = std::make_pair(400u, 480u); active_config = config; + pad_state.hex = 0; + touch_x = 0; + touch_y = 0; + touch_pressed = false; } virtual ~EmuWindow() {} @@ -168,4 +200,11 @@ private: WindowConfig config; ///< Internal configuration (changes pending for being applied in ProcessConfigurationChanges) WindowConfig active_config; ///< Internal active configuration + + bool touch_pressed; ///< True if touchpad area is currently pressed, otherwise false + + u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) + u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240) + + Service::HID::PadState pad_state; }; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index e0689be2e..e7f9bec7e 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -12,31 +12,25 @@ #include "core/hle/kernel/shared_memory.h" #include "core/hle/hle.h" +#include "video_core/video_core.h" + namespace Service { namespace HID { -Kernel::SharedPtr<Kernel::SharedMemory> g_shared_mem = nullptr; - -Kernel::SharedPtr<Kernel::Event> g_event_pad_or_touch_1; -Kernel::SharedPtr<Kernel::Event> g_event_pad_or_touch_2; -Kernel::SharedPtr<Kernel::Event> g_event_accelerometer; -Kernel::SharedPtr<Kernel::Event> g_event_gyroscope; -Kernel::SharedPtr<Kernel::Event> g_event_debug_pad; - -// Next Pad state update information -static PadState next_state = {{0}}; -static u32 next_index = 0; -static s16 next_circle_x = 0; -static s16 next_circle_y = 0; - -/** - * Gets a pointer to the PadData structure inside HID shared memory - */ -static inline PadData* GetPadData() { - if (g_shared_mem == nullptr) - return nullptr; - return reinterpret_cast<PadData*>(g_shared_mem->GetPointer().ValueOr(nullptr)); -} +static const int MAX_CIRCLEPAD_POS = 0x9C; ///< Max value for a circle pad position + +// Handle to shared memory region designated to HID_User service +static Kernel::SharedPtr<Kernel::SharedMemory> shared_mem = nullptr; + +// Event handles +static Kernel::SharedPtr<Kernel::Event> event_pad_or_touch_1 = nullptr; +static Kernel::SharedPtr<Kernel::Event> event_pad_or_touch_2 = nullptr; +static Kernel::SharedPtr<Kernel::Event> event_accelerometer = nullptr; +static Kernel::SharedPtr<Kernel::Event> event_gyroscope = nullptr; +static Kernel::SharedPtr<Kernel::Event> event_debug_pad = nullptr; + +static u32 next_pad_index = 0; +static u32 next_touch_index = 0; // TODO(peachum): // Add a method for setting analog input from joystick device for the circle Pad. @@ -51,90 +45,69 @@ static inline PadData* GetPadData() { // * Set PadData.current_state.circle_left = 1 if current PadEntry.circle_pad_x <= -41 // * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_y <= -41 -/** - * Circle Pad from keys. - * - * This is implemented as "pushed all the way to an edge (max) or centered (0)". - * - * Indicate the circle pad is pushed completely to the edge in 1 of 8 directions. - */ -static void UpdateNextCirclePadState() { - static const s16 max_value = 0x9C; - next_circle_x = next_state.circle_left ? -max_value : 0x0; - next_circle_x += next_state.circle_right ? max_value : 0x0; - next_circle_y = next_state.circle_down ? -max_value : 0x0; - next_circle_y += next_state.circle_up ? max_value : 0x0; -} +void HIDUpdate() { + SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer().ValueOr(nullptr)); + const PadState state = VideoCore::g_emu_window->GetPadState(); -/** - * Sets a Pad state (button or button combo) as pressed - */ -void PadButtonPress(const PadState& pad_state) { - next_state.hex |= pad_state.hex; - UpdateNextCirclePadState(); -} - -/** - * Sets a Pad state (button or button combo) as released - */ -void PadButtonRelease(const PadState& pad_state) { - next_state.hex &= ~pad_state.hex; - UpdateNextCirclePadState(); -} - -/** - * Called after all Pad changes to be included in this update have been made, - * including both Pad key changes and analog circle Pad changes. - */ -void PadUpdateComplete() { - PadData* pad_data = GetPadData(); - - if (pad_data == nullptr) { + if (mem == nullptr) { + LOG_DEBUG(Service_HID, "Cannot update HID prior to mapping shared memory!"); return; } - // Update PadData struct - pad_data->current_state.hex = next_state.hex; - pad_data->index = next_index; - next_index = (next_index + 1) % pad_data->entries.size(); + mem->pad.current_state.hex = state.hex; + mem->pad.index = next_pad_index; + ++next_touch_index %= mem->pad.entries.size(); // Get the previous Pad state - u32 last_entry_index = (pad_data->index - 1) % pad_data->entries.size(); - PadState old_state = pad_data->entries[last_entry_index].current_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; - changed.hex = (next_state.hex ^ old_state.hex); - - // Compute what was added - PadState additions; - additions.hex = changed.hex & next_state.hex; - - // Compute what was removed - PadState removals; - removals.hex = changed.hex & old_state.hex; + PadState changed = { { (state.hex ^ old_state.hex) } }; // Get the current Pad entry - PadDataEntry* current_pad_entry = &pad_data->entries[pad_data->index]; + PadDataEntry* pad_entry = &mem->pad.entries[mem->pad.index]; // Update entry properties - current_pad_entry->current_state.hex = next_state.hex; - current_pad_entry->delta_additions.hex = additions.hex; - current_pad_entry->delta_removals.hex = removals.hex; + 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;; // Set circle Pad - current_pad_entry->circle_pad_x = next_circle_x; - current_pad_entry->circle_pad_y = next_circle_y; + pad_entry->circle_pad_x = state.circle_left ? -MAX_CIRCLEPAD_POS : + state.circle_right ? MAX_CIRCLEPAD_POS : 0x0; + pad_entry->circle_pad_y = state.circle_down ? -MAX_CIRCLEPAD_POS : + state.circle_up ? MAX_CIRCLEPAD_POS : 0x0; // If we just updated index 0, provide a new timestamp - if (pad_data->index == 0) { - pad_data->index_reset_ticks_previous = pad_data->index_reset_ticks; - pad_data->index_reset_ticks = (s64)Core::g_app_core->GetTicks(); + if (mem->pad.index == 0) { + mem->pad.index_reset_ticks_previous = mem->pad.index_reset_ticks; + mem->pad.index_reset_ticks = (s64)Core::g_app_core->GetTicks(); + } + + mem->touch.index = next_touch_index; + ++next_touch_index %= mem->touch.entries.size(); + + // Get the current touch entry + TouchDataEntry* touch_entry = &mem->touch.entries[mem->touch.index]; + bool pressed = false; + + std::tie(touch_entry->x, touch_entry->y, pressed) = VideoCore::g_emu_window->GetTouchState(); + touch_entry->valid = pressed ? 1 : 0; + + // 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). + + // If we just updated index 0, provide a new timestamp + if (mem->touch.index == 0) { + mem->touch.index_reset_ticks_previous = mem->touch.index_reset_ticks; + mem->touch.index_reset_ticks = (s64)Core::g_app_core->GetTicks(); } // Signal both handles when there's an update to Pad or touch - g_event_pad_or_touch_1->Signal(); - g_event_pad_or_touch_2->Signal(); + event_pad_or_touch_1->Signal(); + event_pad_or_touch_2->Signal(); } void GetIPCHandles(Service::Interface* self) { @@ -142,12 +115,12 @@ void GetIPCHandles(Service::Interface* self) { cmd_buff[1] = 0; // No error // TODO(yuriks): Return error from SendSyncRequest is this fails (part of IPC marshalling) - cmd_buff[3] = Kernel::g_handle_table.Create(Service::HID::g_shared_mem).MoveFrom(); - cmd_buff[4] = Kernel::g_handle_table.Create(Service::HID::g_event_pad_or_touch_1).MoveFrom(); - cmd_buff[5] = Kernel::g_handle_table.Create(Service::HID::g_event_pad_or_touch_2).MoveFrom(); - cmd_buff[6] = Kernel::g_handle_table.Create(Service::HID::g_event_accelerometer).MoveFrom(); - cmd_buff[7] = Kernel::g_handle_table.Create(Service::HID::g_event_gyroscope).MoveFrom(); - cmd_buff[8] = Kernel::g_handle_table.Create(Service::HID::g_event_debug_pad).MoveFrom(); + cmd_buff[3] = Kernel::g_handle_table.Create(Service::HID::shared_mem).MoveFrom(); + cmd_buff[4] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_1).MoveFrom(); + cmd_buff[5] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_2).MoveFrom(); + cmd_buff[6] = Kernel::g_handle_table.Create(Service::HID::event_accelerometer).MoveFrom(); + cmd_buff[7] = Kernel::g_handle_table.Create(Service::HID::event_gyroscope).MoveFrom(); + cmd_buff[8] = Kernel::g_handle_table.Create(Service::HID::event_debug_pad).MoveFrom(); } void HIDInit() { @@ -156,19 +129,22 @@ void HIDInit() { AddService(new HID_U_Interface); AddService(new HID_SPVR_Interface); - g_shared_mem = SharedMemory::Create("HID:SharedMem"); + shared_mem = SharedMemory::Create("HID:SharedMem"); + + next_pad_index = 0; + next_touch_index = 0; // Create event handles - g_event_pad_or_touch_1 = Event::Create(RESETTYPE_ONESHOT, "HID:EventPadOrTouch1"); - g_event_pad_or_touch_2 = Event::Create(RESETTYPE_ONESHOT, "HID:EventPadOrTouch2"); - g_event_accelerometer = Event::Create(RESETTYPE_ONESHOT, "HID:EventAccelerometer"); - g_event_gyroscope = Event::Create(RESETTYPE_ONESHOT, "HID:EventGyroscope"); - g_event_debug_pad = Event::Create(RESETTYPE_ONESHOT, "HID:EventDebugPad"); + event_pad_or_touch_1 = Event::Create(RESETTYPE_ONESHOT, "HID:EventPadOrTouch1"); + event_pad_or_touch_2 = Event::Create(RESETTYPE_ONESHOT, "HID:EventPadOrTouch2"); + event_accelerometer = Event::Create(RESETTYPE_ONESHOT, "HID:EventAccelerometer"); + event_gyroscope = Event::Create(RESETTYPE_ONESHOT, "HID:EventGyroscope"); + event_debug_pad = Event::Create(RESETTYPE_ONESHOT, "HID:EventDebugPad"); } void HIDShutdown() { - } -} -} +} // namespace HID + +} // namespace Service diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 9c6e86f77..0946cf660 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -18,16 +18,6 @@ namespace Kernel { namespace Service { namespace HID { -// Handle to shared memory region designated to HID_User service -extern Kernel::SharedPtr<Kernel::SharedMemory> g_shared_mem; - -// Event handles -extern Kernel::SharedPtr<Kernel::Event> g_event_pad_or_touch_1; -extern Kernel::SharedPtr<Kernel::Event> g_event_pad_or_touch_2; -extern Kernel::SharedPtr<Kernel::Event> g_event_accelerometer; -extern Kernel::SharedPtr<Kernel::Event> g_event_gyroscope; -extern Kernel::SharedPtr<Kernel::Event> g_event_debug_pad; - /** * Structure of a Pad controller state. */ @@ -65,7 +55,7 @@ struct PadState { }; /** - * Structure of a single entry in the PadData's Pad state history array. + * Structure of a single entry of Pad state history within HID shared memory */ struct PadDataEntry { PadState current_state; @@ -77,24 +67,65 @@ struct PadDataEntry { }; /** - * Structure of all data related to the 3DS Pad. + * Structure of a single entry of touch state history within HID shared memory + */ +struct TouchDataEntry { + u16 x; ///< Y-coordinate of a touchpad press on the lower screen + u16 y; ///< X-coordinate of a touchpad press on the lower screen + BitField<0, 7, u32> valid; ///< Set to 1 when this entry contains actual X/Y data, otherwise 0 +}; + +/** + * Structure of data stored in HID shared memory */ -struct PadData { - s64 index_reset_ticks; - s64 index_reset_ticks_previous; - u32 index; // the index of the last updated Pad state history element +struct SharedMem { + /// Pad data, this is used for buttons and the circle pad + struct { + s64 index_reset_ticks; ///< CPU tick count for when HID module updated entry index 0 + s64 index_reset_ticks_previous; ///< Previous `index_reset_ticks` + u32 index; ///< Index of the last updated pad state entry + + INSERT_PADDING_WORDS(0x2); + + PadState current_state; ///< Current state of the pad buttons - u32 pad1; - u32 pad2; + // TODO(bunnei): Implement `raw_circle_pad_data` field + u32 raw_circle_pad_data; ///< Raw (analog) circle pad data, before being converted - PadState current_state; // same as entries[index].current_state - u32 raw_circle_pad_data; + INSERT_PADDING_WORDS(0x1); - u32 pad3; + std::array<PadDataEntry, 8> entries; ///< Last 8 pad entries + } pad; - std::array<PadDataEntry, 8> entries; // Pad state history + /// Touchpad data, this is used for touchpad input + struct { + s64 index_reset_ticks; ///< CPU tick count for when HID module updated entry index 0 + s64 index_reset_ticks_previous; ///< Previous `index_reset_ticks` + u32 index; ///< Index of the last updated touch entry + + INSERT_PADDING_WORDS(0x1); + + // TODO(bunnei): Implement `raw_entry` field + TouchDataEntry raw_entry; ///< Raw (analog) touch data, before being converted + + std::array<TouchDataEntry, 8> entries; ///< Last 8 touch entries, in pixel coordinates + } touch; }; +// TODO: MSVC does not support using offsetof() on non-static data members even though this +// is technically allowed since C++11. This macro should be enabled once MSVC adds +// support for that. +#ifndef _MSC_VER +#define ASSERT_REG_POSITION(field_name, position) \ + static_assert(offsetof(SharedMem, field_name) == position * 4, \ + "Field "#field_name" has invalid position") + +ASSERT_REG_POSITION(pad.index_reset_ticks, 0x0); +ASSERT_REG_POSITION(touch.index_reset_ticks, 0x2A); + +#undef ASSERT_REG_POSITION +#endif // !defined(_MSC_VER) + // Pre-defined PadStates for single button presses const PadState PAD_NONE = {{0}}; const PadState PAD_A = {{1u << 0}}; @@ -140,12 +171,13 @@ const PadState PAD_CIRCLE_DOWN = {{1u << 31}}; */ void GetIPCHandles(Interface* self); -// Methods for updating the HID module's state -void PadButtonPress(const PadState& pad_state); -void PadButtonRelease(const PadState& pad_state); -void PadUpdateComplete(); +/// Checks for user input updates +void HIDUpdate(); +/// Initialize HID service void HIDInit(); + +/// Shutdown HID service void HIDShutdown(); } diff --git a/src/core/hle/service/hid/hid_user.cpp b/src/core/hle/service/hid/hid_user.cpp index 1d0accefe..c2d5758fb 100644 --- a/src/core/hle/service/hid/hid_user.cpp +++ b/src/core/hle/service/hid/hid_user.cpp @@ -3,8 +3,6 @@ // Refer to the license.txt file included. #include "core/hle/hle.h" -#include "core/hle/kernel/event.h" -#include "core/hle/kernel/shared_memory.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/hid/hid_user.h" diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index 30318fc06..f933a5e8d 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -14,6 +14,7 @@ #include "core/hle/hle.h" #include "core/hle/service/gsp_gpu.h" #include "core/hle/service/dsp_dsp.h" +#include "core/hle/service/hid/hid.h" #include "core/hw/hw.h" #include "core/hw/gpu.h" @@ -295,6 +296,9 @@ static void VBlankCallback(u64 userdata, int cycles_late) { // this. Certain games expect this to be periodically signaled. DSP_DSP::SignalInterrupt(); + // Check for user input updates + Service::HID::HIDUpdate(); + // Reschedule recurrent event CoreTiming::ScheduleEvent(frame_ticks - cycles_late, vblank_event); } |