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