Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
InputCommon: Add a new ExpressionParser to replace the old hack language
This contains a new, hand-written expression parser to replace the old
hack language based on string munging. The new approach is a simple
AST-based evaluation approach, instead of the "list of operations"
infix-based hack that there was before.

The new language for configuration has support for parentheses, and
counts "!" as a unary operator instead of the binary "NOT OR" operator
it was before. A simple example:

  (X & Y) | !B

Explicit device references, and complex device names ("Right Y+") are
handled with backticks and colons:

  (`SDL/0/6 axis joystick:Right X+` & `DInput/0/Keyboard Mouse:A`)

The basic editor UI that inserts tokens has not been updated to reflect
the new language.
  • Loading branch information
magcius committed Jun 25, 2013
1 parent 877106b commit 6246f6e
Show file tree
Hide file tree
Showing 7 changed files with 633 additions and 129 deletions.
3 changes: 2 additions & 1 deletion Source/Core/InputCommon/CMakeLists.txt
Expand Up @@ -3,7 +3,8 @@ set(SRCS Src/ControllerEmu.cpp
Src/UDPWiimote.cpp
Src/UDPWrapper.cpp
Src/ControllerInterface/ControllerInterface.cpp
Src/ControllerInterface/Device.cpp)
Src/ControllerInterface/Device.cpp
Src/ControllerInterface/ExpressionParser.cpp)

if(WIN32)
set(SRCS ${SRCS}
Expand Down
2 changes: 2 additions & 0 deletions Source/Core/InputCommon/InputCommon.vcxproj
Expand Up @@ -167,6 +167,7 @@
<ClCompile Include="Src\ControllerEmu.cpp" />
<ClCompile Include="Src\ControllerInterface\ControllerInterface.cpp" />
<ClCompile Include="Src\ControllerInterface\Device.cpp" />
<ClCompile Include="Src\ControllerInterface\ExpressionParser.cpp" />
<ClCompile Include="Src\ControllerInterface\DInput\DInput.cpp" />
<ClCompile Include="Src\ControllerInterface\DInput\DInputJoystick.cpp" />
<ClCompile Include="Src\ControllerInterface\DInput\DInputKeyboardMouse.cpp" />
Expand All @@ -180,6 +181,7 @@
<ClInclude Include="Src\ControllerEmu.h" />
<ClInclude Include="Src\ControllerInterface\ControllerInterface.h" />
<ClInclude Include="Src\ControllerInterface\Device.h" />
<ClInclude Include="Src\ControllerInterface\ExpressionParser.h" />
<ClInclude Include="Src\ControllerInterface\DInput\DInput.h" />
<ClInclude Include="Src\ControllerInterface\DInput\DInputJoystick.h" />
<ClInclude Include="Src\ControllerInterface\DInput\DInputKeyboardMouse.h" />
Expand Down
11 changes: 10 additions & 1 deletion Source/Core/InputCommon/InputCommon.vcxproj.filters
Expand Up @@ -23,6 +23,12 @@
<ClCompile Include="Src\ControllerInterface\DInput\DInput.cpp">
<Filter>ControllerInterface\DInput</Filter>
</ClCompile>
<ClCompile Include="Src\ControllerInterface\ExpressionParser.cpp">
<Filter>ControllerInterface</Filter>
</ClCompile>
<ClCompile Include="Src\ControllerInterface\Device.cpp">
<Filter>ControllerInterface</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Src\ControllerEmu.h" />
Expand All @@ -42,6 +48,9 @@
<ClInclude Include="Src\ControllerInterface\Device.h">
<Filter>ControllerInterface\Device</Filter>
</ClInclude>
<ClInclude Include="Src\ControllerInterface\ExpressionParser.h">
<Filter>ControllerInterface</Filter>
</ClInclude>
<ClInclude Include="Src\ControllerInterface\DInput\NamedKeys.h">
<Filter>ControllerInterface\DInput</Filter>
</ClInclude>
Expand Down Expand Up @@ -72,4 +81,4 @@
<UniqueIdentifier>{5a9c1b94-2eab-4357-b44f-87d5db50da3d}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>
</Project>
128 changes: 16 additions & 112 deletions Source/Core/InputCommon/Src/ControllerInterface/ControllerInterface.cpp
Expand Up @@ -21,6 +21,8 @@

#include "Thread.h"

using namespace ciface::ExpressionParser;

namespace
{
const float INPUT_DETECT_THRESHOLD = 0.55f;
Expand Down Expand Up @@ -187,48 +189,10 @@ bool ControllerInterface::UpdateOutput(const bool force)
//
ControlState ControllerInterface::InputReference::State( const ControlState ignore )
{
//if (NULL == device)
//return 0;

ControlState state = 0;

std::vector<DeviceControl>::const_iterator
ci = m_controls.begin(),
ce = m_controls.end();

// bit of hax for "NOT" to work at start of expression
if (ci != ce)
{
if (ci->mode == 2)
state = 1;
}

for (; ci!=ce; ++ci)
{
const ControlState istate = ci->control->ToInput()->GetState();

switch (ci->mode)
{
// OR
case 0 :
state = std::max(state, istate);
break;
// AND
case 1 :
state = std::min(state, istate);
break;
// NOT
case 2 :
state = std::max(std::min(state, 1.0f - istate), 0.0f);
break;
// ADD
case 3 :
state += istate;
break;
}
}

return std::min(1.0f, state * range);
if (parsed_expression)
return parsed_expression->GetValue();
else
return 0.0f;
}

//
Expand All @@ -240,17 +204,9 @@ ControlState ControllerInterface::InputReference::State( const ControlState igno
//
ControlState ControllerInterface::OutputReference::State(const ControlState state)
{
const ControlState tmp_state = std::min(1.0f, state * range);

// output ref just ignores the modes ( |&!... )

std::vector<DeviceControl>::iterator
ci = m_controls.begin(),
ce = m_controls.end();
for (; ci != ce; ++ci)
ci->control->ToOutput()->SetState(tmp_state);

return state; // just return the output, watever
if (parsed_expression)
parsed_expression->SetValue(state);
return 0.0f;
}

//
Expand All @@ -262,65 +218,13 @@ ControlState ControllerInterface::OutputReference::State(const ControlState stat
void ControllerInterface::UpdateReference(ControllerInterface::ControlReference* ref
, const DeviceQualifier& default_device) const
{
ref->m_controls.clear();
delete ref->parsed_expression;
ref->parsed_expression = NULL;

// adding | to parse the last item, silly
std::istringstream ss(ref->expression + '|');

const std::string mode_chars("|&!^");

ControlReference::DeviceControl devc;

std::string dev_str;
std::string ctrl_str;

char c = 0;
while (ss.read(&c, 1))
{
const size_t f = mode_chars.find(c);

if (mode_chars.npos != f)
{
// add ctrl
if (ctrl_str.size())
{
DeviceQualifier devq;

// using default device or alterate device inside `backticks`
if (dev_str.empty())
devq = default_device;
else
devq.FromString(dev_str);

// find device
Device* const def_device = FindDevice(devq);

if (def_device)
{
if (ref->is_input)
devc.control = FindInput(ctrl_str, def_device);
else
devc.control = FindOutput(ctrl_str, def_device);

if (devc.control)
ref->m_controls.push_back(devc);
}
}
// reset stuff for next ctrl
devc.mode = (int)f;
ctrl_str.clear();
}
else if ('`' == c)
{
// different device
if (std::getline(ss, dev_str, '`').eof())
break; // no terminating '`' character
}
else
{
ctrl_str += c;
}
}
ControlFinder finder(*this, default_device, ref->is_input);
ExpressionParseStatus status;
status = ParseExpression(ref->expression, finder, &ref->parsed_expression);
// XXX: do something with status?
}

//
Expand Down Expand Up @@ -388,7 +292,7 @@ Device::Control* ControllerInterface::OutputReference::Detect(const unsigned int
// ignore device

// don't hang if we don't even have any controls mapped
if (m_controls.size())
if (BoundCount() > 0)
{
State(1);
unsigned int slept = 0;
Expand Down
Expand Up @@ -9,6 +9,7 @@

#include "Common.h"
#include "Thread.h"
#include "ExpressionParser.h"
#include "Device.h"

// enable disable sources
Expand Down Expand Up @@ -53,30 +54,28 @@ class ControllerInterface : public DeviceContainer
class ControlReference
{
friend class ControllerInterface;

public:
virtual ~ControlReference() {}

virtual ControlState State(const ControlState state = 0) = 0;
virtual Device::Control* Detect(const unsigned int ms, Device* const device) = 0;
size_t BoundCount() const { return m_controls.size(); }

ControlState range;
ControlState range;
std::string expression;
const bool is_input;

protected:
ControlReference(const bool _is_input) : range(1), is_input(_is_input) {}
virtual ~ControlReference() {
delete parsed_expression;
}

struct DeviceControl
{
DeviceControl() : control(NULL), mode(0) {}
int BoundCount() {
if (parsed_expression)
return parsed_expression->num_controls;
else
return 0;
}

Device::Control* control;
int mode;
};

std::vector<DeviceControl> m_controls;
protected:
ControlReference(const bool _is_input) : range(1), is_input(_is_input), parsed_expression(NULL) {}
ciface::ExpressionParser::Expression *parsed_expression;
};

//
Expand Down

0 comments on commit 6246f6e

Please sign in to comment.