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: 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 //---------------------------------------------------------------------------------// 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 //---------------------------------------------------------------------------------// 94 LIRCFactoryCreator::~LIRCFactoryCreator() 95 { 96 enableConnectionThread(false); 97 enableConnection(false); 98 99 delete mWrapped; 100 } 101 102 //---------------------------------------------------------------------------------// 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 //---------------------------------------------------------------------------------// 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 //---------------------------------------------------------------------------------// 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 //---------------------------------------------------------------------------------// 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 //---------------------------------------------------------------------------------// 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 //---------------------------------------------------------------------------------// 304 int LIRCFactoryCreator::totalDevices(Type iType) 305 { 306 if( iType == OISJoyStick ) 307 return mCount; 308 else 309 return 0; 310 } 311 312 //---------------------------------------------------------------------------------// 313 int LIRCFactoryCreator::freeDevices(Type iType) 314 { 315 if( iType == OISJoyStick ) 316 return (int)mUnusedRemotes.size(); 317 else 318 return 0; 319 } 320 321 //---------------------------------------------------------------------------------// 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 //---------------------------------------------------------------------------------// 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 //---------------------------------------------------------------------------------// 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