Home | History | Annotate | Download | only in LIRC
      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