Skip to content

Commit

Permalink
Add named pipe input for Windows
Browse files Browse the repository at this point in the history
 - 4 controller pipes are always open
 - Windows handles named pipes differently than Unix
 - Always at location: \\.\pipe\slippibot[1-4]
 - Otherwise, same interface as Unix for clients
  • Loading branch information
altf4 authored and NikhilNarayana committed Jul 21, 2020
1 parent 25eb19b commit 87dc83e
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 9 deletions.
29 changes: 29 additions & 0 deletions Data/Sys/Config/Profiles/GCPad/slippibot.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[Profile]
device = Pipe/0/slippibot1
buttons/a = Button A
buttons/b = Button B
buttons/x = Button X
buttons/y = Button Y
buttons/z = Button Z
buttons/l = Button L
buttons/r = Button R
main stick/up = Axis MAIN Y +
main stick/down = Axis MAIN Y -
main stick/left = Axis MAIN X -
main stick/right = Axis MAIN X +
triggers/l = Button L
triggers/r = Button R
main stick/modifier = Shift_L
main stick/modifier/range = 50.000000000000000
d-pad/up = Button D_UP
d-pad/down = Button D_DOWN
d-pad/left = Button D_LEFT
d-pad/right = Button D_RIGHT
buttons/start = Button START
c-stick/up = Axis C Y +
c-stick/down = Axis C Y -
c-stick/left = Axis C X -
c-stick/right = Axis C X +
triggers/l-analog = Axis L -+
triggers/r-analog = Axis R -+
triggers/threshold = 95.000000000000000
81 changes: 76 additions & 5 deletions Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
#include <sstream>
#include <string>
#include <sys/stat.h>
#include <unistd.h>
#include <vector>

#include "Common/FileUtil.h"
Expand Down Expand Up @@ -43,6 +42,32 @@ static double StringToDouble(const std::string& text)

void PopulateDevices()
{
#ifdef _WIN32
PIPE_FD pipes[4];
// Windows has named pipes, but they're different. They don't exist on the
// local filesystem and are transient. So rather than searching the /Pipes
// directory for pipes, we just always assume there's 4 and then make them
for (uint32_t i = 0; i < 4; i++)
{
std::string pipename = "\\\\.\\pipe\\slippibot" + std::to_string(i+1);
pipes[i] = CreateNamedPipeA(
pipename.data(), // pipe name
PIPE_ACCESS_INBOUND, // read access, inward only
PIPE_TYPE_BYTE | PIPE_NOWAIT, // byte mode, nonblocking
1, // number of clients
256, // output buffer size
256, // input buffer size
0, // timeout value
NULL // security attributes
);

// We're in nonblocking mode, so this won't wait for clients
ConnectNamedPipe(pipes[i], NULL);
std::string ui_pipe_name = "slippibot" + std::to_string(i+1);
g_controller_interface.AddDevice(std::make_shared<PipeDevice>(pipes[i], ui_pipe_name));
}
#else

// 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;
Expand All @@ -57,14 +82,15 @@ void PopulateDevices()
const File::FSTEntry& child = fst.children[i];
if (child.isDirectory)
continue;
int fd = open(child.physicalName.c_str(), O_RDONLY | O_NONBLOCK);
PIPE_FD fd = open(child.physicalName.c_str(), O_RDONLY | O_NONBLOCK);
if (fd < 0)
continue;
g_controller_interface.AddDevice(std::make_shared<PipeDevice>(fd, child.virtualName));
}
#endif
}

PipeDevice::PipeDevice(int fd, const std::string& name) : m_fd(fd), m_name(name)
PipeDevice::PipeDevice(PIPE_FD fd, const std::string& name) : m_fd(fd), m_name(name)
{
for (const auto& tok : s_button_tokens)
{
Expand All @@ -85,19 +111,64 @@ PipeDevice::PipeDevice(int fd, const std::string& name) : m_fd(fd), m_name(name)

PipeDevice::~PipeDevice()
{
#ifdef _WIN32
CloseHandle(m_fd);
#else
close(m_fd);
#endif
}

s32 PipeDevice::readFromPipe(PIPE_FD file_descriptor, char *in_buffer, size_t size)
{
#ifdef _WIN32

u32 bytes_available = 0;
DWORD bytesread = 0;
bool peek_success = PeekNamedPipe(
file_descriptor,
NULL,
0,
NULL,
(LPDWORD)&bytes_available,
NULL
);

if(!peek_success && (GetLastError() == ERROR_BROKEN_PIPE))
{
DisconnectNamedPipe(file_descriptor);
ConnectNamedPipe(file_descriptor, NULL);
return -1;
}

if(peek_success && (bytes_available > 0))
{
bool success = ReadFile(
file_descriptor, // pipe handle
in_buffer, // buffer to receive reply
(DWORD)std::min(bytes_available, (u32)size), // size of buffer
&bytesread, // number of bytes read
NULL); // not overlapped
if(!success)
{
return -1;
}
}
return (s32)bytesread;
#else
return read(file_descriptor, in_buffer, size);
#endif
}

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);
s32 bytes_read = readFromPipe(m_fd, buf, sizeof buf);
while (bytes_read > 0)
{
m_buf.append(buf, bytes_read);
bytes_read = read(m_fd, buf, sizeof buf);
bytes_read = readFromPipe(m_fd, buf, sizeof buf);
}
std::size_t newline = m_buf.find("\n");
while (newline != std::string::npos)
Expand Down
16 changes: 14 additions & 2 deletions Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,22 @@
#include <map>
#include <string>
#include <vector>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif

namespace ciface
{
namespace Pipes
{
#ifdef _WIN32
typedef HANDLE PIPE_FD;
#else
typedef int PIPE_FD;
#endif

// 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.
Expand All @@ -27,7 +38,7 @@ void PopulateDevices();
class PipeDevice : public Core::Device
{
public:
PipeDevice(int fd, const std::string& name);
PipeDevice(PIPE_FD fd, const std::string& name);
~PipeDevice();

void UpdateInput() override;
Expand All @@ -49,8 +60,9 @@ class PipeDevice : public Core::Device
void AddAxis(const std::string& name, double value);
void ParseCommand(const std::string& command);
void SetAxis(const std::string& entry, double value);
s32 readFromPipe(PIPE_FD file_descriptor, char *in_buffer, size_t size);

const int m_fd;
const PIPE_FD m_fd;
const std::string m_name;
std::string m_buf;
std::map<std::string, PipeInput*> m_buttons;
Expand Down
4 changes: 3 additions & 1 deletion Source/Core/InputCommon/InputCommon.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
<ClCompile Include="ControllerInterface\DInput\XInputFilter.cpp" />
<ClCompile Include="ControllerInterface\ExpressionParser.cpp" />
<ClCompile Include="ControllerInterface\ForceFeedback\ForceFeedbackDevice.cpp" />
<ClCompile Include="ControllerInterface\Pipes\Pipes.cpp" />
<ClCompile Include="ControllerInterface\XInput\XInput.cpp" />
<ClCompile Include="GCAdapter.cpp">
<!--
Expand All @@ -85,6 +86,7 @@
<ClInclude Include="ControllerInterface\DInput\XInputFilter.h" />
<ClInclude Include="ControllerInterface\ExpressionParser.h" />
<ClInclude Include="ControllerInterface\ForceFeedback\ForceFeedbackDevice.h" />
<ClInclude Include="ControllerInterface\Pipes\Pipes.h" />
<ClInclude Include="ControllerInterface\XInput\XInput.h" />
<ClInclude Include="GCAdapter.h" />
<ClInclude Include="GCPadStatus.h" />
Expand All @@ -101,4 +103,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>
11 changes: 10 additions & 1 deletion Source/Core/InputCommon/InputCommon.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
<Filter Include="ControllerInterface\XInput">
<UniqueIdentifier>{07bad1aa-7e03-4f5c-ade2-a44857c5cbc3}</UniqueIdentifier>
</Filter>
<Filter Include="ControllerInterface\Pipes">
<UniqueIdentifier>{489e6f5e-36e0-4c77-aad3-c08143727baf}</UniqueIdentifier>
</Filter>
<Filter Include="ControllerInterface\ForceFeedback">
<UniqueIdentifier>{e10ce316-283c-4be0-848d-578dec2b6404}</UniqueIdentifier>
</Filter>
Expand All @@ -30,6 +33,9 @@
<ClCompile Include="ControllerInterface\XInput\XInput.cpp">
<Filter>ControllerInterface\XInput</Filter>
</ClCompile>
<ClCompile Include="ControllerInterface\Pipes\Pipes.cpp">
<Filter>ControllerInterface\Pipes</Filter>
</ClCompile>
<ClCompile Include="ControllerInterface\Device.cpp">
<Filter>ControllerInterface</Filter>
</ClCompile>
Expand Down Expand Up @@ -62,6 +68,9 @@
<ClInclude Include="ControllerInterface\XInput\XInput.h">
<Filter>ControllerInterface\XInput</Filter>
</ClInclude>
<ClInclude Include="ControllerInterface\Pipes\Pipes.h">
<Filter>ControllerInterface\Pipes</Filter>
</ClInclude>
<ClInclude Include="ControllerInterface\ControllerInterface.h">
<Filter>ControllerInterface</Filter>
</ClInclude>
Expand All @@ -85,4 +94,4 @@
<ItemGroup>
<Text Include="CMakeLists.txt" />
</ItemGroup>
</Project>
</Project>
1 change: 1 addition & 0 deletions Source/VSProps/Base.props
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>USE_UPNP;__LIBUSB__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>PSAPI_VERSION=1;_M_X86=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>USE_PIPES=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>SFML_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>CURL_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Platform)'=='x64'">_ARCH_64=1;_M_X86_64=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
Expand Down

0 comments on commit 87dc83e

Please sign in to comment.