summaryrefslogtreecommitdiff
path: root/Source/Core/InputCommon/ControllerInterface
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Core/InputCommon/ControllerInterface')
-rw-r--r--Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp6
-rw-r--r--Source/Core/InputCommon/ControllerInterface/ControllerInterface.h3
-rw-r--r--Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.cpp180
-rw-r--r--Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.h65
4 files changed, 254 insertions, 0 deletions
diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp
index 0631069e16..93cd744003 100644
--- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp
+++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp
@@ -29,6 +29,9 @@
#ifdef CIFACE_USE_EVDEV
#include "InputCommon/ControllerInterface/evdev/evdev.h"
#endif
+#ifdef CIFACE_USE_PIPES
+ #include "InputCommon/ControllerInterface/Pipes/Pipes.h"
+#endif
using namespace ciface::ExpressionParser;
@@ -75,6 +78,9 @@ void ControllerInterface::Initialize(void* const hwnd)
#ifdef CIFACE_USE_EVDEV
ciface::evdev::Init(m_devices);
#endif
+#ifdef CIFACE_USE_PIPES
+ ciface::Pipes::Init(m_devices);
+#endif
m_is_init = true;
}
diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h
index 3aca18e95c..08b282dba8 100644
--- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h
+++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h
@@ -38,6 +38,9 @@
#if defined(HAVE_LIBEVDEV) && defined(HAVE_LIBUDEV)
#define CIFACE_USE_EVDEV
#endif
+#if defined(USE_PIPES)
+ #define CIFACE_USE_PIPES
+#endif
//
// ControllerInterface
diff --git a/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.cpp b/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.cpp
new file mode 100644
index 0000000000..f2923a5395
--- /dev/null
+++ b/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.cpp
@@ -0,0 +1,180 @@
+// Copyright 2015 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include <array>
+#include <cstdlib>
+#include <fcntl.h>
+#include <iostream>
+#include <map>
+#include <string>
+#include <unistd.h>
+#include <vector>
+#include <sys/stat.h>
+
+#include "Common/FileUtil.h"
+#include "Common/MathUtil.h"
+#include "Common/StringUtil.h"
+#include "InputCommon/ControllerInterface/Pipes/Pipes.h"
+
+namespace ciface
+{
+namespace Pipes
+{
+
+static const std::array<std::string, 12> s_button_tokens
+{{
+ "A",
+ "B",
+ "X",
+ "Y",
+ "Z",
+ "START",
+ "L",
+ "R",
+ "D_UP",
+ "D_DOWN",
+ "D_LEFT",
+ "D_RIGHT"
+}};
+
+static const std::array<std::string, 2> s_shoulder_tokens
+{{
+ "L",
+ "R"
+}};
+
+static const std::array<std::string, 2> s_axis_tokens
+{{
+ "MAIN",
+ "C"
+}};
+
+void Init(std::vector<Core::Device*>& devices)
+{
+ // Search the Pipes directory for files that we can open in read-only,
+ // non-blocking mode. The device name is the virtual name of the file.
+ File::FSTEntry fst;
+ int found = 0;
+ std::string dir_path = File::GetUserPath(D_PIPES_IDX);
+ if (!File::Exists(dir_path))
+ return;
+ fst = File::ScanDirectoryTree(dir_path, false);
+ if (!fst.isDirectory)
+ return;
+ for (unsigned int i = 0; i < fst.size; ++i)
+ {
+ const File::FSTEntry& child = fst.children[i];
+ if (child.isDirectory)
+ continue;
+ int fd = open(child.physicalName.c_str(), O_RDONLY | O_NONBLOCK);
+ if (fd < 0)
+ continue;
+ devices.push_back(new PipeDevice(fd, child.virtualName, found++));
+ }
+}
+
+PipeDevice::PipeDevice(int fd, const std::string& name, int id)
+ : m_fd(fd), m_name(name), m_id(id)
+{
+ for (const auto& tok : s_button_tokens)
+ {
+ PipeInput* btn = new PipeInput("Button " + tok);
+ AddInput(btn);
+ m_buttons[tok] = btn;
+ }
+ for (const auto& tok : s_shoulder_tokens)
+ {
+ AddAxis(tok, 0.0);
+ }
+ for (const auto& tok : s_axis_tokens)
+ {
+ AddAxis(tok + " X", 0.5);
+ AddAxis(tok + " Y", 0.5);
+ }
+}
+
+PipeDevice::~PipeDevice()
+{
+ close(m_fd);
+}
+
+void PipeDevice::UpdateInput()
+{
+ // Read any pending characters off the pipe. If we hit a newline,
+ // then dequeue a command off the front of m_buf and parse it.
+ char buf[32];
+ ssize_t bytes_read = read(m_fd, buf, sizeof buf);
+ while (bytes_read > 0)
+ {
+ m_buf.append(buf, bytes_read);
+ bytes_read = read(m_fd, buf, sizeof buf);
+ }
+ std::size_t newline = m_buf.find("\n");
+ std::size_t erase_until = 0;
+ while (newline != std::string::npos)
+ {
+ std::string command = m_buf.substr(0, newline);
+ ParseCommand(command);
+ erase_until = newline + 1;
+ newline = m_buf.find("\n", erase_until);
+ }
+ m_buf.erase(0, erase_until);
+}
+
+void PipeDevice::AddAxis(const std::string& name, double value)
+{
+ // Dolphin uses separate axes for left/right, which complicates things.
+ PipeInput* ax_hi = new PipeInput("Axis " + name + " +");
+ ax_hi->SetState(value);
+ PipeInput* ax_lo = new PipeInput("Axis " + name + " -");
+ ax_lo->SetState(value);
+ m_axes[name + " +"] = ax_hi;
+ m_axes[name + " -"] = ax_lo;
+ AddAnalogInputs(ax_lo, ax_hi);
+}
+
+void PipeDevice::SetAxis(const std::string& entry, double value)
+{
+ value = MathUtil::Clamp(value, 0.0, 1.0);
+ double hi = std::max(0.0, value - 0.5) * 2.0;
+ double lo = (0.5 - std::min(0.5, value)) * 2.0;
+ auto search_hi = m_axes.find(entry + " +");
+ if (search_hi != m_axes.end())
+ search_hi->second->SetState(hi);
+ auto search_lo = m_axes.find(entry + " -");
+ if (search_lo != m_axes.end())
+ search_lo->second->SetState(lo);
+}
+
+void PipeDevice::ParseCommand(const std::string& command)
+{
+ std::vector<std::string> tokens;
+ SplitString(command, ' ', tokens);
+ if (tokens.size() < 2 || tokens.size() > 4)
+ return;
+ if (tokens[0] == "PRESS" || tokens[0] == "RELEASE")
+ {
+ auto search = m_buttons.find(tokens[1]);
+ if (search != m_buttons.end())
+ search->second->SetState(tokens[0] == "PRESS" ? 1.0 : 0.0);
+ }
+ else if (tokens[0] == "SET")
+ {
+ if (tokens.size() == 3)
+ {
+ double value = strtod(tokens[2].c_str(), nullptr);
+ SetAxis(tokens[1], (value / 2.0) + 0.5);
+ }
+ else
+ {
+ double x = strtod(tokens[2].c_str(), nullptr);
+ double y = strtod(tokens[3].c_str(), nullptr);
+ SetAxis(tokens[1] + " X", x);
+ SetAxis(tokens[1] + " Y", y);
+ }
+ }
+}
+
+}
+}
diff --git a/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.h b/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.h
new file mode 100644
index 0000000000..c3c2ea0063
--- /dev/null
+++ b/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.h
@@ -0,0 +1,65 @@
+// Copyright 2015 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "InputCommon/ControllerInterface/Device.h"
+
+namespace ciface
+{
+namespace Pipes
+{
+// To create a piped controller input, create a named pipe in the
+// Pipes directory and write commands out to it. Commands are separated
+// by a newline character, with spaces separating command tokens.
+// Command syntax is as follows, where curly brackets are one-of and square
+// brackets are inclusive numeric ranges. Cases are sensitive. Numeric inputs
+// are clamped to [0, 1] and otherwise invalid commands are discarded.
+// {PRESS, RELEASE} {A, B, X, Y, Z, START, L, R, D_UP, D_DOWN, D_LEFT, D_RIGHT}
+// SET {L, R} [0, 1]
+// SET {MAIN, C} [0, 1] [0, 1]
+
+void Init(std::vector<Core::Device*>& devices);
+
+class PipeDevice : public Core::Device
+{
+public:
+ PipeDevice(int fd, const std::string& name, int id);
+ ~PipeDevice();
+
+ void UpdateInput() override;
+ std::string GetName() const override { return m_name; }
+ int GetId() const override { return m_id; }
+ std::string GetSource() const override { return "Pipe"; }
+
+private:
+ class PipeInput : public Input
+ {
+ public:
+ PipeInput(const std::string& name) : m_name(name), m_state(0.0) {}
+ std::string GetName() const override { return m_name; }
+ ControlState GetState() const override { return m_state; }
+ void SetState(ControlState state) { m_state = state; }
+ private:
+ const std::string m_name;
+ ControlState m_state;
+ };
+
+ void AddAxis(const std::string& name, double value);
+ void ParseCommand(const std::string& command);
+ void SetAxis(const std::string& entry, double value);
+
+ const int m_fd;
+ const std::string m_name;
+ const int m_id;
+ std::string m_buf;
+ std::map<std::string, PipeInput*> m_buttons;
+ std::map<std::string, PipeInput*> m_axes;
+};
+}
+}