mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	
						commit
						ed5b275d21
					
				
					 10 changed files with 298 additions and 161 deletions
				
			
		|  | @ -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); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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() | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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); | ||||
| 
 | ||||
|     Service::HID::PadButtonRelease(mapped_key); | ||||
|     pad_state.hex &= ~KeyMap::GetPadKey(key).hex; | ||||
| } | ||||
| 
 | ||||
| EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(unsigned width, unsigned height) { | ||||
| /**
 | ||||
|  * 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); | ||||
| 
 | ||||
|     touch_pressed = true; | ||||
|     pad_state.touch = 1; | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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; | ||||
| }; | ||||
|  |  | |||
|  | @ -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; | ||||
| static const int MAX_CIRCLEPAD_POS = 0x9C; ///< Max value for a circle pad position
 | ||||
| 
 | ||||
| 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; | ||||
| // Handle to shared memory region designated to HID_User service
 | ||||
| static Kernel::SharedPtr<Kernel::SharedMemory> shared_mem = nullptr; | ||||
| 
 | ||||
| // 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; | ||||
| // 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; | ||||
| 
 | ||||
| /**
 | ||||
|  * 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 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
 | ||||
|  |  | |||
|  | @ -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 PadData { | ||||
|     s64 index_reset_ticks; | ||||
|     s64 index_reset_ticks_previous; | ||||
|     u32 index; // the index of the last updated Pad state history element
 | ||||
| 
 | ||||
|     u32 pad1; | ||||
|     u32 pad2; | ||||
| 
 | ||||
|     PadState current_state; // same as entries[index].current_state
 | ||||
|     u32 raw_circle_pad_data; | ||||
| 
 | ||||
|     u32 pad3; | ||||
| 
 | ||||
|     std::array<PadDataEntry, 8> entries; // Pad state history
 | ||||
| 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 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
 | ||||
| 
 | ||||
|         // TODO(bunnei): Implement `raw_circle_pad_data` field
 | ||||
|         u32 raw_circle_pad_data; ///< Raw (analog) circle pad data, before being converted
 | ||||
| 
 | ||||
|         INSERT_PADDING_WORDS(0x1); | ||||
| 
 | ||||
|         std::array<PadDataEntry, 8> entries; ///< Last 8 pad entries
 | ||||
|     } pad; | ||||
| 
 | ||||
|     /// 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(); | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -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" | ||||
| 
 | ||||
|  |  | |||
|  | @ -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); | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue