Merge pull request #642 from bunnei/touchpad

Touchpad support
This commit is contained in:
bunnei 2015-03-11 21:28:57 -04:00
commit ed5b275d21
10 changed files with 298 additions and 161 deletions

View file

@ -16,18 +16,34 @@ EmuWindow_GLFW* EmuWindow_GLFW::GetEmuWindow(GLFWwindow* win) {
return static_cast<EmuWindow_GLFW*>(glfwGetWindowUserPointer(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 /// Called by GLFW when a key event occurs
void EmuWindow_GLFW::OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods) { void EmuWindow_GLFW::OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods) {
auto emu_window = GetEmuWindow(win);
int keyboard_id = GetEmuWindow(win)->keyboard_id; int keyboard_id = emu_window->keyboard_id;
if (action == GLFW_PRESS) { if (action == GLFW_PRESS) {
EmuWindow::KeyPressed({key, keyboard_id}); emu_window->KeyPressed({key, keyboard_id});
} else if (action == GLFW_RELEASE) { } 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 /// 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 // Setup callbacks
glfwSetKeyCallback(m_render_window, OnKeyEvent); glfwSetKeyCallback(m_render_window, OnKeyEvent);
glfwSetMouseButtonCallback(m_render_window, OnMouseButtonEvent);
glfwSetCursorPosCallback(m_render_window, OnCursorPosEvent);
glfwSetFramebufferSizeCallback(m_render_window, OnFramebufferResizeEvent); glfwSetFramebufferSizeCallback(m_render_window, OnFramebufferResizeEvent);
glfwSetWindowSizeCallback(m_render_window, OnClientAreaResizeEvent); glfwSetWindowSizeCallback(m_render_window, OnClientAreaResizeEvent);

View file

@ -27,6 +27,10 @@ public:
static void OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods); 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 /// Whether the window is still open, and a close request hasn't yet been sent
const bool IsOpen(); const bool IsOpen();

View file

@ -268,14 +268,33 @@ QByteArray GRenderWindow::saveGeometry()
void GRenderWindow::keyPressEvent(QKeyEvent* event) void GRenderWindow::keyPressEvent(QKeyEvent* event)
{ {
EmuWindow::KeyPressed({event->key(), keyboard_id}); this->KeyPressed({event->key(), keyboard_id});
Service::HID::PadUpdateComplete();
} }
void GRenderWindow::keyReleaseEvent(QKeyEvent* event) void GRenderWindow::keyReleaseEvent(QKeyEvent* event)
{ {
EmuWindow::KeyReleased({event->key(), keyboard_id}); this->KeyReleased({event->key(), keyboard_id});
Service::HID::PadUpdateComplete(); }
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() void GRenderWindow::ReloadSetKeymaps()

View file

@ -121,6 +121,10 @@ public:
void keyPressEvent(QKeyEvent* event) override; void keyPressEvent(QKeyEvent* event) override;
void keyReleaseEvent(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 ReloadSetKeymaps() override;
void OnClientAreaResized(unsigned width, unsigned height); void OnClientAreaResized(unsigned width, unsigned height);

View file

@ -6,18 +6,61 @@
#include "video_core/video_core.h" #include "video_core/video_core.h"
void EmuWindow::KeyPressed(KeyMap::HostDeviceKey key) { void EmuWindow::KeyPressed(KeyMap::HostDeviceKey key) {
Service::HID::PadState mapped_key = KeyMap::GetPadKey(key); pad_state.hex |= KeyMap::GetPadKey(key).hex;
Service::HID::PadButtonPress(mapped_key);
} }
void EmuWindow::KeyReleased(KeyMap::HostDeviceKey key) { void EmuWindow::KeyReleased(KeyMap::HostDeviceKey key) {
Service::HID::PadState mapped_key = KeyMap::GetPadKey(key); pad_state.hex &= ~KeyMap::GetPadKey(key).hex;
Service::HID::PadButtonRelease(mapped_key);
} }
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(width > 0);
ASSERT(height > 0); ASSERT(height > 0);

View file

@ -71,10 +71,48 @@ public:
virtual void ReloadSetKeymaps() = 0; virtual void ReloadSetKeymaps() = 0;
/// Signals a key press action to the HID module /// 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 /// 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. * Returns currently active configuration.
@ -100,21 +138,15 @@ public:
return framebuffer_layout; 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: protected:
EmuWindow() EmuWindow() {
{
// TODO: Find a better place to set this. // TODO: Find a better place to set this.
config.min_client_area_size = std::make_pair(400u, 480u); config.min_client_area_size = std::make_pair(400u, 480u);
active_config = config; active_config = config;
pad_state.hex = 0;
touch_x = 0;
touch_y = 0;
touch_pressed = false;
} }
virtual ~EmuWindow() {} virtual ~EmuWindow() {}
@ -168,4 +200,11 @@ private:
WindowConfig config; ///< Internal configuration (changes pending for being applied in ProcessConfigurationChanges) WindowConfig config; ///< Internal configuration (changes pending for being applied in ProcessConfigurationChanges)
WindowConfig active_config; ///< Internal active configuration 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;
}; };

View file

@ -12,31 +12,25 @@
#include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/shared_memory.h"
#include "core/hle/hle.h" #include "core/hle/hle.h"
#include "video_core/video_core.h"
namespace Service { namespace Service {
namespace HID { 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; // Handle to shared memory region designated to HID_User service
Kernel::SharedPtr<Kernel::Event> g_event_pad_or_touch_2; static Kernel::SharedPtr<Kernel::SharedMemory> shared_mem = nullptr;
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 // Event handles
static PadState next_state = {{0}}; static Kernel::SharedPtr<Kernel::Event> event_pad_or_touch_1 = nullptr;
static u32 next_index = 0; static Kernel::SharedPtr<Kernel::Event> event_pad_or_touch_2 = nullptr;
static s16 next_circle_x = 0; static Kernel::SharedPtr<Kernel::Event> event_accelerometer = nullptr;
static s16 next_circle_y = 0; static Kernel::SharedPtr<Kernel::Event> event_gyroscope = nullptr;
static Kernel::SharedPtr<Kernel::Event> event_debug_pad = nullptr;
/** static u32 next_pad_index = 0;
* Gets a pointer to the PadData structure inside HID shared memory static u32 next_touch_index = 0;
*/
static inline PadData* GetPadData() {
if (g_shared_mem == nullptr)
return nullptr;
return reinterpret_cast<PadData*>(g_shared_mem->GetPointer().ValueOr(nullptr));
}
// TODO(peachum): // TODO(peachum):
// Add a method for setting analog input from joystick device for the circle Pad. // 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_left = 1 if current PadEntry.circle_pad_x <= -41
// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_y <= -41 // * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_y <= -41
/** void HIDUpdate() {
* Circle Pad from keys. SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer().ValueOr(nullptr));
* const PadState state = VideoCore::g_emu_window->GetPadState();
* 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;
}
/** if (mem == nullptr) {
* Sets a Pad state (button or button combo) as pressed LOG_DEBUG(Service_HID, "Cannot update HID prior to mapping shared memory!");
*/
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) {
return; return;
} }
// Update PadData struct mem->pad.current_state.hex = state.hex;
pad_data->current_state.hex = next_state.hex; mem->pad.index = next_pad_index;
pad_data->index = next_index; ++next_touch_index %= mem->pad.entries.size();
next_index = (next_index + 1) % pad_data->entries.size();
// Get the previous Pad state // Get the previous Pad state
u32 last_entry_index = (pad_data->index - 1) % pad_data->entries.size(); u32 last_entry_index = (mem->pad.index - 1) % mem->pad.entries.size();
PadState old_state = pad_data->entries[last_entry_index].current_state; PadState old_state = mem->pad.entries[last_entry_index].current_state;
// Compute bitmask with 1s for bits different from the old state // Compute bitmask with 1s for bits different from the old state
PadState changed; PadState changed = { { (state.hex ^ old_state.hex) } };
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;
// Get the current Pad entry // 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 // Update entry properties
current_pad_entry->current_state.hex = next_state.hex; pad_entry->current_state.hex = state.hex;
current_pad_entry->delta_additions.hex = additions.hex; pad_entry->delta_additions.hex = changed.hex & state.hex;
current_pad_entry->delta_removals.hex = removals.hex; pad_entry->delta_removals.hex = changed.hex & old_state.hex;;
// Set circle Pad // Set circle Pad
current_pad_entry->circle_pad_x = next_circle_x; pad_entry->circle_pad_x = state.circle_left ? -MAX_CIRCLEPAD_POS :
current_pad_entry->circle_pad_y = next_circle_y; 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 we just updated index 0, provide a new timestamp
if (pad_data->index == 0) { if (mem->pad.index == 0) {
pad_data->index_reset_ticks_previous = pad_data->index_reset_ticks; mem->pad.index_reset_ticks_previous = mem->pad.index_reset_ticks;
pad_data->index_reset_ticks = (s64)Core::g_app_core->GetTicks(); 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 // Signal both handles when there's an update to Pad or touch
g_event_pad_or_touch_1->Signal(); event_pad_or_touch_1->Signal();
g_event_pad_or_touch_2->Signal(); event_pad_or_touch_2->Signal();
} }
void GetIPCHandles(Service::Interface* self) { void GetIPCHandles(Service::Interface* self) {
@ -142,12 +115,12 @@ void GetIPCHandles(Service::Interface* self) {
cmd_buff[1] = 0; // No error cmd_buff[1] = 0; // No error
// TODO(yuriks): Return error from SendSyncRequest is this fails (part of IPC marshalling) // 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[3] = Kernel::g_handle_table.Create(Service::HID::shared_mem).MoveFrom();
cmd_buff[4] = Kernel::g_handle_table.Create(Service::HID::g_event_pad_or_touch_1).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::g_event_pad_or_touch_2).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::g_event_accelerometer).MoveFrom(); cmd_buff[6] = Kernel::g_handle_table.Create(Service::HID::event_accelerometer).MoveFrom();
cmd_buff[7] = Kernel::g_handle_table.Create(Service::HID::g_event_gyroscope).MoveFrom(); cmd_buff[7] = Kernel::g_handle_table.Create(Service::HID::event_gyroscope).MoveFrom();
cmd_buff[8] = Kernel::g_handle_table.Create(Service::HID::g_event_debug_pad).MoveFrom(); cmd_buff[8] = Kernel::g_handle_table.Create(Service::HID::event_debug_pad).MoveFrom();
} }
void HIDInit() { void HIDInit() {
@ -156,19 +129,22 @@ void HIDInit() {
AddService(new HID_U_Interface); AddService(new HID_U_Interface);
AddService(new HID_SPVR_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 // Create event handles
g_event_pad_or_touch_1 = Event::Create(RESETTYPE_ONESHOT, "HID:EventPadOrTouch1"); event_pad_or_touch_1 = Event::Create(RESETTYPE_ONESHOT, "HID:EventPadOrTouch1");
g_event_pad_or_touch_2 = Event::Create(RESETTYPE_ONESHOT, "HID:EventPadOrTouch2"); event_pad_or_touch_2 = Event::Create(RESETTYPE_ONESHOT, "HID:EventPadOrTouch2");
g_event_accelerometer = Event::Create(RESETTYPE_ONESHOT, "HID:EventAccelerometer"); event_accelerometer = Event::Create(RESETTYPE_ONESHOT, "HID:EventAccelerometer");
g_event_gyroscope = Event::Create(RESETTYPE_ONESHOT, "HID:EventGyroscope"); event_gyroscope = Event::Create(RESETTYPE_ONESHOT, "HID:EventGyroscope");
g_event_debug_pad = Event::Create(RESETTYPE_ONESHOT, "HID:EventDebugPad"); event_debug_pad = Event::Create(RESETTYPE_ONESHOT, "HID:EventDebugPad");
} }
void HIDShutdown() { void HIDShutdown() {
} }
} } // namespace HID
}
} // namespace Service

View file

@ -18,16 +18,6 @@ namespace Kernel {
namespace Service { namespace Service {
namespace HID { 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. * 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 { struct PadDataEntry {
PadState current_state; 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 { struct TouchDataEntry {
s64 index_reset_ticks; u16 x; ///< Y-coordinate of a touchpad press on the lower screen
s64 index_reset_ticks_previous; u16 y; ///< X-coordinate of a touchpad press on the lower screen
u32 index; // the index of the last updated Pad state history element BitField<0, 7, u32> valid; ///< Set to 1 when this entry contains actual X/Y data, otherwise 0
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
}; };
/**
* 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 // Pre-defined PadStates for single button presses
const PadState PAD_NONE = {{0}}; const PadState PAD_NONE = {{0}};
const PadState PAD_A = {{1u << 0}}; const PadState PAD_A = {{1u << 0}};
@ -140,12 +171,13 @@ const PadState PAD_CIRCLE_DOWN = {{1u << 31}};
*/ */
void GetIPCHandles(Interface* self); void GetIPCHandles(Interface* self);
// Methods for updating the HID module's state /// Checks for user input updates
void PadButtonPress(const PadState& pad_state); void HIDUpdate();
void PadButtonRelease(const PadState& pad_state);
void PadUpdateComplete();
/// Initialize HID service
void HIDInit(); void HIDInit();
/// Shutdown HID service
void HIDShutdown(); void HIDShutdown();
} }

View file

@ -3,8 +3,6 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "core/hle/hle.h" #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.h"
#include "core/hle/service/hid/hid_user.h" #include "core/hle/service/hid/hid_user.h"

View file

@ -14,6 +14,7 @@
#include "core/hle/hle.h" #include "core/hle/hle.h"
#include "core/hle/service/gsp_gpu.h" #include "core/hle/service/gsp_gpu.h"
#include "core/hle/service/dsp_dsp.h" #include "core/hle/service/dsp_dsp.h"
#include "core/hle/service/hid/hid.h"
#include "core/hw/hw.h" #include "core/hw/hw.h"
#include "core/hw/gpu.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. // this. Certain games expect this to be periodically signaled.
DSP_DSP::SignalInterrupt(); DSP_DSP::SignalInterrupt();
// Check for user input updates
Service::HID::HIDUpdate();
// Reschedule recurrent event // Reschedule recurrent event
CoreTiming::ScheduleEvent(frame_ticks - cycles_late, vblank_event); CoreTiming::ScheduleEvent(frame_ticks - cycles_late, vblank_event);
} }