mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-10-15 04:29:04 +00:00
Merge #15
This commit is contained in:
parent
7d8fe0f105
commit
871da4e307
275 changed files with 33002 additions and 27474 deletions
|
@ -16,7 +16,6 @@
|
|||
#include "InputCommon/ControllerInterface/Xlib/XInput2.h"
|
||||
#endif
|
||||
#ifdef CIFACE_USE_OSX
|
||||
#include "InputCommon/ControllerInterface/OSX/OSX.h"
|
||||
#include "InputCommon/ControllerInterface/Quartz/Quartz.h"
|
||||
#endif
|
||||
#ifdef CIFACE_USE_SDL
|
||||
|
@ -34,6 +33,9 @@
|
|||
#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT
|
||||
#include "InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.h"
|
||||
#endif
|
||||
#ifdef CIFACE_USE_STEAMDECK
|
||||
#include "InputCommon/ControllerInterface/SteamDeck/SteamDeck.h"
|
||||
#endif
|
||||
|
||||
ControllerInterface g_controller_interface;
|
||||
|
||||
|
@ -61,7 +63,7 @@ void ControllerInterface::Initialize(const WindowSystemInfo& wsi)
|
|||
// nothing needed
|
||||
#endif
|
||||
#ifdef CIFACE_USE_OSX
|
||||
// nothing needed for OSX and Quartz
|
||||
// nothing needed for Quartz
|
||||
#endif
|
||||
#ifdef CIFACE_USE_SDL
|
||||
m_input_backends.emplace_back(ciface::SDL::CreateInputBackend(this));
|
||||
|
@ -78,6 +80,9 @@ void ControllerInterface::Initialize(const WindowSystemInfo& wsi)
|
|||
#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT
|
||||
m_input_backends.emplace_back(ciface::DualShockUDPClient::CreateInputBackend(this));
|
||||
#endif
|
||||
#ifdef CIFACE_USE_STEAMDECK
|
||||
m_input_backends.emplace_back(ciface::SteamDeck::CreateInputBackend(this));
|
||||
#endif
|
||||
|
||||
// Don't allow backends to add devices before the first RefreshDevices() as they will be cleaned
|
||||
// there. Or they'd end up waiting on the devices mutex if populated from another thread.
|
||||
|
@ -114,18 +119,6 @@ void ControllerInterface::RefreshDevices(RefreshReason reason)
|
|||
if (!m_is_init)
|
||||
return;
|
||||
|
||||
#ifdef CIFACE_USE_OSX
|
||||
if (m_wsi.type == WindowSystemType::MacOS)
|
||||
{
|
||||
std::lock_guard lk_pre_population(m_pre_population_mutex);
|
||||
// This is needed to stop its threads before locking our mutexes, to avoid deadlocks
|
||||
// (in case it tried to add a device after we had locked m_devices_population_mutex).
|
||||
// There doesn't seem to be an easy to way to repopulate OSX devices without restarting its
|
||||
// hotplug thread. This should not remove its devices, and if it did, calls should be ignored.
|
||||
ciface::OSX::DeInit();
|
||||
}
|
||||
#endif
|
||||
|
||||
// We lock m_devices_population_mutex here to make everything simpler.
|
||||
// Multiple devices classes have their own "hotplug" thread, and can add/remove devices at any
|
||||
// time, while actual writes to "m_devices" are safe, the order in which they happen is not. That
|
||||
|
@ -174,10 +167,6 @@ void ControllerInterface::RefreshDevices(RefreshReason reason)
|
|||
#ifdef CIFACE_USE_OSX
|
||||
if (m_wsi.type == WindowSystemType::MacOS)
|
||||
{
|
||||
{
|
||||
std::lock_guard lk_pre_population(m_pre_population_mutex);
|
||||
ciface::OSX::Init();
|
||||
}
|
||||
ciface::Quartz::PopulateDevices(m_wsi.render_window);
|
||||
}
|
||||
#endif
|
||||
|
@ -233,7 +222,6 @@ void ControllerInterface::Shutdown()
|
|||
// nothing needed
|
||||
#endif
|
||||
#ifdef CIFACE_USE_OSX
|
||||
ciface::OSX::DeInit();
|
||||
ciface::Quartz::DeInit();
|
||||
#endif
|
||||
#ifdef CIFACE_USE_ANDROID
|
||||
|
|
|
@ -34,6 +34,9 @@
|
|||
#if defined(HAVE_SDL2)
|
||||
#define CIFACE_USE_SDL
|
||||
#endif
|
||||
#if defined(HAVE_HIDAPI)
|
||||
#define CIFACE_USE_STEAMDECK
|
||||
#endif
|
||||
|
||||
namespace ciface
|
||||
{
|
||||
|
|
|
@ -43,7 +43,7 @@ std::string GetExpressionForControl(const std::string& control_name,
|
|||
{
|
||||
// If our expression contains any non-alpha characters
|
||||
// we should quote it
|
||||
if (!std::all_of(expr.begin(), expr.end(), IsAlpha))
|
||||
if (!std::all_of(expr.begin(), expr.end(), Common::IsAlpha))
|
||||
expr = fmt::format("`{}`", expr);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
// Copyright 2010 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace ciface::OSX
|
||||
{
|
||||
void Init();
|
||||
void DeInit();
|
||||
|
||||
void DeviceElementDebugPrint(const void*, void*);
|
||||
} // namespace ciface::OSX
|
|
@ -1,222 +0,0 @@
|
|||
// Copyright 2013 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "InputCommon/ControllerInterface/OSX/OSX.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <Foundation/Foundation.h>
|
||||
#include <IOKit/hid/IOHIDLib.h>
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Common/Thread.h"
|
||||
|
||||
#include "InputCommon/ControllerInterface/ControllerInterface.h"
|
||||
#include "InputCommon/ControllerInterface/OSX/OSXJoystick.h"
|
||||
#include "InputCommon/ControllerInterface/OSX/RunLoopStopper.h"
|
||||
|
||||
namespace ciface::OSX
|
||||
{
|
||||
constexpr CFTimeInterval FOREVER = 1e20;
|
||||
static std::thread s_hotplug_thread;
|
||||
static RunLoopStopper s_stopper;
|
||||
static IOHIDManagerRef HIDManager = nullptr;
|
||||
static CFStringRef OurRunLoop = CFSTR("DolphinOSXInput");
|
||||
|
||||
void DeviceElementDebugPrint(const void* value, void* context)
|
||||
{
|
||||
IOHIDElementRef e = (IOHIDElementRef)value;
|
||||
bool recurse = false;
|
||||
if (context)
|
||||
recurse = *(bool*)context;
|
||||
|
||||
std::string type = "";
|
||||
switch (IOHIDElementGetType(e))
|
||||
{
|
||||
case kIOHIDElementTypeInput_Axis:
|
||||
type = "axis";
|
||||
break;
|
||||
case kIOHIDElementTypeInput_Button:
|
||||
type = "button";
|
||||
break;
|
||||
case kIOHIDElementTypeInput_Misc:
|
||||
type = "misc";
|
||||
break;
|
||||
case kIOHIDElementTypeInput_ScanCodes:
|
||||
type = "scancodes";
|
||||
break;
|
||||
case kIOHIDElementTypeOutput:
|
||||
type = "output";
|
||||
break;
|
||||
case kIOHIDElementTypeFeature:
|
||||
type = "feature";
|
||||
break;
|
||||
case kIOHIDElementTypeCollection:
|
||||
type = "collection";
|
||||
break;
|
||||
}
|
||||
|
||||
std::string c_type = "";
|
||||
if (type == "collection")
|
||||
{
|
||||
switch (IOHIDElementGetCollectionType(e))
|
||||
{
|
||||
case kIOHIDElementCollectionTypePhysical:
|
||||
c_type = "physical";
|
||||
break;
|
||||
case kIOHIDElementCollectionTypeApplication:
|
||||
c_type = "application";
|
||||
break;
|
||||
case kIOHIDElementCollectionTypeLogical:
|
||||
c_type = "logical";
|
||||
break;
|
||||
case kIOHIDElementCollectionTypeReport:
|
||||
c_type = "report";
|
||||
break;
|
||||
case kIOHIDElementCollectionTypeNamedArray:
|
||||
c_type = "namedArray";
|
||||
break;
|
||||
case kIOHIDElementCollectionTypeUsageSwitch:
|
||||
c_type = "usageSwitch";
|
||||
break;
|
||||
case kIOHIDElementCollectionTypeUsageModifier:
|
||||
c_type = "usageModifier";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
c_type.append(" ");
|
||||
NSLog(@"%s%s%spage: 0x%x usage: 0x%x name: %@ "
|
||||
"lmin: %ld lmax: %ld pmin: %ld pmax: %ld",
|
||||
type.c_str(), type == "collection" ? ":" : "", type == "collection" ? c_type.c_str() : " ",
|
||||
IOHIDElementGetUsagePage(e), IOHIDElementGetUsage(e),
|
||||
IOHIDElementGetName(e), // usually just nullptr
|
||||
IOHIDElementGetLogicalMin(e), IOHIDElementGetLogicalMax(e), IOHIDElementGetPhysicalMin(e),
|
||||
IOHIDElementGetPhysicalMax(e));
|
||||
|
||||
if ((type == "collection") && recurse)
|
||||
{
|
||||
CFArrayRef elements = IOHIDElementGetChildren(e);
|
||||
CFRange range = {0, CFArrayGetCount(elements)};
|
||||
// this leaks...but it's just debug code, right? :D
|
||||
CFArrayApplyFunction(elements, range, DeviceElementDebugPrint, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
static void DeviceDebugPrint(IOHIDDeviceRef device)
|
||||
{
|
||||
#if 0
|
||||
#define shortlog(x) NSLog(@"%s: %@", x, IOHIDDeviceGetProperty(device, CFSTR(x)));
|
||||
NSLog(@"-------------------------");
|
||||
NSLog(@"Got Device: %@",
|
||||
IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)));
|
||||
shortlog(kIOHIDTransportKey)
|
||||
shortlog(kIOHIDVendorIDKey)
|
||||
shortlog(kIOHIDVendorIDSourceKey)
|
||||
shortlog(kIOHIDProductIDKey)
|
||||
shortlog(kIOHIDVersionNumberKey)
|
||||
shortlog(kIOHIDManufacturerKey)
|
||||
shortlog(kIOHIDProductKey)
|
||||
shortlog(kIOHIDSerialNumberKey)
|
||||
shortlog(kIOHIDCountryCodeKey)
|
||||
shortlog(kIOHIDLocationIDKey)
|
||||
shortlog(kIOHIDDeviceUsageKey)
|
||||
shortlog(kIOHIDDeviceUsagePageKey)
|
||||
shortlog(kIOHIDDeviceUsagePairsKey)
|
||||
shortlog(kIOHIDPrimaryUsageKey)
|
||||
shortlog(kIOHIDPrimaryUsagePageKey)
|
||||
shortlog(kIOHIDMaxInputReportSizeKey)
|
||||
shortlog(kIOHIDMaxOutputReportSizeKey)
|
||||
shortlog(kIOHIDMaxFeatureReportSizeKey)
|
||||
shortlog(kIOHIDReportIntervalKey)
|
||||
shortlog(kIOHIDReportDescriptorKey)
|
||||
#endif
|
||||
}
|
||||
|
||||
static std::string GetDeviceRefName(IOHIDDeviceRef inIOHIDDeviceRef)
|
||||
{
|
||||
const NSString* name = reinterpret_cast<const NSString*>(
|
||||
IOHIDDeviceGetProperty(inIOHIDDeviceRef, CFSTR(kIOHIDProductKey)));
|
||||
return (name != nullptr) ? std::string(StripWhitespace([name UTF8String])) : "Unknown device";
|
||||
}
|
||||
|
||||
static void DeviceRemovalCallback(void* inContext, IOReturn inResult, void* inSender,
|
||||
IOHIDDeviceRef inIOHIDDeviceRef)
|
||||
{
|
||||
g_controller_interface.RemoveDevice([&inIOHIDDeviceRef](const auto* device) {
|
||||
const Joystick* joystick = dynamic_cast<const Joystick*>(device);
|
||||
if (joystick && joystick->IsSameDevice(inIOHIDDeviceRef))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
static void DeviceMatchingCallback(void* inContext, IOReturn inResult, void* inSender,
|
||||
IOHIDDeviceRef inIOHIDDeviceRef)
|
||||
{
|
||||
DeviceDebugPrint(inIOHIDDeviceRef);
|
||||
std::string name = GetDeviceRefName(inIOHIDDeviceRef);
|
||||
|
||||
// Add a device if it's of a type we want
|
||||
if (IOHIDDeviceConformsTo(inIOHIDDeviceRef, kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick) ||
|
||||
IOHIDDeviceConformsTo(inIOHIDDeviceRef, kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad) ||
|
||||
IOHIDDeviceConformsTo(inIOHIDDeviceRef, kHIDPage_GenericDesktop,
|
||||
kHIDUsage_GD_MultiAxisController))
|
||||
{
|
||||
g_controller_interface.AddDevice(std::make_shared<Joystick>(inIOHIDDeviceRef, name));
|
||||
}
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
HIDManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
|
||||
if (!HIDManager)
|
||||
ERROR_LOG_FMT(CONTROLLERINTERFACE, "Failed to create HID Manager reference");
|
||||
|
||||
IOHIDManagerSetDeviceMatching(HIDManager, nullptr);
|
||||
if (IOHIDManagerOpen(HIDManager, kIOHIDOptionsTypeNone) != kIOReturnSuccess)
|
||||
ERROR_LOG_FMT(CONTROLLERINTERFACE, "Failed to open HID Manager");
|
||||
|
||||
// Callbacks for acquisition or loss of a matching device
|
||||
IOHIDManagerRegisterDeviceMatchingCallback(HIDManager, DeviceMatchingCallback, nullptr);
|
||||
IOHIDManagerRegisterDeviceRemovalCallback(HIDManager, DeviceRemovalCallback, nullptr);
|
||||
|
||||
// Match devices that are plugged in right now
|
||||
IOHIDManagerScheduleWithRunLoop(HIDManager, CFRunLoopGetCurrent(), OurRunLoop);
|
||||
while (CFRunLoopRunInMode(OurRunLoop, 0, TRUE) == kCFRunLoopRunHandledSource)
|
||||
{
|
||||
};
|
||||
IOHIDManagerUnscheduleFromRunLoop(HIDManager, CFRunLoopGetCurrent(), OurRunLoop);
|
||||
|
||||
// Enable hotplugging
|
||||
s_hotplug_thread = std::thread([] {
|
||||
Common::SetCurrentThreadName("IOHIDManager Hotplug Thread");
|
||||
NOTICE_LOG_FMT(CONTROLLERINTERFACE, "IOHIDManager hotplug thread started");
|
||||
|
||||
IOHIDManagerScheduleWithRunLoop(HIDManager, CFRunLoopGetCurrent(), OurRunLoop);
|
||||
s_stopper.AddToRunLoop(CFRunLoopGetCurrent(), OurRunLoop);
|
||||
CFRunLoopRunInMode(OurRunLoop, FOREVER, FALSE);
|
||||
s_stopper.RemoveFromRunLoop(CFRunLoopGetCurrent(), OurRunLoop);
|
||||
IOHIDManagerUnscheduleFromRunLoop(HIDManager, CFRunLoopGetCurrent(), OurRunLoop);
|
||||
|
||||
NOTICE_LOG_FMT(CONTROLLERINTERFACE, "IOHIDManager hotplug thread stopped");
|
||||
});
|
||||
}
|
||||
|
||||
void DeInit()
|
||||
{
|
||||
if (HIDManager)
|
||||
{
|
||||
s_stopper.Signal();
|
||||
s_hotplug_thread.join();
|
||||
|
||||
// This closes all devices as well
|
||||
IOHIDManagerClose(HIDManager, kIOHIDOptionsTypeNone);
|
||||
CFRelease(HIDManager);
|
||||
HIDManager = nullptr;
|
||||
}
|
||||
}
|
||||
} // namespace ciface::OSX
|
|
@ -1,90 +0,0 @@
|
|||
// Copyright 2010 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
|
||||
#include <IOKit/hid/IOHIDLib.h>
|
||||
|
||||
#include "InputCommon/ControllerInterface/CoreDevice.h"
|
||||
#include "InputCommon/ControllerInterface/ForceFeedback/ForceFeedbackDevice.h"
|
||||
|
||||
namespace ciface::OSX
|
||||
{
|
||||
class Joystick : public ForceFeedback::ForceFeedbackDevice
|
||||
{
|
||||
private:
|
||||
class Button : public Input
|
||||
{
|
||||
public:
|
||||
Button(IOHIDElementRef element, IOHIDDeviceRef device) : m_element(element), m_device(device) {}
|
||||
std::string GetName() const override;
|
||||
ControlState GetState() const override;
|
||||
|
||||
private:
|
||||
const IOHIDElementRef m_element;
|
||||
const IOHIDDeviceRef m_device;
|
||||
};
|
||||
|
||||
class Axis : public Input
|
||||
{
|
||||
public:
|
||||
enum direction
|
||||
{
|
||||
positive = 0,
|
||||
negative
|
||||
};
|
||||
|
||||
Axis(IOHIDElementRef element, IOHIDDeviceRef device, direction dir);
|
||||
std::string GetName() const override;
|
||||
ControlState GetState() const override;
|
||||
|
||||
private:
|
||||
const IOHIDElementRef m_element;
|
||||
const IOHIDDeviceRef m_device;
|
||||
std::string m_name;
|
||||
const direction m_direction;
|
||||
float m_neutral;
|
||||
float m_scale;
|
||||
};
|
||||
|
||||
class Hat : public Input
|
||||
{
|
||||
public:
|
||||
enum direction
|
||||
{
|
||||
up = 0,
|
||||
right,
|
||||
down,
|
||||
left
|
||||
};
|
||||
|
||||
Hat(IOHIDElementRef element, IOHIDDeviceRef device, direction dir);
|
||||
std::string GetName() const override;
|
||||
ControlState GetState() const override;
|
||||
|
||||
private:
|
||||
const IOHIDElementRef m_element;
|
||||
const IOHIDDeviceRef m_device;
|
||||
const char* m_name;
|
||||
const direction m_direction;
|
||||
};
|
||||
|
||||
public:
|
||||
Joystick(IOHIDDeviceRef device, std::string name);
|
||||
~Joystick();
|
||||
|
||||
std::string GetName() const override;
|
||||
std::string GetSource() const override;
|
||||
bool IsSameDevice(const IOHIDDeviceRef) const;
|
||||
|
||||
private:
|
||||
const IOHIDDeviceRef m_device;
|
||||
const std::string m_device_name;
|
||||
|
||||
void AddElements(CFArrayRef elements, std::set<IOHIDElementCookie>& cookies);
|
||||
|
||||
ForceFeedback::FFDeviceAdapterReference m_ff_device;
|
||||
};
|
||||
} // namespace ciface::OSX
|
|
@ -1,342 +0,0 @@
|
|||
// Copyright 2013 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "InputCommon/ControllerInterface/OSX/OSXJoystick.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
#include <IOKit/hid/IOHIDLib.h>
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
namespace ciface::OSX
|
||||
{
|
||||
void Joystick::AddElements(CFArrayRef elements, std::set<IOHIDElementCookie>& cookies)
|
||||
{
|
||||
for (int i = 0; i < CFArrayGetCount(elements); i++)
|
||||
{
|
||||
IOHIDElementRef e = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
|
||||
|
||||
const uint32_t type = IOHIDElementGetType(e);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case kIOHIDElementTypeCollection:
|
||||
AddElements(IOHIDElementGetChildren(e), cookies);
|
||||
continue;
|
||||
case kIOHIDElementTypeOutput:
|
||||
continue;
|
||||
}
|
||||
|
||||
IOHIDElementCookie cookie = IOHIDElementGetCookie(e);
|
||||
|
||||
// Check for any existing elements with the same cookie
|
||||
if (cookies.count(cookie) > 0)
|
||||
continue;
|
||||
|
||||
cookies.insert(cookie);
|
||||
|
||||
const uint32_t usage = IOHIDElementGetUsage(e);
|
||||
|
||||
switch (usage)
|
||||
{
|
||||
// Axis
|
||||
case kHIDUsage_GD_X:
|
||||
case kHIDUsage_GD_Y:
|
||||
case kHIDUsage_GD_Z:
|
||||
case kHIDUsage_GD_Rx:
|
||||
case kHIDUsage_GD_Ry:
|
||||
case kHIDUsage_GD_Rz:
|
||||
case kHIDUsage_GD_Slider:
|
||||
case kHIDUsage_GD_Dial:
|
||||
case kHIDUsage_GD_Wheel:
|
||||
case kHIDUsage_GD_Hatswitch:
|
||||
// Simulator
|
||||
case kHIDUsage_Sim_Accelerator:
|
||||
case kHIDUsage_Sim_Brake:
|
||||
case kHIDUsage_Sim_Rudder:
|
||||
case kHIDUsage_Sim_Throttle:
|
||||
{
|
||||
if (usage == kHIDUsage_GD_Hatswitch)
|
||||
{
|
||||
AddInput(new Hat(e, m_device, Hat::up));
|
||||
AddInput(new Hat(e, m_device, Hat::right));
|
||||
AddInput(new Hat(e, m_device, Hat::down));
|
||||
AddInput(new Hat(e, m_device, Hat::left));
|
||||
}
|
||||
else
|
||||
{
|
||||
AddAnalogInputs(new Axis(e, m_device, Axis::negative),
|
||||
new Axis(e, m_device, Axis::positive));
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Buttons
|
||||
case kHIDUsage_GD_DPadUp:
|
||||
case kHIDUsage_GD_DPadDown:
|
||||
case kHIDUsage_GD_DPadRight:
|
||||
case kHIDUsage_GD_DPadLeft:
|
||||
case kHIDUsage_GD_Start:
|
||||
case kHIDUsage_GD_Select:
|
||||
case kHIDUsage_GD_SystemMainMenu:
|
||||
AddInput(new Button(e, m_device));
|
||||
break;
|
||||
|
||||
default:
|
||||
// Catch any easily identifiable axis and buttons that slipped through
|
||||
if (type == kIOHIDElementTypeInput_Button)
|
||||
{
|
||||
AddInput(new Button(e, m_device));
|
||||
break;
|
||||
}
|
||||
|
||||
const uint32_t usage_page = IOHIDElementGetUsagePage(e);
|
||||
|
||||
if (usage_page == kHIDPage_Button || usage_page == kHIDPage_Consumer)
|
||||
{
|
||||
AddInput(new Button(e, m_device));
|
||||
break;
|
||||
}
|
||||
|
||||
if (type == kIOHIDElementTypeInput_Axis)
|
||||
{
|
||||
AddAnalogInputs(new Axis(e, m_device, Axis::negative),
|
||||
new Axis(e, m_device, Axis::positive));
|
||||
break;
|
||||
}
|
||||
|
||||
NOTICE_LOG_FMT(CONTROLLERINTERFACE,
|
||||
"Unknown IOHIDElement, ignoring (Usage: {:x}, Type: {:x})", usage,
|
||||
IOHIDElementGetType(e));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Joystick::Joystick(IOHIDDeviceRef device, std::string name)
|
||||
: m_device(device), m_device_name(name), m_ff_device(nullptr)
|
||||
{
|
||||
CFArrayRef elements = IOHIDDeviceCopyMatchingElements(m_device, nullptr, kIOHIDOptionsTypeNone);
|
||||
std::set<IOHIDElementCookie> known_cookies;
|
||||
AddElements(elements, known_cookies);
|
||||
|
||||
// Force Feedback
|
||||
FFCAPABILITIES ff_caps;
|
||||
if (SUCCEEDED(
|
||||
ForceFeedback::FFDeviceAdapter::Create(IOHIDDeviceGetService(m_device), &m_ff_device)) &&
|
||||
SUCCEEDED(FFDeviceGetForceFeedbackCapabilities(m_ff_device->m_device, &ff_caps)))
|
||||
{
|
||||
InitForceFeedback(m_ff_device, ff_caps.numFfAxes);
|
||||
}
|
||||
}
|
||||
|
||||
Joystick::~Joystick()
|
||||
{
|
||||
DeInitForceFeedback();
|
||||
|
||||
if (m_ff_device)
|
||||
m_ff_device->Release();
|
||||
}
|
||||
|
||||
std::string Joystick::GetName() const
|
||||
{
|
||||
return m_device_name;
|
||||
}
|
||||
|
||||
std::string Joystick::GetSource() const
|
||||
{
|
||||
return "Input";
|
||||
}
|
||||
|
||||
ControlState Joystick::Button::GetState() const
|
||||
{
|
||||
IOHIDValueRef value;
|
||||
if (IOHIDDeviceGetValue(m_device, m_element, &value) == kIOReturnSuccess)
|
||||
return IOHIDValueGetIntegerValue(value);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string Joystick::Button::GetName() const
|
||||
{
|
||||
std::ostringstream s;
|
||||
s << IOHIDElementGetUsage(m_element);
|
||||
return std::string("Button ").append(StripWhitespace(s.str()));
|
||||
}
|
||||
|
||||
Joystick::Axis::Axis(IOHIDElementRef element, IOHIDDeviceRef device, direction dir)
|
||||
: m_element(element), m_device(device), m_direction(dir)
|
||||
{
|
||||
// Need to parse the element a bit first
|
||||
std::string description("unk");
|
||||
|
||||
int const usage = IOHIDElementGetUsage(m_element);
|
||||
switch (usage)
|
||||
{
|
||||
case kHIDUsage_GD_X:
|
||||
description = "X";
|
||||
break;
|
||||
case kHIDUsage_GD_Y:
|
||||
description = "Y";
|
||||
break;
|
||||
case kHIDUsage_GD_Z:
|
||||
description = "Z";
|
||||
break;
|
||||
case kHIDUsage_GD_Rx:
|
||||
description = "Rx";
|
||||
break;
|
||||
case kHIDUsage_GD_Ry:
|
||||
description = "Ry";
|
||||
break;
|
||||
case kHIDUsage_GD_Rz:
|
||||
description = "Rz";
|
||||
break;
|
||||
case kHIDUsage_GD_Wheel:
|
||||
description = "Wheel";
|
||||
break;
|
||||
case kHIDUsage_Csmr_ACPan:
|
||||
description = "Pan";
|
||||
break;
|
||||
default:
|
||||
{
|
||||
IOHIDElementCookie elementCookie = IOHIDElementGetCookie(m_element);
|
||||
// This axis isn't a 'well-known' one so cook a descriptive and uniquely
|
||||
// identifiable name. macOS provides a 'cookie' for each element that
|
||||
// will persist between sessions and identify the same physical controller
|
||||
// element so we can use that as a component of the axis name
|
||||
std::ostringstream s;
|
||||
s << "CK-";
|
||||
s << elementCookie;
|
||||
description = StripWhitespace(s.str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_name = std::string("Axis ") + description;
|
||||
m_name.append((m_direction == positive) ? "+" : "-");
|
||||
|
||||
m_neutral = (IOHIDElementGetLogicalMax(m_element) + IOHIDElementGetLogicalMin(m_element)) / 2.;
|
||||
m_scale = 1 / fabs(IOHIDElementGetLogicalMax(m_element) - m_neutral);
|
||||
}
|
||||
|
||||
ControlState Joystick::Axis::GetState() const
|
||||
{
|
||||
IOHIDValueRef value;
|
||||
|
||||
if (IOHIDDeviceGetValue(m_device, m_element, &value) == kIOReturnSuccess)
|
||||
{
|
||||
// IOHIDValueGetIntegerValue() crashes when trying
|
||||
// to convert unusually large element values.
|
||||
if (IOHIDValueGetLength(value) > 2)
|
||||
return 0;
|
||||
|
||||
float position = IOHIDValueGetIntegerValue(value);
|
||||
|
||||
if (m_direction == positive && position > m_neutral)
|
||||
return (position - m_neutral) * m_scale;
|
||||
if (m_direction == negative && position < m_neutral)
|
||||
return (m_neutral - position) * m_scale;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string Joystick::Axis::GetName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
Joystick::Hat::Hat(IOHIDElementRef element, IOHIDDeviceRef device, direction dir)
|
||||
: m_element(element), m_device(device), m_direction(dir)
|
||||
{
|
||||
switch (dir)
|
||||
{
|
||||
case up:
|
||||
m_name = "Up";
|
||||
break;
|
||||
case right:
|
||||
m_name = "Right";
|
||||
break;
|
||||
case down:
|
||||
m_name = "Down";
|
||||
break;
|
||||
case left:
|
||||
m_name = "Left";
|
||||
break;
|
||||
default:
|
||||
m_name = "unk";
|
||||
}
|
||||
}
|
||||
|
||||
ControlState Joystick::Hat::GetState() const
|
||||
{
|
||||
IOHIDValueRef value;
|
||||
|
||||
if (IOHIDDeviceGetValue(m_device, m_element, &value) == kIOReturnSuccess)
|
||||
{
|
||||
int position = IOHIDValueGetIntegerValue(value);
|
||||
int min = IOHIDElementGetLogicalMin(m_element);
|
||||
int max = IOHIDElementGetLogicalMax(m_element);
|
||||
|
||||
// if the position is outside the min or max, don't register it as a valid button press
|
||||
if (position < min || position > max)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// normalize the position so that its lowest value is 0
|
||||
position -= min;
|
||||
|
||||
switch (position)
|
||||
{
|
||||
case 0:
|
||||
if (m_direction == up)
|
||||
return 1;
|
||||
break;
|
||||
case 1:
|
||||
if (m_direction == up || m_direction == right)
|
||||
return 1;
|
||||
break;
|
||||
case 2:
|
||||
if (m_direction == right)
|
||||
return 1;
|
||||
break;
|
||||
case 3:
|
||||
if (m_direction == right || m_direction == down)
|
||||
return 1;
|
||||
break;
|
||||
case 4:
|
||||
if (m_direction == down)
|
||||
return 1;
|
||||
break;
|
||||
case 5:
|
||||
if (m_direction == down || m_direction == left)
|
||||
return 1;
|
||||
break;
|
||||
case 6:
|
||||
if (m_direction == left)
|
||||
return 1;
|
||||
break;
|
||||
case 7:
|
||||
if (m_direction == left || m_direction == up)
|
||||
return 1;
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string Joystick::Hat::GetName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
bool Joystick::IsSameDevice(const IOHIDDeviceRef other_device) const
|
||||
{
|
||||
return m_device == other_device;
|
||||
}
|
||||
} // namespace ciface::OSX
|
|
@ -1,40 +0,0 @@
|
|||
// Copyright 2016 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
namespace ciface::OSX
|
||||
{
|
||||
class RunLoopStopper
|
||||
{
|
||||
CFRunLoopSourceRef m_source;
|
||||
CFRunLoopRef m_runloop = nullptr;
|
||||
|
||||
public:
|
||||
RunLoopStopper()
|
||||
{
|
||||
CFRunLoopSourceContext ctx = {.version = 0,
|
||||
.perform = [](void*) { CFRunLoopStop(CFRunLoopGetCurrent()); }};
|
||||
m_source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &ctx);
|
||||
}
|
||||
~RunLoopStopper() { CFRelease(m_source); }
|
||||
void Signal()
|
||||
{
|
||||
CFRunLoopSourceSignal(m_source);
|
||||
if (m_runloop != nullptr)
|
||||
CFRunLoopWakeUp(m_runloop);
|
||||
}
|
||||
void AddToRunLoop(CFRunLoopRef runloop, CFStringRef mode)
|
||||
{
|
||||
m_runloop = runloop;
|
||||
CFRunLoopAddSource(runloop, m_source, mode);
|
||||
}
|
||||
void RemoveFromRunLoop(CFRunLoopRef runloop, CFStringRef mode)
|
||||
{
|
||||
m_runloop = nullptr;
|
||||
CFRunLoopRemoveSource(runloop, m_source, mode);
|
||||
}
|
||||
};
|
||||
} // namespace ciface::OSX
|
|
@ -0,0 +1,304 @@
|
|||
// Copyright 2023 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "InputCommon/ControllerInterface/SteamDeck/SteamDeck.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <hidapi.h>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/MathUtil.h"
|
||||
#include "InputCommon/ControllerInterface/ControllerInterface.h"
|
||||
|
||||
namespace ciface::SteamDeck
|
||||
{
|
||||
constexpr std::string_view STEAMDECK_SOURCE_NAME = "SteamDeck";
|
||||
|
||||
struct DeckInputReport
|
||||
{
|
||||
// +0
|
||||
u8 major_ver;
|
||||
u8 minor_ver;
|
||||
u8 report_type;
|
||||
u8 report_sz;
|
||||
|
||||
u32 frame;
|
||||
|
||||
// +8
|
||||
u32 buttons0;
|
||||
u32 buttons1;
|
||||
|
||||
// +16
|
||||
s16 l_pad_x;
|
||||
s16 l_pad_y;
|
||||
s16 r_pad_x;
|
||||
s16 r_pad_y;
|
||||
|
||||
// +24
|
||||
s16 accel_x;
|
||||
s16 accel_y;
|
||||
s16 accel_z;
|
||||
s16 gyro_pitch;
|
||||
s16 gyro_roll;
|
||||
s16 gyro_yaw;
|
||||
s16 pose_quat_w;
|
||||
s16 pose_quat_x;
|
||||
s16 pose_quat_y;
|
||||
s16 pose_quat_z;
|
||||
|
||||
// +44
|
||||
u16 l_trig;
|
||||
u16 r_trig;
|
||||
|
||||
// +48
|
||||
s16 l_stick_x;
|
||||
s16 l_stick_y;
|
||||
s16 r_stick_x;
|
||||
s16 r_stick_y;
|
||||
|
||||
// +56
|
||||
u16 l_pad_force;
|
||||
u16 r_pad_force;
|
||||
s16 l_stick_capa;
|
||||
s16 r_stick_capa;
|
||||
};
|
||||
static_assert(sizeof(DeckInputReport) == 64);
|
||||
|
||||
class Device final : public Core::Device
|
||||
{
|
||||
private:
|
||||
class Button final : public Input
|
||||
{
|
||||
public:
|
||||
Button(const char* name, const u32& buttons, u32 mask)
|
||||
: m_name(name), m_buttons(buttons), m_mask(mask)
|
||||
{
|
||||
}
|
||||
std::string GetName() const override { return m_name; }
|
||||
ControlState GetState() const override { return (m_buttons & m_mask) != 0; }
|
||||
|
||||
private:
|
||||
const char* const m_name;
|
||||
const u32& m_buttons;
|
||||
const u32 m_mask;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class AnalogInput : public Input
|
||||
{
|
||||
public:
|
||||
AnalogInput(const char* name, const T& input, ControlState range)
|
||||
: m_name(name), m_input(input), m_range(range)
|
||||
{
|
||||
}
|
||||
std::string GetName() const final override { return m_name; }
|
||||
ControlState GetState() const final override { return ControlState(m_input) / m_range; }
|
||||
|
||||
private:
|
||||
const char* m_name;
|
||||
const T& m_input;
|
||||
const ControlState m_range;
|
||||
};
|
||||
|
||||
class MotionInput final : public AnalogInput<s16>
|
||||
{
|
||||
public:
|
||||
using AnalogInput::AnalogInput;
|
||||
bool IsDetectable() const override { return false; }
|
||||
};
|
||||
|
||||
public:
|
||||
Device(hid_device* device);
|
||||
std::string GetName() const final override;
|
||||
std::string GetSource() const final override;
|
||||
void UpdateInput() override;
|
||||
|
||||
private:
|
||||
hid_device* m_device;
|
||||
DeckInputReport m_latest_input;
|
||||
};
|
||||
|
||||
class InputBackend final : public ciface::InputBackend
|
||||
{
|
||||
public:
|
||||
InputBackend(ControllerInterface* controller_interface);
|
||||
void PopulateDevices() override;
|
||||
};
|
||||
|
||||
std::unique_ptr<ciface::InputBackend> CreateInputBackend(ControllerInterface* controller_interface)
|
||||
{
|
||||
return std::make_unique<InputBackend>(controller_interface);
|
||||
}
|
||||
|
||||
InputBackend::InputBackend(ControllerInterface* controller_interface)
|
||||
: ciface::InputBackend(controller_interface)
|
||||
{
|
||||
}
|
||||
|
||||
void InputBackend::PopulateDevices()
|
||||
{
|
||||
INFO_LOG_FMT(CONTROLLERINTERFACE, "SteamDeck PopulateDevices");
|
||||
|
||||
auto possible_devices = hid_enumerate(0x28de, 0x1205);
|
||||
bool was_found = false;
|
||||
std::string found_path;
|
||||
auto this_device = possible_devices;
|
||||
|
||||
while (this_device)
|
||||
{
|
||||
INFO_LOG_FMT(CONTROLLERINTERFACE, "Found {} (interface {})", this_device->path,
|
||||
this_device->interface_number);
|
||||
|
||||
if (this_device->interface_number == 2)
|
||||
{
|
||||
was_found = true;
|
||||
found_path = this_device->path;
|
||||
break;
|
||||
}
|
||||
|
||||
this_device = this_device->next;
|
||||
}
|
||||
|
||||
hid_free_enumeration(possible_devices);
|
||||
|
||||
if (was_found)
|
||||
{
|
||||
INFO_LOG_FMT(CONTROLLERINTERFACE, "Found Steam Deck controls at {}", found_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
INFO_LOG_FMT(CONTROLLERINTERFACE, "No Steam Deck interface found");
|
||||
return;
|
||||
}
|
||||
|
||||
auto deck_dev = hid_open_path(found_path.c_str());
|
||||
if (!deck_dev)
|
||||
{
|
||||
ERROR_LOG_FMT(CONTROLLERINTERFACE, "Steam Deck controls could not be opened");
|
||||
return;
|
||||
}
|
||||
if (hid_set_nonblocking(deck_dev, 1) != 0)
|
||||
{
|
||||
ERROR_LOG_FMT(CONTROLLERINTERFACE, "Steam Deck controls could not be set nonblocking");
|
||||
return;
|
||||
}
|
||||
|
||||
GetControllerInterface().RemoveDevice(
|
||||
[](const auto* dev) { return dev->GetSource() == STEAMDECK_SOURCE_NAME; });
|
||||
|
||||
GetControllerInterface().AddDevice(std::make_shared<Device>(deck_dev));
|
||||
}
|
||||
|
||||
Device::Device(hid_device* device) : m_device{device}
|
||||
{
|
||||
// clang-format off
|
||||
AddInput(new Button("R2 Full Pull", m_latest_input.buttons0, 0x00000001));
|
||||
AddInput(new Button("L2 Full Pull", m_latest_input.buttons0, 0x00000002));
|
||||
AddInput(new Button("R1", m_latest_input.buttons0, 0x00000004));
|
||||
AddInput(new Button("L1", m_latest_input.buttons0, 0x00000008));
|
||||
AddInput(new Button("Y", m_latest_input.buttons0, 0x00000010));
|
||||
AddInput(new Button("B", m_latest_input.buttons0, 0x00000020));
|
||||
AddInput(new Button("X", m_latest_input.buttons0, 0x00000040));
|
||||
AddInput(new Button("A", m_latest_input.buttons0, 0x00000080));
|
||||
AddInput(new Button("D-Pad Up", m_latest_input.buttons0, 0x00000100));
|
||||
AddInput(new Button("D-Pad Right", m_latest_input.buttons0, 0x00000200));
|
||||
AddInput(new Button("D-Pad Left", m_latest_input.buttons0, 0x00000400));
|
||||
AddInput(new Button("D-Pad Down", m_latest_input.buttons0, 0x00000800));
|
||||
AddInput(new Button("View", m_latest_input.buttons0, 0x00001000));
|
||||
AddInput(new Button("Steam", m_latest_input.buttons0, 0x00002000));
|
||||
AddInput(new Button("Menu", m_latest_input.buttons0, 0x00004000));
|
||||
AddInput(new Button("L5", m_latest_input.buttons0, 0x00008000));
|
||||
AddInput(new Button("R5", m_latest_input.buttons0, 0x00010000));
|
||||
AddInput(new Button("Left Trackpad Click", m_latest_input.buttons0, 0x00020000));
|
||||
AddInput(new Button("Right Trackpad Click", m_latest_input.buttons0, 0x00040000));
|
||||
AddInput(new Button("Left Trackpad Touch", m_latest_input.buttons0, 0x00080000));
|
||||
AddInput(new Button("Right Trackpad Touch", m_latest_input.buttons0, 0x00100000));
|
||||
AddInput(new Button("L3", m_latest_input.buttons0, 0x00400000));
|
||||
AddInput(new Button("R3", m_latest_input.buttons0, 0x04000000));
|
||||
AddInput(new Button("L4", m_latest_input.buttons1, 0x00000200));
|
||||
AddInput(new Button("R4", m_latest_input.buttons1, 0x00000400));
|
||||
AddInput(new Button("Left Stick Touch", m_latest_input.buttons1, 0x00004000));
|
||||
AddInput(new Button("Right Stick Touch", m_latest_input.buttons1, 0x00008000));
|
||||
AddInput(new Button("Quick Access", m_latest_input.buttons1, 0x00040000));
|
||||
|
||||
AddInput(new AnalogInput("L2", m_latest_input.l_trig, 32767));
|
||||
AddInput(new AnalogInput("R2", m_latest_input.r_trig, 32767));
|
||||
AddInput(new AnalogInput("Left Stick X-", m_latest_input.l_stick_x, -32767));
|
||||
AddInput(new AnalogInput("Left Stick X+", m_latest_input.l_stick_x, 32767));
|
||||
AddInput(new AnalogInput("Left Stick Y-", m_latest_input.l_stick_y, -32767));
|
||||
AddInput(new AnalogInput("Left Stick Y+", m_latest_input.l_stick_y, 32767));
|
||||
AddInput(new AnalogInput("Right Stick X-", m_latest_input.r_stick_x, -32767));
|
||||
AddInput(new AnalogInput("Right Stick X+", m_latest_input.r_stick_x, 32767));
|
||||
AddInput(new AnalogInput("Right Stick Y-", m_latest_input.r_stick_y, -32767));
|
||||
AddInput(new AnalogInput("Right Stick Y+", m_latest_input.r_stick_y, 32767));
|
||||
AddInput(new AnalogInput("Left Trackpad X-", m_latest_input.l_pad_x, -32767));
|
||||
AddInput(new AnalogInput("Left Trackpad X+", m_latest_input.l_pad_x, 32767));
|
||||
AddInput(new AnalogInput("Left Trackpad Y-", m_latest_input.l_pad_y, -32767));
|
||||
AddInput(new AnalogInput("Left Trackpad Y+", m_latest_input.l_pad_y, 32767));
|
||||
AddInput(new AnalogInput("Right Trackpad X-", m_latest_input.r_pad_x, -32767));
|
||||
AddInput(new AnalogInput("Right Trackpad X+", m_latest_input.r_pad_x, 32767));
|
||||
AddInput(new AnalogInput("Right Trackpad Y-", m_latest_input.r_pad_y, -32767));
|
||||
AddInput(new AnalogInput("Right Trackpad Y+", m_latest_input.r_pad_y, 32767));
|
||||
AddInput(new AnalogInput("Left Trackpad Pressure", m_latest_input.l_pad_force, 32767));
|
||||
AddInput(new AnalogInput("Right Trackpad Pressure", m_latest_input.r_pad_force, 32767));
|
||||
AddInput(new AnalogInput("Left Stick Capacitance", m_latest_input.l_stick_capa, 32767));
|
||||
AddInput(new AnalogInput("Right Stick Capacitance", m_latest_input.r_stick_capa, 32767));
|
||||
|
||||
// 0x4000 LSBs = 1 g
|
||||
// final output in m/s^2
|
||||
constexpr auto accel_scale = 16384.0 / MathUtil::GRAVITY_ACCELERATION;
|
||||
AddInput(new MotionInput("Accel Up", m_latest_input.accel_z, -accel_scale));
|
||||
AddInput(new MotionInput("Accel Down", m_latest_input.accel_z, accel_scale));
|
||||
AddInput(new MotionInput("Accel Left", m_latest_input.accel_x, accel_scale));
|
||||
AddInput(new MotionInput("Accel Right", m_latest_input.accel_x, -accel_scale));
|
||||
AddInput(new MotionInput("Accel Forward", m_latest_input.accel_y, -accel_scale));
|
||||
AddInput(new MotionInput("Accel Backward", m_latest_input.accel_y, accel_scale));
|
||||
|
||||
// 16.384 (?) LSBs = 1 deg / s
|
||||
// final output in rads / s
|
||||
constexpr auto gyro_scale = 16.384 * 360.0 / MathUtil::TAU;
|
||||
AddInput(new MotionInput("Gyro Pitch Up", m_latest_input.gyro_pitch, gyro_scale));
|
||||
AddInput(new MotionInput("Gyro Pitch Down", m_latest_input.gyro_pitch, -gyro_scale));
|
||||
AddInput(new MotionInput("Gyro Roll Left", m_latest_input.gyro_roll, -gyro_scale));
|
||||
AddInput(new MotionInput("Gyro Roll Right", m_latest_input.gyro_roll, gyro_scale));
|
||||
AddInput(new MotionInput("Gyro Yaw Left", m_latest_input.gyro_yaw, gyro_scale));
|
||||
AddInput(new MotionInput("Gyro Yaw Right", m_latest_input.gyro_yaw, -gyro_scale));
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
std::string Device::GetName() const
|
||||
{
|
||||
return "Steam Deck";
|
||||
}
|
||||
|
||||
std::string Device::GetSource() const
|
||||
{
|
||||
return std::string(STEAMDECK_SOURCE_NAME);
|
||||
}
|
||||
|
||||
void Device::UpdateInput()
|
||||
{
|
||||
DeckInputReport rpt;
|
||||
bool got_anything = false;
|
||||
// Read all available input reports (processing only the most recent one).
|
||||
while (hid_read(m_device, reinterpret_cast<u8*>(&rpt), sizeof(rpt)) > 0)
|
||||
{
|
||||
got_anything = true;
|
||||
}
|
||||
// In case there were no reports available to be read, bail early.
|
||||
if (!got_anything)
|
||||
return;
|
||||
|
||||
if (rpt.major_ver != 0x01 || rpt.minor_ver != 0x00 || rpt.report_type != 0x09 ||
|
||||
rpt.report_sz != sizeof(rpt))
|
||||
{
|
||||
ERROR_LOG_FMT(CONTROLLERINTERFACE, "Steam Deck bad report");
|
||||
return;
|
||||
}
|
||||
|
||||
m_latest_input = rpt;
|
||||
}
|
||||
|
||||
} // namespace ciface::SteamDeck
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright 2023 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "InputCommon/ControllerInterface/InputBackend.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace ciface::SteamDeck
|
||||
{
|
||||
std::unique_ptr<ciface::InputBackend> CreateInputBackend(ControllerInterface* controller_interface);
|
||||
}
|
|
@ -166,6 +166,10 @@ KeyboardMouse::KeyboardMouse(Window window, int opcode, int pointer, int keyboar
|
|||
name = std::string(pointer_device->name);
|
||||
XIFreeDeviceInfo(pointer_device);
|
||||
|
||||
// Tell core X functions which keyboard is "the" keyboard for this
|
||||
// X connection.
|
||||
XISetClientPointer(m_display, None, pointer_deviceid);
|
||||
|
||||
{
|
||||
unsigned char mask_buf[(XI_LASTEVENT + 7) / 8] = {};
|
||||
XISetMask(mask_buf, XI_ButtonPress);
|
||||
|
@ -387,6 +391,10 @@ void KeyboardMouse::UpdateInput()
|
|||
// KeyRelease and FocusOut events are sometimes not received.
|
||||
// Cycling Alt-Tab and landing on the same window results in a stuck "Alt" key.
|
||||
// Unpressed keys are released here.
|
||||
// Because we called XISetClientPointer in the constructor, XQueryKeymap
|
||||
// will return the state of the associated keyboard, even if it isn't the
|
||||
// first master keyboard. (XInput2 doesn't provide a function to query
|
||||
// keyboard state.)
|
||||
std::array<char, 32> keyboard;
|
||||
XQueryKeymap(m_display, keyboard.data());
|
||||
for (size_t i = 0; i != keyboard.size(); ++i)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue