mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-23 06:09:50 -06:00
Merge pull request #604 from magcius/wip/emu-cleanup-2
Start cleaning up the input interface
This commit is contained in:
@ -1,7 +1,5 @@
|
||||
set(SRCS ControllerEmu.cpp
|
||||
InputConfig.cpp
|
||||
UDPWiimote.cpp
|
||||
UDPWrapper.cpp
|
||||
ControllerInterface/ControllerInterface.cpp
|
||||
ControllerInterface/Device.cpp
|
||||
ControllerInterface/ExpressionParser.cpp)
|
||||
|
@ -4,10 +4,6 @@
|
||||
|
||||
#include "InputCommon/ControllerEmu.h"
|
||||
|
||||
#if defined(HAVE_X11) && HAVE_X11
|
||||
#include <X11/Xlib.h>
|
||||
#endif
|
||||
|
||||
void ControllerEmu::UpdateReferences(ControllerInterface& devi)
|
||||
{
|
||||
for (auto& ctrlGroup : groups)
|
||||
@ -47,6 +43,8 @@ void ControllerEmu::ControlGroup::LoadConfig(IniFile::Section *sec, const std::s
|
||||
// settings
|
||||
for (auto& s : settings)
|
||||
{
|
||||
if (s->is_virtual)
|
||||
continue;
|
||||
sec->Get(group + s->name, &s->value, s->default_value * 100);
|
||||
s->value /= 100;
|
||||
}
|
||||
@ -103,7 +101,11 @@ void ControllerEmu::ControlGroup::SaveConfig(IniFile::Section *sec, const std::s
|
||||
std::string group(base + name); group += "/";
|
||||
|
||||
for (auto& s : settings)
|
||||
{
|
||||
if (s->is_virtual)
|
||||
continue;
|
||||
sec->Set(group + s->name, s->value*100.0f, s->default_value*100.0f);
|
||||
}
|
||||
|
||||
for (auto& c : controls)
|
||||
{
|
||||
@ -144,8 +146,6 @@ ControllerEmu::AnalogStick::AnalogStick(const char* const _name) : ControlGroup(
|
||||
|
||||
settings.emplace_back(new Setting(_trans("Radius"), 0.7f, 0, 100));
|
||||
settings.emplace_back(new Setting(_trans("Dead Zone"), 0, 0, 50));
|
||||
settings.emplace_back(new Setting(_trans("Square Stick"), 0));
|
||||
|
||||
}
|
||||
|
||||
ControllerEmu::Buttons::Buttons(const std::string& _name) : ControlGroup(_name, GROUP_TYPE_BUTTONS)
|
||||
@ -185,8 +185,7 @@ ControllerEmu::Force::Force(const std::string& _name) : ControlGroup(_name, GROU
|
||||
settings.emplace_back(new Setting(_trans("Dead Zone"), 0, 0, 50));
|
||||
}
|
||||
|
||||
ControllerEmu::Tilt::Tilt(const std::string& _name)
|
||||
: ControlGroup(_name, GROUP_TYPE_TILT)
|
||||
ControllerEmu::Tilt::Tilt(const std::string& _name) : ControlGroup(_name, GROUP_TYPE_TILT)
|
||||
{
|
||||
memset(m_tilt, 0, sizeof(m_tilt));
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "Common/IniFile.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "InputCommon/GCPadStatus.h"
|
||||
#include "InputCommon/ControllerInterface/ControllerInterface.h"
|
||||
|
||||
@ -30,7 +31,6 @@ enum
|
||||
GROUP_TYPE_TILT,
|
||||
GROUP_TYPE_CURSOR,
|
||||
GROUP_TYPE_TRIGGERS,
|
||||
GROUP_TYPE_UDPWII,
|
||||
GROUP_TYPE_SLIDER,
|
||||
};
|
||||
|
||||
@ -38,7 +38,6 @@ enum
|
||||
{
|
||||
SETTING_RADIUS,
|
||||
SETTING_DEADZONE,
|
||||
SETTING_SQUARE,
|
||||
};
|
||||
|
||||
const char* const named_directions[] =
|
||||
@ -96,12 +95,43 @@ public:
|
||||
, value(def_value)
|
||||
, default_value(def_value)
|
||||
, low(_low)
|
||||
, high(_high){}
|
||||
, high(_high)
|
||||
, is_virtual(false) {}
|
||||
|
||||
const std::string name;
|
||||
ControlState value;
|
||||
const ControlState default_value;
|
||||
const unsigned int low, high;
|
||||
bool is_virtual;
|
||||
|
||||
virtual void SetValue(ControlState new_value)
|
||||
{
|
||||
value = new_value;
|
||||
}
|
||||
|
||||
virtual ControlState GetValue()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
class BackgroundInputSetting : public Setting
|
||||
{
|
||||
public:
|
||||
BackgroundInputSetting(const std::string &_name) : Setting(_name, false)
|
||||
{
|
||||
is_virtual = true;
|
||||
}
|
||||
|
||||
void SetValue(ControlState new_value) override
|
||||
{
|
||||
SConfig::GetInstance().m_BackgroundInput = new_value;
|
||||
}
|
||||
|
||||
ControlState GetValue() override
|
||||
{
|
||||
return SConfig::GetInstance().m_BackgroundInput;
|
||||
}
|
||||
};
|
||||
|
||||
ControlGroup(const std::string& _name, const unsigned int _type = GROUP_TYPE_OTHER) : name(_name), type(_type) {}
|
||||
@ -121,66 +151,41 @@ public:
|
||||
class AnalogStick : public ControlGroup
|
||||
{
|
||||
public:
|
||||
AnalogStick(const char* const _name);
|
||||
|
||||
template <typename C>
|
||||
void GetState(C* const x, C* const y, const unsigned int base, const unsigned int range)
|
||||
void GetState(double* const x, double* const y)
|
||||
{
|
||||
// this is all a mess
|
||||
|
||||
ControlState yy = controls[0]->control_ref->State() - controls[1]->control_ref->State();
|
||||
ControlState xx = controls[3]->control_ref->State() - controls[2]->control_ref->State();
|
||||
|
||||
ControlState radius = settings[SETTING_RADIUS]->value;
|
||||
ControlState deadzone = settings[SETTING_DEADZONE]->value;
|
||||
ControlState square = settings[SETTING_SQUARE]->value;
|
||||
ControlState m = controls[4]->control_ref->State();
|
||||
|
||||
// modifier code
|
||||
ControlState ang = atan2(yy, xx);
|
||||
ControlState ang_sin = sin(ang);
|
||||
ControlState ang_cos = cos(ang);
|
||||
|
||||
ControlState dist = sqrt(xx*xx + yy*yy);
|
||||
|
||||
// dead zone code
|
||||
dist = std::max(0.0f, dist - deadzone);
|
||||
dist /= (1 - deadzone);
|
||||
|
||||
// radius
|
||||
dist *= radius;
|
||||
|
||||
// The modifier halves the distance by 50%, which is useful
|
||||
// for keyboard controls.
|
||||
if (m)
|
||||
{
|
||||
yy = (fabsf(yy)>deadzone) * sign(yy) * (m + deadzone/2);
|
||||
xx = (fabsf(xx)>deadzone) * sign(xx) * (m + deadzone/2);
|
||||
}
|
||||
dist *= 0.5;
|
||||
|
||||
// deadzone / square stick code
|
||||
if (radius != 1 || deadzone || square)
|
||||
{
|
||||
// this section might be all wrong, but its working good enough, I think
|
||||
yy = std::max(-1.0f, std::min(1.0f, ang_sin * dist));
|
||||
xx = std::max(-1.0f, std::min(1.0f, ang_cos * dist));
|
||||
|
||||
ControlState ang = atan2(yy, xx);
|
||||
ControlState ang_sin = sin(ang);
|
||||
ControlState ang_cos = cos(ang);
|
||||
|
||||
// the amt a full square stick would have at current angle
|
||||
ControlState square_full = std::min(ang_sin ? 1/fabsf(ang_sin) : 2, ang_cos ? 1/fabsf(ang_cos) : 2);
|
||||
|
||||
// the amt a full stick would have that was ( user setting squareness) at current angle
|
||||
// I think this is more like a pointed circle rather than a rounded square like it should be
|
||||
ControlState stick_full = (1 + (square_full - 1) * square);
|
||||
|
||||
ControlState dist = sqrt(xx*xx + yy*yy);
|
||||
|
||||
// dead zone code
|
||||
dist = std::max(0.0f, dist - deadzone * stick_full);
|
||||
dist /= (1 - deadzone);
|
||||
|
||||
// square stick code
|
||||
ControlState amt = dist / stick_full;
|
||||
dist -= ((square_full - 1) * amt * square);
|
||||
|
||||
// radius
|
||||
dist *= radius;
|
||||
|
||||
yy = std::max(-1.0f, std::min(1.0f, ang_sin * dist));
|
||||
xx = std::max(-1.0f, std::min(1.0f, ang_cos * dist));
|
||||
}
|
||||
|
||||
*y = C(yy * range + base);
|
||||
*x = C(xx * range + base);
|
||||
*y = yy;
|
||||
*x = xx;
|
||||
}
|
||||
|
||||
AnalogStick(const char* const _name);
|
||||
|
||||
};
|
||||
|
||||
class Buttons : public ControlGroup
|
||||
@ -205,64 +210,55 @@ public:
|
||||
class MixedTriggers : public ControlGroup
|
||||
{
|
||||
public:
|
||||
MixedTriggers(const std::string& _name);
|
||||
|
||||
template <typename C, typename S>
|
||||
void GetState(C* const digital, const C* bitmasks, S* analog, const unsigned int range)
|
||||
void GetState(u16 *const digital, const u16* bitmasks, double* analog)
|
||||
{
|
||||
const unsigned int trig_count = ((unsigned int) (controls.size() / 2));
|
||||
for (unsigned int i=0; i<trig_count; ++i,++bitmasks,++analog)
|
||||
{
|
||||
if (controls[i]->control_ref->State() > settings[0]->value) //threshold
|
||||
{
|
||||
*analog = range;
|
||||
*analog = 1.0;
|
||||
*digital |= *bitmasks;
|
||||
}
|
||||
else
|
||||
{
|
||||
*analog = S(controls[i+trig_count]->control_ref->State() * range);
|
||||
*analog = controls[i+trig_count]->control_ref->State();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MixedTriggers(const std::string& _name);
|
||||
|
||||
};
|
||||
|
||||
class Triggers : public ControlGroup
|
||||
{
|
||||
public:
|
||||
Triggers(const std::string& _name);
|
||||
|
||||
template <typename S>
|
||||
void GetState(S* analog, const unsigned int range)
|
||||
void GetState(double* analog)
|
||||
{
|
||||
const unsigned int trig_count = ((unsigned int) (controls.size()));
|
||||
const ControlState deadzone = settings[0]->value;
|
||||
for (unsigned int i=0; i<trig_count; ++i,++analog)
|
||||
*analog = S(std::max(controls[i]->control_ref->State() - deadzone, 0.0f) / (1 - deadzone) * range);
|
||||
*analog = std::max(controls[i]->control_ref->State() - deadzone, 0.0f) / (1 - deadzone);
|
||||
}
|
||||
|
||||
Triggers(const std::string& _name);
|
||||
|
||||
};
|
||||
|
||||
class Slider : public ControlGroup
|
||||
{
|
||||
public:
|
||||
Slider(const std::string& _name);
|
||||
|
||||
template <typename S>
|
||||
void GetState(S* const slider, const unsigned int range, const unsigned int base = 0)
|
||||
void GetState(double* const slider)
|
||||
{
|
||||
const float deadzone = settings[0]->value;
|
||||
const float state = controls[1]->control_ref->State() - controls[0]->control_ref->State();
|
||||
|
||||
if (fabsf(state) > deadzone)
|
||||
*slider = (S)((state - (deadzone * sign(state))) / (1 - deadzone) * range + base);
|
||||
*slider = (state - (deadzone * sign(state))) / (1 - deadzone);
|
||||
else
|
||||
*slider = 0;
|
||||
}
|
||||
|
||||
Slider(const std::string& _name);
|
||||
|
||||
};
|
||||
|
||||
class Force : public ControlGroup
|
||||
@ -270,8 +266,7 @@ public:
|
||||
public:
|
||||
Force(const std::string& _name);
|
||||
|
||||
template <typename C, typename R>
|
||||
void GetState(C* axis, const u8 base, const R range)
|
||||
void GetState(double* axis)
|
||||
{
|
||||
const float deadzone = settings[0]->value;
|
||||
for (unsigned int i=0; i<6; i+=2)
|
||||
@ -282,7 +277,7 @@ public:
|
||||
tmpf = ((state - (deadzone * sign(state))) / (1 - deadzone));
|
||||
|
||||
float &ax = m_swing[i >> 1];
|
||||
*axis++ = (C)((tmpf - ax) * range + base);
|
||||
*axis++ = (tmpf - ax);
|
||||
ax = tmpf;
|
||||
}
|
||||
}
|
||||
@ -296,8 +291,7 @@ public:
|
||||
public:
|
||||
Tilt(const std::string& _name);
|
||||
|
||||
template <typename C, typename R>
|
||||
void GetState(C* const x, C* const y, const unsigned int base, const R range, const bool step = true)
|
||||
void GetState(double* const x, double* const y, const bool step = true)
|
||||
{
|
||||
// this is all a mess
|
||||
|
||||
@ -309,42 +303,35 @@ public:
|
||||
auto const angle = settings[2]->value / 1.8f;
|
||||
ControlState m = controls[4]->control_ref->State();
|
||||
|
||||
// modifier code
|
||||
if (m)
|
||||
{
|
||||
yy = (fabsf(yy)>deadzone) * sign(yy) * (m + deadzone/2);
|
||||
xx = (fabsf(xx)>deadzone) * sign(xx) * (m + deadzone/2);
|
||||
}
|
||||
|
||||
// deadzone / circle stick code
|
||||
if (deadzone || circle)
|
||||
{
|
||||
// this section might be all wrong, but its working good enough, I think
|
||||
// this section might be all wrong, but its working good enough, I think
|
||||
|
||||
ControlState ang = atan2(yy, xx);
|
||||
ControlState ang_sin = sin(ang);
|
||||
ControlState ang_cos = cos(ang);
|
||||
ControlState ang = atan2(yy, xx);
|
||||
ControlState ang_sin = sin(ang);
|
||||
ControlState ang_cos = cos(ang);
|
||||
|
||||
// the amt a full square stick would have at current angle
|
||||
ControlState square_full = std::min(ang_sin ? 1/fabsf(ang_sin) : 2, ang_cos ? 1/fabsf(ang_cos) : 2);
|
||||
// the amt a full square stick would have at current angle
|
||||
ControlState square_full = std::min(ang_sin ? 1/fabsf(ang_sin) : 2, ang_cos ? 1/fabsf(ang_cos) : 2);
|
||||
|
||||
// the amt a full stick would have that was (user setting circular) at current angle
|
||||
// I think this is more like a pointed circle rather than a rounded square like it should be
|
||||
ControlState stick_full = (square_full * (1 - circle)) + (circle);
|
||||
// the amt a full stick would have that was (user setting circular) at current angle
|
||||
// I think this is more like a pointed circle rather than a rounded square like it should be
|
||||
ControlState stick_full = (square_full * (1 - circle)) + (circle);
|
||||
|
||||
ControlState dist = sqrt(xx*xx + yy*yy);
|
||||
ControlState dist = sqrt(xx*xx + yy*yy);
|
||||
|
||||
// dead zone code
|
||||
dist = std::max(0.0f, dist - deadzone * stick_full);
|
||||
dist /= (1 - deadzone);
|
||||
// dead zone code
|
||||
dist = std::max(0.0f, dist - deadzone * stick_full);
|
||||
dist /= (1 - deadzone);
|
||||
|
||||
// circle stick code
|
||||
ControlState amt = dist / stick_full;
|
||||
dist += (square_full - 1) * amt * circle;
|
||||
// circle stick code
|
||||
ControlState amt = dist / stick_full;
|
||||
dist += (square_full - 1) * amt * circle;
|
||||
|
||||
yy = std::max(-1.0f, std::min(1.0f, ang_sin * dist));
|
||||
xx = std::max(-1.0f, std::min(1.0f, ang_cos * dist));
|
||||
}
|
||||
if (m)
|
||||
dist *= 0.5;
|
||||
|
||||
yy = std::max(-1.0f, std::min(1.0f, ang_sin * dist));
|
||||
xx = std::max(-1.0f, std::min(1.0f, ang_cos * dist));
|
||||
|
||||
// this is kinda silly here
|
||||
// gui being open will make this happen 2x as fast, o well
|
||||
@ -363,8 +350,8 @@ public:
|
||||
m_tilt[1] = std::max(m_tilt[1] - 0.1f, yy);
|
||||
}
|
||||
|
||||
*y = C(m_tilt[1] * angle * range + base);
|
||||
*x = C(m_tilt[0] * angle * range + base);
|
||||
*y = m_tilt[1] * angle;
|
||||
*x = m_tilt[0] * angle;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -376,8 +363,7 @@ public:
|
||||
public:
|
||||
Cursor(const std::string& _name);
|
||||
|
||||
template <typename C>
|
||||
void GetState(C* const x, C* const y, C* const z, const bool adjusted = false)
|
||||
void GetState(double* const x, double* const y, double* const z, const bool adjusted = false)
|
||||
{
|
||||
const float zz = controls[4]->control_ref->State() - controls[5]->control_ref->State();
|
||||
|
||||
@ -425,7 +411,7 @@ public:
|
||||
|
||||
~Extension() {}
|
||||
|
||||
void GetState(u8* const data, const bool focus = true);
|
||||
void GetState(u8* const data);
|
||||
|
||||
std::vector<std::unique_ptr<ControllerEmu>> attachments;
|
||||
|
||||
|
@ -5,6 +5,12 @@
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
// For InputGateOn()
|
||||
// This is a really bad layering violation, but it's the cleanest
|
||||
// place I could find to put it.
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Host.h"
|
||||
|
||||
#include "InputCommon/ControllerInterface/Device.h"
|
||||
|
||||
namespace ciface
|
||||
@ -74,6 +80,16 @@ void Device::ClearInputState()
|
||||
// kinda slow but, w/e, should only happen when user unplugs a device while playing
|
||||
}
|
||||
|
||||
bool Device::Control::InputGateOn()
|
||||
{
|
||||
if (SConfig::GetInstance().m_BackgroundInput)
|
||||
return true;
|
||||
else if (Host_RendererHasFocus())
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// DeviceQualifier :: ToString
|
||||
//
|
||||
|
@ -42,6 +42,8 @@ public:
|
||||
virtual std::string GetName() const = 0;
|
||||
virtual ~Control() {}
|
||||
|
||||
bool InputGateOn();
|
||||
|
||||
virtual Input* ToInput() { return nullptr; }
|
||||
virtual Output* ToOutput() { return nullptr; }
|
||||
};
|
||||
@ -59,6 +61,16 @@ public:
|
||||
|
||||
virtual ControlState GetState() const = 0;
|
||||
|
||||
bool ShouldHaveInput();
|
||||
|
||||
ControlState GetGatedState()
|
||||
{
|
||||
if (InputGateOn())
|
||||
return GetState();
|
||||
else
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
Input* ToInput() override { return this; }
|
||||
};
|
||||
|
||||
@ -74,6 +86,12 @@ public:
|
||||
|
||||
virtual void SetState(ControlState state) = 0;
|
||||
|
||||
void SetGatedState(ControlState state)
|
||||
{
|
||||
if (InputGateOn())
|
||||
SetState(state);
|
||||
}
|
||||
|
||||
Output* ToOutput() override { return this; }
|
||||
};
|
||||
|
||||
|
@ -228,12 +228,12 @@ public:
|
||||
|
||||
virtual ControlState GetValue() override
|
||||
{
|
||||
return control->ToInput()->GetState();
|
||||
return control->ToInput()->GetGatedState();
|
||||
}
|
||||
|
||||
virtual void SetValue(ControlState value) override
|
||||
{
|
||||
control->ToOutput()->SetState(value);
|
||||
control->ToOutput()->SetGatedState(value);
|
||||
}
|
||||
|
||||
virtual int CountNumControls() override
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
@ -48,8 +48,6 @@
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="UDPWiimote.cpp" />
|
||||
<ClCompile Include="UDPWrapper.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ControllerEmu.h" />
|
||||
@ -64,8 +62,6 @@
|
||||
<ClInclude Include="GCPadStatus.h" />
|
||||
<ClInclude Include="InputConfig.h" />
|
||||
<ClInclude Include="stdafx.h" />
|
||||
<ClInclude Include="UDPWiimote.h" />
|
||||
<ClInclude Include="UDPWrapper.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="CMakeLists.txt" />
|
||||
@ -78,4 +74,4 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="ControllerInterface">
|
||||
@ -17,8 +17,6 @@
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ControllerEmu.cpp" />
|
||||
<ClCompile Include="InputConfig.cpp" />
|
||||
<ClCompile Include="UDPWiimote.cpp" />
|
||||
<ClCompile Include="UDPWrapper.cpp" />
|
||||
<ClCompile Include="ControllerInterface\DInput\DInput.cpp">
|
||||
<Filter>ControllerInterface\DInput</Filter>
|
||||
</ClCompile>
|
||||
@ -49,8 +47,6 @@
|
||||
<ClInclude Include="ControllerEmu.h" />
|
||||
<ClInclude Include="GCPadStatus.h" />
|
||||
<ClInclude Include="InputConfig.h" />
|
||||
<ClInclude Include="UDPWiimote.h" />
|
||||
<ClInclude Include="UDPWrapper.h" />
|
||||
<ClInclude Include="ControllerInterface\DInput\DInput.h">
|
||||
<Filter>ControllerInterface\DInput</Filter>
|
||||
</ClInclude>
|
||||
@ -80,4 +76,4 @@
|
||||
<ItemGroup>
|
||||
<Text Include="CMakeLists.txt" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
@ -1,439 +0,0 @@
|
||||
// Copyright 2013 Dolphin Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
#include "Common/Thread.h"
|
||||
#include "Common/Timer.h"
|
||||
|
||||
#include "InputCommon/UDPWiimote.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#define sock_t SOCKET
|
||||
#define ERRNO WSAGetLastError()
|
||||
#undef EWOULDBLOCK
|
||||
#define EWOULDBLOCK WSAEWOULDBLOCK
|
||||
#define BAD_SOCK INVALID_SOCKET
|
||||
#define close(x) closesocket(x)
|
||||
#define cleanup do {noinst--; if (noinst==0) WSACleanup();} while (0)
|
||||
#define blockingoff(sock) ioctlsocket(sock, FIONBIO, &iMode)
|
||||
#define dataz char*
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment (lib, "Ws2_32.lib")
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#define BAD_SOCK -1
|
||||
#define ERRNO errno
|
||||
#define cleanup noinst--
|
||||
#define blockingoff(sock) fcntl(sock, F_SETFL, O_NONBLOCK)
|
||||
#define dataz void*
|
||||
#define sock_t int
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
struct UDPWiimote::_d
|
||||
{
|
||||
std::thread thread;
|
||||
std::list<sock_t> sockfds;
|
||||
std::mutex termLock, mutex, nameMutex;
|
||||
volatile bool exit;
|
||||
sock_t bipv4_fd, bipv6_fd;
|
||||
};
|
||||
|
||||
int UDPWiimote::noinst = 0;
|
||||
|
||||
UDPWiimote::UDPWiimote(const std::string& _port, const std::string& name, int _index) :
|
||||
port(_port), displayName(name), d(new _d),
|
||||
waX(0), waY(0), waZ(1), naX(0), naY(0), naZ(-1), nunX(0), nunY(0),
|
||||
pointerX(1001.0f / 2), pointerY(0), nunMask(0), wiimoteMask(0), index(_index), int_port(atoi(_port.c_str()))
|
||||
{
|
||||
|
||||
static bool sranded=false;
|
||||
if (!sranded)
|
||||
{
|
||||
srand((unsigned int)time(nullptr));
|
||||
sranded=true;
|
||||
}
|
||||
bcastMagic=rand() & 0xFFFF;
|
||||
|
||||
#ifdef _WIN32
|
||||
u_long iMode = 1;
|
||||
#endif
|
||||
struct addrinfo hints, *servinfo, *p;
|
||||
int rv;
|
||||
|
||||
#ifdef _WIN32
|
||||
if (noinst==0)
|
||||
{
|
||||
WORD sockVersion;
|
||||
WSADATA wsaData;
|
||||
sockVersion = MAKEWORD(2, 2);
|
||||
WSAStartup(sockVersion, &wsaData);
|
||||
}
|
||||
#endif
|
||||
|
||||
noinst++;
|
||||
|
||||
memset(&hints, 0, sizeof hints);
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_flags = AI_PASSIVE; // use my IP
|
||||
|
||||
if (!int_port)
|
||||
{
|
||||
cleanup;
|
||||
err=-1;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((rv = getaddrinfo(nullptr, _port.c_str(), &hints, &servinfo)) != 0)
|
||||
{
|
||||
cleanup;
|
||||
err=-1;
|
||||
return;
|
||||
}
|
||||
|
||||
// loop through all the results and bind to everything we can
|
||||
for (p = servinfo; p != nullptr; p = p->ai_next)
|
||||
{
|
||||
sock_t sock;
|
||||
if ((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == BAD_SOCK)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bind(sock, p->ai_addr, (int)p->ai_addrlen) == -1)
|
||||
{
|
||||
close(sock);
|
||||
continue;
|
||||
}
|
||||
d->sockfds.push_back(sock);
|
||||
}
|
||||
|
||||
if (d->sockfds.empty())
|
||||
{
|
||||
cleanup;
|
||||
err=-2;
|
||||
return;
|
||||
}
|
||||
|
||||
freeaddrinfo(servinfo);
|
||||
err=0;
|
||||
d->exit=false;
|
||||
initBroadcastIPv4();
|
||||
initBroadcastIPv6();
|
||||
|
||||
std::lock_guard<std::mutex> lk(d->termLock);
|
||||
d->thread = std::thread(std::mem_fn(&UDPWiimote::mainThread), this);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void UDPWiimote::mainThread()
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(d->termLock);
|
||||
|
||||
Common::Timer time;
|
||||
fd_set fds;
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec=0;
|
||||
timeout.tv_usec=0;
|
||||
time.Update();
|
||||
do
|
||||
{
|
||||
int maxfd=0;
|
||||
FD_ZERO(&fds);
|
||||
for (auto& fd : d->sockfds)
|
||||
{
|
||||
FD_SET(fd,&fds);
|
||||
#ifndef _WIN32
|
||||
if (fd>=maxfd)
|
||||
maxfd=(fd)+1;
|
||||
#endif
|
||||
}
|
||||
|
||||
u64 tleft=timeout.tv_sec*1000+timeout.tv_usec/1000;
|
||||
u64 telapsed=time.GetTimeDifference();
|
||||
time.Update();
|
||||
if (tleft<=telapsed)
|
||||
{
|
||||
timeout.tv_sec=1;
|
||||
timeout.tv_usec=500000;
|
||||
broadcastPresence();
|
||||
}
|
||||
else
|
||||
{
|
||||
tleft-=telapsed;
|
||||
timeout.tv_sec=(long)(tleft/1000);
|
||||
timeout.tv_usec=(tleft%1000)*1000;
|
||||
}
|
||||
|
||||
lk.unlock(); //VERY hacky. don't like it
|
||||
if (d->exit) return;
|
||||
int rt=select(maxfd,&fds,nullptr,nullptr,&timeout);
|
||||
if (d->exit) return;
|
||||
lk.lock();
|
||||
if (d->exit) return;
|
||||
|
||||
if (rt)
|
||||
{
|
||||
for (sock_t fd : d->sockfds)
|
||||
{
|
||||
if (FD_ISSET(fd,&fds))
|
||||
{
|
||||
u8 bf[64];
|
||||
int size=60;
|
||||
size_t addr_len;
|
||||
struct sockaddr_storage their_addr;
|
||||
addr_len = sizeof their_addr;
|
||||
if ((size = recvfrom(fd,
|
||||
(dataz)bf,
|
||||
size , 0,(struct sockaddr *)&their_addr, (socklen_t*)&addr_len)) == -1)
|
||||
{
|
||||
ERROR_LOG(WIIMOTE,"UDPWii Packet error");
|
||||
}
|
||||
else
|
||||
{
|
||||
std::lock_guard<std::mutex> lkm(d->mutex);
|
||||
if (pharsePacket(bf,size)==0)
|
||||
{
|
||||
//NOTICE_LOG(WIIMOTE,"UDPWII New pack");
|
||||
}
|
||||
else
|
||||
{
|
||||
//NOTICE_LOG(WIIMOTE,"UDPWII Wrong pack format... ignoring");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (!(d->exit));
|
||||
}
|
||||
|
||||
UDPWiimote::~UDPWiimote()
|
||||
{
|
||||
d->exit = true;
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(d->termLock);
|
||||
d->thread.join();
|
||||
}
|
||||
for (auto& elem : d->sockfds)
|
||||
close(elem);
|
||||
close(d->bipv4_fd);
|
||||
close(d->bipv6_fd);
|
||||
cleanup;
|
||||
delete d;
|
||||
}
|
||||
|
||||
#define ACCEL_FLAG (1 << 0)
|
||||
#define BUTT_FLAG (1 << 1)
|
||||
#define IR_FLAG (1 << 2)
|
||||
#define NUN_FLAG (1 << 3)
|
||||
#define NUNACCEL_FLAG (1 << 4)
|
||||
|
||||
int UDPWiimote::pharsePacket(u8 * bf, size_t size)
|
||||
{
|
||||
if (size < 3)
|
||||
return -1;
|
||||
|
||||
if (bf[0] != 0xde)
|
||||
return -1;
|
||||
//if (bf[1]==0)
|
||||
// time=0;
|
||||
//if (bf[1]<time) //NOT LONGER NEEDED TO ALLOW MULTIPLE IPHONES ON A SINGLE PORT
|
||||
// return -1;
|
||||
//time=bf[1];
|
||||
u32 *p=(u32*)(&bf[3]);
|
||||
if (bf[2] & ACCEL_FLAG)
|
||||
{
|
||||
if ((size-(((u8*)p)-bf)) < 12)
|
||||
return -1;
|
||||
|
||||
double ux,uy,uz;
|
||||
ux=(double)((s32)ntohl(*p)); p++;
|
||||
uy=(double)((s32)ntohl(*p)); p++;
|
||||
uz=(double)((s32)ntohl(*p)); p++;
|
||||
waX=ux/1048576; //packet accel data
|
||||
waY=uy/1048576;
|
||||
waZ=uz/1048576;
|
||||
}
|
||||
|
||||
if (bf[2] & BUTT_FLAG)
|
||||
{
|
||||
if ((size-(((u8*)p)-bf)) < 4)
|
||||
return -1;
|
||||
|
||||
wiimoteMask = ntohl(*p); p++;
|
||||
}
|
||||
|
||||
if (bf[2] & IR_FLAG)
|
||||
{
|
||||
if ((size-(((u8*)p)-bf)) < 8)
|
||||
return -1;
|
||||
|
||||
pointerX=((double)((s32)ntohl(*p)))/1048576; p++;
|
||||
pointerY=((double)((s32)ntohl(*p)))/1048576; p++;
|
||||
}
|
||||
|
||||
if (bf[2] & NUN_FLAG)
|
||||
{
|
||||
if ((size-(((u8*)p)-bf)) < 9)
|
||||
return -1;
|
||||
|
||||
nunMask=*((u8*)p); p=(u32*)(((u8*)p)+1);
|
||||
nunX=((double)((s32)ntohl(*p)))/1048576; p++;
|
||||
nunY=((double)((s32)ntohl(*p)))/1048576; p++;
|
||||
}
|
||||
|
||||
if (bf[2] & NUNACCEL_FLAG)
|
||||
{
|
||||
if ((size-(((u8*)p)-bf)) < 12)
|
||||
return -1;
|
||||
|
||||
double ux,uy,uz;
|
||||
ux=(double)((s32)ntohl(*p)); p++;
|
||||
uy=(double)((s32)ntohl(*p)); p++;
|
||||
uz=(double)((s32)ntohl(*p)); p++;
|
||||
naX=ux/1048576; //packet accel data
|
||||
naY=uy/1048576;
|
||||
naZ=uz/1048576;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void UDPWiimote::initBroadcastIPv4()
|
||||
{
|
||||
d->bipv4_fd=socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (d->bipv4_fd == BAD_SOCK)
|
||||
{
|
||||
WARN_LOG(WIIMOTE,"socket() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
int broad=1;
|
||||
if (setsockopt(d->bipv4_fd,SOL_SOCKET,SO_BROADCAST, (const dataz)(&broad), sizeof broad) == -1)
|
||||
{
|
||||
WARN_LOG(WIIMOTE,"setsockopt(SO_BROADCAST) failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void UDPWiimote::broadcastIPv4(const void * data, size_t size)
|
||||
{
|
||||
|
||||
struct sockaddr_in their_addr;
|
||||
their_addr.sin_family = AF_INET;
|
||||
their_addr.sin_port = htons(4431);
|
||||
their_addr.sin_addr.s_addr = INADDR_BROADCAST;
|
||||
memset(their_addr.sin_zero, '\0', sizeof their_addr.sin_zero);
|
||||
|
||||
int num;
|
||||
if ((num=sendto(d->bipv4_fd,(const dataz)data,(int)size,0,(struct sockaddr *) &their_addr, sizeof their_addr)) == -1)
|
||||
{
|
||||
WARN_LOG(WIIMOTE,"sendto() failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void UDPWiimote::initBroadcastIPv6()
|
||||
{
|
||||
//TODO: IPv6 support
|
||||
}
|
||||
|
||||
void UDPWiimote::broadcastIPv6(const void * data, size_t size)
|
||||
{
|
||||
//TODO: IPv6 support
|
||||
}
|
||||
|
||||
void UDPWiimote::broadcastPresence()
|
||||
{
|
||||
size_t slen;
|
||||
u8 bf[512];
|
||||
bf[0]=0xdf; //magic number
|
||||
*((u16*)(&(bf[1])))=htons(bcastMagic); //unique per-wiimote 16-bit ID
|
||||
bf[3]=(u8)index; //wiimote index
|
||||
*((u16*)(&(bf[4])))=htons(int_port); //port
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(d->nameMutex);
|
||||
slen=displayName.size();
|
||||
if (slen>=256)
|
||||
slen=255;
|
||||
bf[6]=(u8)slen; //display name size (max 255)
|
||||
memcpy(&(bf[7]),displayName.c_str(),slen); //display name
|
||||
}
|
||||
broadcastIPv4(bf,7+slen);
|
||||
broadcastIPv6(bf,7+slen);
|
||||
}
|
||||
|
||||
void UDPWiimote::getAccel(float* x, float* y, float* z)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(d->mutex);
|
||||
*x = (float)waX;
|
||||
*y = (float)waY;
|
||||
*z = (float)waZ;
|
||||
}
|
||||
|
||||
u32 UDPWiimote::getButtons()
|
||||
{
|
||||
u32 msk;
|
||||
std::lock_guard<std::mutex> lk(d->mutex);
|
||||
msk = wiimoteMask;
|
||||
return msk;
|
||||
}
|
||||
|
||||
void UDPWiimote::getIR(float* x, float* y)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(d->mutex);
|
||||
*x = (float)pointerX;
|
||||
*y = (float)pointerY;
|
||||
}
|
||||
|
||||
void UDPWiimote::getNunchuck(float* x, float* y, u8* mask)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(d->mutex);
|
||||
*x = (float)nunX;
|
||||
*y = (float)nunY;
|
||||
*mask = nunMask;
|
||||
}
|
||||
|
||||
void UDPWiimote::getNunchuckAccel(float* x, float* y, float* z)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(d->mutex);
|
||||
*x = (float)naX;
|
||||
*y = (float)naY;
|
||||
*z = (float)naZ;
|
||||
}
|
||||
|
||||
const std::string& UDPWiimote::getPort()
|
||||
{
|
||||
return port;
|
||||
}
|
||||
|
||||
void UDPWiimote::changeName(const std::string& name)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(d->nameMutex);
|
||||
displayName = name;
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
// Copyright 2013 Dolphin Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "Common/Common.h"
|
||||
|
||||
#define UDPWM_B1 (1<<0)
|
||||
#define UDPWM_B2 (1<<1)
|
||||
#define UDPWM_BA (1<<2)
|
||||
#define UDPWM_BB (1<<3)
|
||||
#define UDPWM_BP (1<<4)
|
||||
#define UDPWM_BM (1<<5)
|
||||
#define UDPWM_BH (1<<6)
|
||||
#define UDPWM_BU (1<<7)
|
||||
#define UDPWM_BD (1<<8)
|
||||
#define UDPWM_BL (1<<9)
|
||||
#define UDPWM_BR (1<<10)
|
||||
#define UDPWM_SK (1<<11)
|
||||
#define UDPWM_NC (1<<0)
|
||||
#define UDPWM_NZ (1<<1)
|
||||
|
||||
class UDPWiimote
|
||||
{
|
||||
public:
|
||||
UDPWiimote(const std::string& port, const std::string& name, int index);
|
||||
virtual ~UDPWiimote();
|
||||
void getAccel(float* x, float* y, float* z);
|
||||
u32 getButtons();
|
||||
void getNunchuck(float* x, float* y, u8* mask);
|
||||
void getIR(float* x, float* y);
|
||||
void getNunchuckAccel(float* x, float* y, float* z);
|
||||
int getErrNo()
|
||||
{
|
||||
return err;
|
||||
}
|
||||
const std::string& getPort();
|
||||
void changeName(const std::string& name);
|
||||
|
||||
void mainThread();
|
||||
private:
|
||||
std::string port,displayName;
|
||||
int pharsePacket(u8* data, size_t size);
|
||||
struct _d; //using pimpl because Winsock2.h doesn't have include guards -_-
|
||||
_d* d;
|
||||
double waX, waY, waZ;
|
||||
double naX, naY, naZ;
|
||||
double nunX, nunY;
|
||||
double pointerX, pointerY;
|
||||
u8 nunMask;
|
||||
u32 wiimoteMask;
|
||||
u16 bcastMagic;
|
||||
int err;
|
||||
int index;
|
||||
int int_port;
|
||||
static int noinst;
|
||||
void broadcastPresence();
|
||||
void broadcastIPv4(const void* data, size_t size);
|
||||
void broadcastIPv6(const void* data, size_t size);
|
||||
void initBroadcastIPv4();
|
||||
void initBroadcastIPv6();
|
||||
};
|
@ -1,96 +0,0 @@
|
||||
// Copyright 2013 Dolphin Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "InputCommon/UDPWrapper.h"
|
||||
|
||||
static const std::string DefaultPort(const int index)
|
||||
{
|
||||
static std::string s;
|
||||
s = "443";
|
||||
s += (char)('2' + index);
|
||||
return s;
|
||||
}
|
||||
|
||||
UDPWrapper::UDPWrapper(int indx, const char* const _name) :
|
||||
ControllerEmu::ControlGroup(_name,GROUP_TYPE_UDPWII),
|
||||
inst(nullptr), index(indx),
|
||||
updIR(false),updAccel(false),
|
||||
updButt(false),udpEn(false)
|
||||
, port(DefaultPort(indx))
|
||||
{
|
||||
//PanicAlert("UDPWrapper #%d ctor",index);
|
||||
}
|
||||
|
||||
void UDPWrapper::LoadConfig(IniFile::Section *sec, const std::string& defdev, const std::string& base )
|
||||
{
|
||||
ControlGroup::LoadConfig(sec,defdev,base);
|
||||
|
||||
std::string group( base + name ); group += "/";
|
||||
|
||||
int _updAccel,_updIR,_updButt,_udpEn,_updNun,_updNunAccel;
|
||||
sec->Get(group + "Enable",&_udpEn, 0);
|
||||
sec->Get(group + "Port", &port, DefaultPort(index));
|
||||
sec->Get(group + "Update_Accel", &_updAccel, 1);
|
||||
sec->Get(group + "Update_IR", &_updIR, 1);
|
||||
sec->Get(group + "Update_Butt", &_updButt, 1);
|
||||
sec->Get(group + "Update_Nunchuk", &_updNun, 1);
|
||||
sec->Get(group + "Update_NunchukAccel", &_updNunAccel, 0);
|
||||
|
||||
udpEn=(_udpEn>0);
|
||||
updAccel=(_updAccel>0);
|
||||
updIR=(_updIR>0);
|
||||
updButt=(_updButt>0);
|
||||
updNun=(_updNun>0);
|
||||
updNunAccel=(_updNunAccel>0);
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
|
||||
void UDPWrapper::SaveConfig(IniFile::Section *sec, const std::string& defdev, const std::string& base )
|
||||
{
|
||||
ControlGroup::SaveConfig(sec,defdev,base);
|
||||
std::string group( base + name ); group += "/";
|
||||
sec->Set(group + "Enable", (int)udpEn, 0);
|
||||
sec->Set(group + "Port", port, DefaultPort(index));
|
||||
sec->Set(group + "Update_Accel", (int)updAccel, 1);
|
||||
sec->Set(group + "Update_IR", (int)updIR, 1);
|
||||
sec->Set(group + "Update_Butt", (int)updButt, 1);
|
||||
sec->Set(group + "Update_Nunchuk", (int)updNun, 1);
|
||||
sec->Set(group + "Update_NunchukAccel", (int)updNunAccel, 0);
|
||||
}
|
||||
|
||||
|
||||
void UDPWrapper::Refresh()
|
||||
{
|
||||
bool udpAEn=(inst!=nullptr);
|
||||
if (udpEn && udpAEn)
|
||||
{
|
||||
if (inst->getPort() == port)
|
||||
{
|
||||
delete inst;
|
||||
inst = new UDPWiimote(port, "Dolphin-Emu", index); //TODO: Changeable display name
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!udpEn)
|
||||
{
|
||||
if (inst)
|
||||
delete inst;
|
||||
inst = nullptr;
|
||||
return;
|
||||
}
|
||||
//else
|
||||
inst = new UDPWiimote(port, "Dolphin-Emu", index);
|
||||
}
|
||||
|
||||
UDPWrapper::~UDPWrapper()
|
||||
{
|
||||
if (inst)
|
||||
delete inst;
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
// Copyright 2013 Dolphin Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Common/Common.h"
|
||||
#include "Common/IniFile.h"
|
||||
#include "InputCommon/ControllerEmu.h"
|
||||
#include "InputCommon/UDPWiimote.h"
|
||||
|
||||
class UDPWrapper : public ControllerEmu::ControlGroup
|
||||
{
|
||||
public:
|
||||
UDPWiimote * inst;
|
||||
int index;
|
||||
bool updIR, updAccel, updButt, updNun, updNunAccel, udpEn; //upd from update and udp from... well... UDP
|
||||
std::string port;
|
||||
|
||||
UDPWrapper(int index, const char* const _name);
|
||||
virtual void LoadConfig(IniFile::Section *sec, const std::string& defdev = "", const std::string& base = "") override;
|
||||
virtual void SaveConfig(IniFile::Section *sec, const std::string& defdev = "", const std::string& base = "") override;
|
||||
void Refresh();
|
||||
virtual ~UDPWrapper();
|
||||
};
|
Reference in New Issue
Block a user