• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "OISConfig.h"
2 #ifdef OIS_LIRC_SUPPORT
3 /*
4 The zlib/libpng License
5 
6 Copyright (c) 2005-2007 Phillip Castaneda (pjcast -- www.wreckedgames.com)
7 
8 This software is provided 'as-is', without any express or implied warranty. In no event will
9 the authors be held liable for any damages arising from the use of this software.
10 
11 Permission is granted to anyone to use this software for any purpose, including commercial
12 applications, and to alter it and redistribute it freely, subject to the following
13 restrictions:
14 
15     1. The origin of this software must not be misrepresented; you must not claim that
16 		you wrote the original software. If you use this software in a product,
17 		an acknowledgment in the product documentation would be appreciated but is
18 		not required.
19 
20     2. Altered source versions must be plainly marked as such, and must not be
21 		misrepresented as being the original software.
22 
23     3. This notice may not be removed or altered from any source distribution.
24 */
25 #include "OISLIRCFactoryCreator.h"
26 #include "OISException.h"
27 #include <assert.h>
28 #include <stdlib.h>
29 
30 #ifdef OIS_WIN32_PLATFORM
31 #  pragma warning (disable : 4996)
32 #  pragma warning (disable : 4267)
33 #  pragma warning (disable : 4554)
34 #  pragma warning (disable : 4996)
35 #  define _WIN32_WINNT 0x0500
36 #endif
37 #include <boost/asio.hpp>
38 #include <boost/bind.hpp>
39 #include <boost/thread.hpp>
40 
41 #include <istream>
42 #include <sstream>
43 
44 using namespace OIS;
45 
46 //---------------------------------------------------------------------------------//
47 class LIRCFactoryCreator::BoostWrapper
48 {
49 public:
BoostWrapper()50 	LIRCFactoryCreator::BoostWrapper() : mSocket(mIOService), mThreadHandler(0)
51 	{
52 	}
53 
54 	//TCP stuff
55 	boost::asio::io_service mIOService;
56 	boost::asio::ip::tcp::socket mSocket;
57 
58 	//Thread Stuff
59 	//! Boost thread execution object (only alive when at least 1 lirc is alive)
60 	boost::thread *mThreadHandler;
61 
62 	//! Gaurds access to the active lirc list
63 	boost::mutex mLircListMutex;
64 
65 };
66 
67 //---------------------------------------------------------------------------------//
LIRCFactoryCreator()68 LIRCFactoryCreator::LIRCFactoryCreator() :
69 	mConnected(false),
70 	mThreadRunning(false),
71 	mCount(0),
72 	mWrapped(0)
73 {
74 	mWrapped = new BoostWrapper();
75 
76 	mIP   = (getenv("OIS_LIRC_IP") != 0) ? getenv("OIS_LIRC_IP") : "127.0.0.1";
77 	mPort = (getenv("OIS_LIRC_PORT") != 0) ? getenv("OIS_LIRC_PORT") : "8765";
78 
79 	try
80 	{
81 		enableConnection(true);
82 		discoverRemotes();
83 	}
84 	catch(...)
85 	{
86 		mCount = 0;
87 	}
88 
89 	//Regardless of if there is remotes or not, we will close the conenction now.
90 	enableConnection(false);
91 }
92 
93 //---------------------------------------------------------------------------------//
~LIRCFactoryCreator()94 LIRCFactoryCreator::~LIRCFactoryCreator()
95 {
96 	enableConnectionThread(false);
97 	enableConnection(false);
98 
99 	delete mWrapped;
100 }
101 
102 //---------------------------------------------------------------------------------//
discoverRemotes()103 void LIRCFactoryCreator::discoverRemotes()
104 {
105 	//http://www.lirc.org/html/technical.html#applications
106 	mCount = 0;
107 
108 	mWrapped->mSocket.write_some(boost::asio::buffer("LIST\n"));
109 
110 	boost::asio::streambuf buffer;
111 
112 	//Read all remotes
113 	bool start = false;
114 	bool data = false;
115 	for(;;)
116 	{
117 		boost::asio::read_until(mWrapped->mSocket, buffer, '\n');
118 
119 		std::istream str(&buffer);
120 		std::string res;
121 		str >> res;
122 
123 		if( res == "" )				//If nothing left, we are done
124 			break;
125 		else if( res == "ERROR" )	//If any errors, we leave immediately
126 			return;
127 		else if( res == "END" )		//We have reached the end block
128 			start = false;
129 		else if( res == "DATA" )	//After Data will be a list of remote names
130 		{
131 			start = true;
132 			data = true;
133 			continue;
134 		}
135 
136 		//Have we  gotten the DATA word yet?
137 		if( start == false )
138 			continue;
139 
140 		if( data ) //How many?
141 			mCount = atoi(res.c_str());
142 		else //What follows should now be a list of remote names
143 			mUnusedRemotes.push_back(res);
144 
145 		data = false;
146 	}
147 
148 	//Read information about each remote
149 	boost::asio::streambuf buffer2;
150 	for( int i = 0; i < mCount; ++i )
151 	{
152 		std::ostringstream istr;
153 		istr << "LIST " << mUnusedRemotes[i] << "\n";
154 
155 		mWrapped->mSocket.write_some(boost::asio::buffer(istr.str()));
156 		RemoteInfo information;
157 		int buttonCount = 0;
158 
159 		start = data = false;
160 
161 		for(;;)
162 		{
163 			boost::asio::read_until(mWrapped->mSocket, buffer, '\n');
164 
165 			std::istream str(&buffer);
166 			std::string res;
167 			str >> res;
168 
169 			if( res == "" )				//If nothing left, we are done
170 				break;
171 			else if( res == "ERROR" )	//If error, bail out
172 				return;
173 			else if( res == "END" )		//We have reached the end block
174 				start = false;
175 			else if( res == "DATA" )	//After Data will be button count
176 			{
177 				start = true;
178 				data = true;
179 				continue;
180 			}
181 
182 			//Have we  gotten the DATA word yet?
183 			if( start == false )
184 				continue;
185 
186 			if( data ) //After button count, there will be a list of button names
187 				information.buttons = atoi(res.c_str());
188 			else
189 				information.buttonMap[res] = buttonCount++;
190 
191 			data = false;
192 		}
193 
194 		mJoyStickInformation[mUnusedRemotes[i]] = information;
195 	}
196 }
197 
198 //---------------------------------------------------------------------------------//
enableConnection(bool enable,bool blocking)199 void LIRCFactoryCreator::enableConnection(bool enable, bool blocking)
200 {
201 	if( enable == true && mConnected == false )
202 	{
203 		boost::asio::ip::tcp::resolver resolver(mWrapped->mIOService);
204 		boost::asio::ip::tcp::resolver::query query(mIP, mPort);
205 		boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
206 		boost::asio::ip::tcp::resolver::iterator end;
207 
208 		//Connect (trying all found connections - ip4/ip6)
209 		boost::asio::error result = boost::asio::error::host_not_found;
210 		while (result && endpoint_iterator != end)
211 		{
212 			mWrapped->mSocket.close();
213 			mWrapped->mSocket.connect(*endpoint_iterator++, boost::asio::assign_error(result));
214 		}
215 
216 		if (result != boost::asio::error::success)
217 			throw (result);
218 
219 		if( blocking == false )
220 		{
221 			mWrapped->mSocket.io_control(boost::asio::socket_base::non_blocking_io(true));
222 		}
223 
224 		mConnected = true;
225 	}
226 	else if( enable == false )
227 	{
228 		mWrapped->mSocket.close();
229 		mConnected = false;
230 	}
231 }
232 
233 //---------------------------------------------------------------------------------//
enableConnectionThread(bool enable)234 void LIRCFactoryCreator::enableConnectionThread(bool enable)
235 {
236 	if( enable == true && mThreadRunning == false )
237 	{
238 		mThreadRunning = true;
239 		mWrapped->mThreadHandler = new boost::thread(boost::bind(&LIRCFactoryCreator::threadUpdate, this));
240 	}
241 	else if( enable == false && mThreadRunning == true )
242 	{
243 		mThreadRunning = false;
244 		mWrapped->mThreadHandler->join();
245 		delete mWrapped->mThreadHandler;
246 		mWrapped->mThreadHandler = 0;
247 	}
248 }
249 
250 //---------------------------------------------------------------------------------//
threadUpdate()251 void LIRCFactoryCreator::threadUpdate()
252 {
253 	boost::xtime timer;
254 	boost::asio::streambuf buffer;
255 	std::istream stream(&buffer);
256 	std::string code, repeat, button, remote;
257 
258 
259 	while( mThreadRunning )
260 	{
261 		try
262 		{
263 			while(  mWrapped->mSocket.in_avail() > 0 )
264 			{
265 				boost::asio::read_until(mWrapped->mSocket, buffer, '\n');
266 
267 				stream >> code;   //64 bit value, ignorable
268 				stream >> repeat; //Repeat rate starting at zero (we ignore, for now)
269 				stream >> button; //Button name
270 				stream >> remote; //Remote name
271 
272 				{	//Lock object, find out which remote sent event
273 					boost::mutex::scoped_lock arrayLock(mWrapped->mLircListMutex);
274 					std::map<std::string, LIRCControl*>::iterator i = mUpdateRemotes.find(remote);
275 					if( i != mUpdateRemotes.end() )
276 					{
277 						i->second->queueButtonPressed(button);
278 					}
279 				}
280 			}
281 		}
282 		catch(...)
283 		{	//Hmm, what should we do if we get a socket error here.. Ignore it I suppose,
284 		}	//and wait till the used remote objects get shutdown. We could try to
285 			//reconnect, but how do we know if we will even get the same remotes.
286 
287 		boost::xtime_get(&timer, boost::TIME_UTC);
288 		timer.nsec += 300000000; // 100 000 000 ~= .3 sec
289 		boost::thread::sleep(timer);
290 	}
291 }
292 
293 //---------------------------------------------------------------------------------//
freeDeviceList()294 DeviceList LIRCFactoryCreator::freeDeviceList()
295 {
296 	DeviceList list;
297 	for( std::vector<std::string>::iterator i = mUnusedRemotes.begin(); i != mUnusedRemotes.end(); ++i )
298 		list.insert(std::make_pair(OISJoyStick, *i));
299 
300 	return list;
301 }
302 
303 //---------------------------------------------------------------------------------//
totalDevices(Type iType)304 int LIRCFactoryCreator::totalDevices(Type iType)
305 {
306 	if( iType == OISJoyStick )
307 		return mCount;
308 	else
309 		return 0;
310 }
311 
312 //---------------------------------------------------------------------------------//
freeDevices(Type iType)313 int LIRCFactoryCreator::freeDevices(Type iType)
314 {
315 	if( iType == OISJoyStick )
316 		return (int)mUnusedRemotes.size();
317 	else
318 		return 0;
319 }
320 
321 //---------------------------------------------------------------------------------//
vendorExist(Type iType,const std::string & vendor)322 bool LIRCFactoryCreator::vendorExist(Type iType, const std::string & vendor)
323 {
324 	if( iType == OISJoyStick && std::find(mUnusedRemotes.begin(), mUnusedRemotes.end(), vendor) != mUnusedRemotes.end() )
325 		return true;
326 	else
327 		return false;
328 }
329 
330 //---------------------------------------------------------------------------------//
createObject(InputManager * creator,Type iType,bool bufferMode,const std::string & vendor)331 Object* LIRCFactoryCreator::createObject(InputManager* creator, Type iType, bool bufferMode, const std::string & vendor)
332 {
333 	if( mUnusedRemotes.size() > 0 )
334 	{
335 		std::vector<std::string>::iterator remote = mUnusedRemotes.end();
336 		if( vendor == "" )
337 			remote = mUnusedRemotes.begin();
338 		else
339 			remote = std::find(mUnusedRemotes.begin(), mUnusedRemotes.end(), vendor);
340 
341 		if( remote != mUnusedRemotes.end() )
342 		{
343 			//Make sure connection is established
344 			enableConnection(true, false);
345 
346 			//Make sure connection thread is alive
347 			enableConnectionThread(true);
348 
349 			//Create device
350 			LIRCControl *obj = new LIRCControl(creator, 0, bufferMode, this, mJoyStickInformation[*remote]);
351 
352 			//Add to used list, and then remove from unused list
353 			{
354 				boost::mutex::scoped_lock arrayLock(mWrapped->mLircListMutex);
355 				mUpdateRemotes[*remote] = obj;
356 			}
357 			mUnusedRemotes.erase(remote);
358 
359 			return obj;
360 		}
361 	}
362 
363 	OIS_EXCEPT(E_InputDeviceNonExistant, "No Device found which matches description!");
364 }
365 
366 //---------------------------------------------------------------------------------//
destroyObject(Object * obj)367 void LIRCFactoryCreator::destroyObject(Object* obj)
368 {
369 	if( obj == 0 )
370 		return;
371 
372 	int remotes_alive = 0;
373 
374 	{	//Scope lock
375 		boost::mutex::scoped_lock arrayLock(mWrapped->mLircListMutex);
376 
377 		//Find object
378 		std::map<std::string, LIRCControl*>::iterator i = mUpdateRemotes.begin(), e = mUpdateRemotes.end();
379 		bool found = false;
380 		for(; i != e; ++i)
381 		{
382 			if( i->second == obj )
383 			{
384 				found = true;
385 				break;
386 			}
387 		}
388 
389 		if( found == false )
390 			OIS_EXCEPT(E_General, "Device not found in LIRC remote collection!");
391 
392 		//Move from used to unused list
393 		mUnusedRemotes.push_back(i->first);
394 		mUpdateRemotes.erase(i);
395 
396 		delete obj;
397 
398 		remotes_alive = (int)mUpdateRemotes.size();
399 	}
400 
401 	//Destroy thread if no longer in use (we do this after unlocking mutex!)
402 	if( remotes_alive == 0 )
403 		enableConnectionThread(false);
404 }
405 #endif
406