| 
									
										
										
										
											2015-10-24 20:20:03 -07:00
										 |  |  | // Copyright 2015 Dolphin Emulator Project
 | 
					
						
							|  |  |  | // Licensed under GPLv2+
 | 
					
						
							|  |  |  | // Refer to the license.txt file included.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <array>
 | 
					
						
							|  |  |  | #include <cstdlib>
 | 
					
						
							|  |  |  | #include <fcntl.h>
 | 
					
						
							|  |  |  | #include <iostream>
 | 
					
						
							| 
									
										
										
										
											2016-01-20 22:04:42 +01:00
										 |  |  | #include <locale>
 | 
					
						
							| 
									
										
										
										
											2015-10-24 20:20:03 -07:00
										 |  |  | #include <map>
 | 
					
						
							| 
									
										
										
										
											2016-01-20 22:04:42 +01:00
										 |  |  | #include <sstream>
 | 
					
						
							| 
									
										
										
										
											2015-10-24 20:20:03 -07:00
										 |  |  | #include <string>
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | #include <sys/stat.h>
 | 
					
						
							| 
									
										
										
										
											2015-10-24 20:20:03 -07:00
										 |  |  | #include <unistd.h>
 | 
					
						
							|  |  |  | #include <vector>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "Common/FileUtil.h"
 | 
					
						
							|  |  |  | #include "Common/MathUtil.h"
 | 
					
						
							|  |  |  | #include "Common/StringUtil.h"
 | 
					
						
							| 
									
										
										
										
											2016-06-12 17:08:04 +02:00
										 |  |  | #include "InputCommon/ControllerInterface/ControllerInterface.h"
 | 
					
						
							| 
									
										
										
										
											2015-10-24 20:20:03 -07:00
										 |  |  | #include "InputCommon/ControllerInterface/Pipes/Pipes.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace ciface | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | namespace Pipes | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | 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"}}; | 
					
						
							| 
									
										
										
										
											2015-10-24 20:20:03 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | static const std::array<std::string, 2> s_shoulder_tokens{{"L", "R"}}; | 
					
						
							| 
									
										
										
										
											2015-10-24 20:20:03 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | static const std::array<std::string, 2> s_axis_tokens{{"MAIN", "C"}}; | 
					
						
							| 
									
										
										
										
											2015-10-24 20:20:03 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-20 22:04:42 +01:00
										 |  |  | static double StringToDouble(const std::string& text) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   std::istringstream is(text); | 
					
						
							|  |  |  |   // ignore current locale
 | 
					
						
							|  |  |  |   is.imbue(std::locale::classic()); | 
					
						
							|  |  |  |   double result; | 
					
						
							|  |  |  |   is >> result; | 
					
						
							|  |  |  |   return result; | 
					
						
							| 
									
										
										
										
											2016-01-20 22:04:42 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-16 13:39:05 -07:00
										 |  |  | void PopulateDevices() | 
					
						
							| 
									
										
										
										
											2015-10-24 20:20:03 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   // 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; | 
					
						
							|  |  |  |   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; | 
					
						
							| 
									
										
										
										
											2016-07-14 10:25:52 +02:00
										 |  |  |     g_controller_interface.AddDevice(std::make_shared<PipeDevice>(fd, child.virtualName)); | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2015-10-24 20:20:03 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-14 10:25:52 +02:00
										 |  |  | PipeDevice::PipeDevice(int fd, const std::string& name) : m_fd(fd), m_name(name) | 
					
						
							| 
									
										
										
										
											2015-10-24 20:20:03 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   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); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2015-10-24 20:20:03 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PipeDevice::~PipeDevice() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   close(m_fd); | 
					
						
							| 
									
										
										
										
											2015-10-24 20:20:03 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PipeDevice::UpdateInput() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   // 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"); | 
					
						
							|  |  |  |   while (newline != std::string::npos) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     std::string command = m_buf.substr(0, newline); | 
					
						
							|  |  |  |     ParseCommand(command); | 
					
						
							|  |  |  |     m_buf.erase(0, newline + 1); | 
					
						
							|  |  |  |     newline = m_buf.find("\n"); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2015-10-24 20:20:03 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PipeDevice::AddAxis(const std::string& name, double value) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   // 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); | 
					
						
							| 
									
										
										
										
											2015-10-24 20:20:03 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PipeDevice::SetAxis(const std::string& entry, double value) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   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); | 
					
						
							| 
									
										
										
										
											2015-10-24 20:20:03 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PipeDevice::ParseCommand(const std::string& command) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2017-06-11 16:33:10 +02:00
										 |  |  |   const std::vector<std::string> tokens = SplitString(command, ' '); | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   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 = StringToDouble(tokens[2]); | 
					
						
							|  |  |  |       SetAxis(tokens[1], (value / 2.0) + 0.5); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (tokens.size() == 4) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       double x = StringToDouble(tokens[2]); | 
					
						
							|  |  |  |       double y = StringToDouble(tokens[3]); | 
					
						
							|  |  |  |       SetAxis(tokens[1] + " X", x); | 
					
						
							|  |  |  |       SetAxis(tokens[1] + " Y", y); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2015-10-24 20:20:03 -07:00
										 |  |  | } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | } |