2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "threads/SystemClock.h"
22 #include "IRServerSuite.h"
23 #include "IrssMessage.h"
24 #include "input/ButtonTranslator.h"
25 #include "utils/log.h"
26 #include "settings/AdvancedSettings.h"
27 #include "utils/TimeUtils.h"
30 #define IRSS_PORT 24000
32 CRemoteControl g_RemoteControl;
34 CRemoteControl::CRemoteControl() : CThread("CRemoteControl")
36 m_socket = INVALID_SOCKET;
37 m_bInitialized = false;
38 m_isConnecting = false;
43 CRemoteControl::~CRemoteControl()
48 void CRemoteControl::Disconnect()
54 void CRemoteControl::Close()
56 m_isConnecting = false;
57 if (m_socket != INVALID_SOCKET)
61 m_bInitialized = false;
62 CIrssMessage message(IRSSMT_UnregisterClient, IRSSMF_Request | IRSSMF_ForceNotRespond);
65 shutdown(m_socket, SD_BOTH);
66 closesocket(m_socket);
67 m_socket = INVALID_SOCKET;
71 void CRemoteControl::Reset()
76 void CRemoteControl::Initialize()
78 if (m_isConnecting || m_bInitialized) return;
79 //trying to connect when there is nothing to connect to is kinda slow so kick it off in a thread.
83 void CRemoteControl::Process()
85 unsigned int iMsRetryDelay = 5000;
86 unsigned int time = XbmcThreads::SystemClockMillis() - iMsRetryDelay;
87 // try to connect 60 times @ a 5 second interval (5 minutes)
88 // multiple tries because irss service might be up and running a little later then xbmc on boot.
89 while (!m_bStop && m_iAttempt <= 60)
91 if (XbmcThreads::SystemClockMillis() - time >= iMsRetryDelay)
93 time = XbmcThreads::SystemClockMillis();
98 CLog::Log(LOGINFO, "CRemoteControl::Process - failed to connect to irss, will keep retrying every %d seconds", iMsRetryDelay / 1000);
107 bool CRemoteControl::Connect()
109 char namebuf[NI_MAXHOST], portbuf[NI_MAXSERV];
110 struct addrinfo hints = {};
111 struct addrinfo *result, *addr;
115 hints.ai_family = AF_UNSPEC;
116 hints.ai_socktype = SOCK_STREAM;
117 hints.ai_protocol = IPPROTO_TCP;
118 sprintf(service, "%d", IRSS_PORT);
120 res = getaddrinfo("localhost", service, &hints, &result);
124 CLog::Log(LOGDEBUG, "CRemoteControl::Connect - getaddrinfo failed: %s", gai_strerror(res));
128 for(addr = result; addr; addr = addr->ai_next)
130 if(getnameinfo(addr->ai_addr, addr->ai_addrlen, namebuf, sizeof(namebuf), portbuf, sizeof(portbuf),NI_NUMERICHOST))
132 strcpy(namebuf, "[unknown]");
133 strcpy(portbuf, "[unknown]");
137 CLog::Log(LOGDEBUG, "CRemoteControl::Connect - connecting to: %s:%s ...", namebuf, portbuf);
139 m_socket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
140 if(m_socket == INVALID_SOCKET)
143 if(connect(m_socket, addr->ai_addr, addr->ai_addrlen) != SOCKET_ERROR)
146 closesocket(m_socket);
147 m_socket = INVALID_SOCKET;
150 freeaddrinfo(result);
151 if(m_socket == INVALID_SOCKET)
154 CLog::Log(LOGDEBUG, "CRemoteControl::Connect - failed to connect");
159 u_long iMode = 1; //non-blocking
160 if (ioctlsocket(m_socket, FIONBIO, &iMode) == SOCKET_ERROR)
163 CLog::Log(LOGERROR, "IRServerSuite: failed to set socket to non-blocking.");
169 CIrssMessage mess(IRSSMT_RegisterClient, IRSSMF_Request);
170 if (!SendPacket(mess))
173 CLog::Log(LOGERROR, "IRServerSuite: failed to send RegisterClient packet.");
176 m_isConnecting = true;
180 bool CRemoteControl::SendPacket(CIrssMessage& message)
183 char* bytes = message.ToBytes(iSize);
185 uint32_t len = htonl(iSize);
186 memcpy(&buffer[0], &len, 4);
187 bool bResult = WriteN(&buffer[0], 4);
190 bResult = WriteN(bytes, iSize);
202 void CRemoteControl::Update()
204 if ((!m_bInitialized && !m_isConnecting) || (m_socket == INVALID_SOCKET))
210 if (!ReadPacket(mess))
214 switch (mess.GetType())
216 case IRSSMT_RegisterClient:
217 m_isConnecting = false;
218 if ((mess.GetFlags() & IRSSMF_Success) != IRSSMF_Success)
220 //uh oh, it failed to register
222 CLog::Log(LOGERROR, "IRServerSuite: failed to register XBMC as a client.");
226 m_bInitialized = true;
227 //request info about receivers
228 CIrssMessage mess(IRSSMT_DetectedReceivers, IRSSMF_Request);
229 if (!SendPacket(mess))
231 CLog::Log(LOGERROR, "IRServerSuite: failed to send AvailableReceivers packet.");
233 mess.SetType(IRSSMT_AvailableReceivers);
234 if (!SendPacket(mess))
236 CLog::Log(LOGERROR, "IRServerSuite: failed to send AvailableReceivers packet.");
238 mess.SetType(IRSSMT_ActiveReceivers);
239 if (!SendPacket(mess))
241 CLog::Log(LOGERROR, "IRServerSuite: failed to send AvailableReceivers packet.");
245 case IRSSMT_RemoteEvent:
246 HandleRemoteEvent(mess);
249 //I suppose the errormessage is in the packet somewhere...
250 CLog::Log(LOGERROR, "IRServerSuite: we got an error message.");
252 case IRSSMT_ServerShutdown:
255 case IRSSMT_ServerSuspend:
256 //should we do something?
258 case IRSSMT_ServerResume:
259 //should we do something?
261 case IRSSMT_AvailableReceivers:
263 uint32_t size = mess.GetDataSize();
266 char* data = mess.GetData();
267 char* availablereceivers = new char[size + 1];
268 memcpy(availablereceivers, data, size);
269 availablereceivers[size] = '\0';
270 CLog::Log(LOGINFO, "IRServerSuite: Available receivers: %s", availablereceivers);
271 delete[] availablereceivers;
275 case IRSSMT_DetectedReceivers:
277 uint32_t size = mess.GetDataSize();
280 char* data = mess.GetData();
281 char* detectedreceivers = new char[size + 1];
282 memcpy(detectedreceivers, data, size);
283 detectedreceivers[size] = '\0';
284 CLog::Log(LOGINFO, "IRServerSuite: Detected receivers: %s", detectedreceivers);
285 delete[] detectedreceivers;
289 case IRSSMT_ActiveReceivers:
291 uint32_t size = mess.GetDataSize();
294 char* data = mess.GetData();
295 char* activereceivers = new char[size + 1];
296 memcpy(activereceivers, data, size);
297 activereceivers[size] = '\0';
298 CLog::Log(LOGINFO, "IRServerSuite: Active receivers: %s", activereceivers);
299 delete[] activereceivers;
306 bool CRemoteControl::HandleRemoteEvent(CIrssMessage& message)
310 //flag should be notify, maybe check it?
311 char* data = message.GetData();
312 uint32_t datalen = message.GetDataSize();
315 uint32_t devicenamelength;
316 uint32_t keycodelength;
319 CLog::Log(LOGERROR, "IRServerSuite: no data in remote message.");
324 //seems to be version 1.0.4.1, only keycode is sent, use Microsoft MCE mapping??
325 devicenamelength = 13;
326 deviceName = new char[devicenamelength + 1];
327 sprintf(deviceName, "Microsoft MCE");
328 keycodelength = datalen;
329 keycode = new char[keycodelength + 1];
330 memcpy(keycode, data, keycodelength);
334 //first 4 bytes is devicename length
335 memcpy(&devicenamelength, data, 4);
337 if (datalen < 4 + devicenamelength)
339 CLog::Log(LOGERROR, "IRServerSuite: invalid data in remote message (size: %u).", datalen);
342 deviceName = new char[devicenamelength + 1];
343 memcpy(deviceName, data + 4, devicenamelength);
344 if (datalen < 8 + devicenamelength)
346 CLog::Log(LOGERROR, "IRServerSuite: invalid data in remote message (size: %u).", datalen);
350 //next 4 bytes is keycode length
351 memcpy(&keycodelength, data + 4 + devicenamelength, 4);
353 if (datalen < 8 + devicenamelength + keycodelength)
355 CLog::Log(LOGERROR, "IRServerSuite: invalid data in remote message (size: %u).", datalen);
359 keycode = new char[keycodelength + 1];
360 memcpy(keycode, data + 8 + devicenamelength, keycodelength);
362 deviceName[devicenamelength] = '\0';
363 keycode[keycodelength] = '\0';
364 //translate to a buttoncode xbmc understands
365 m_button = CButtonTranslator::GetInstance().TranslateLircRemoteString(deviceName, keycode);
366 if (g_advancedSettings.m_logLevel == LOG_LEVEL_DEBUG_FREEMEM)
368 CLog::Log(LOGINFO, "IRServerSuite, RemoteEvent: %s %s", deviceName, keycode);
376 CLog::Log(LOGERROR, "IRServerSuite: exception while processing RemoteEvent.");
381 int CRemoteControl::ReadN(char *buffer, int n)
383 int nOriginalSize = n;
384 memset(buffer, 0, n);
389 nBytes = recv(m_socket, ptr, n, 0);
391 if (WSAGetLastError() == WSAEWOULDBLOCK)
393 return nOriginalSize - n;
399 CLog::Log(LOGERROR, "%s, IRServerSuite recv error %d", __FUNCTION__, GetLastError());
407 CLog::Log(LOGDEBUG,"%s, IRServerSuite socket closed by server", __FUNCTION__);
416 return nOriginalSize - n;
419 bool CRemoteControl::WriteN(const char *buffer, int n)
421 const char *ptr = buffer;
424 int nBytes = send(m_socket, ptr, n, 0);
427 CLog::Log(LOGERROR, "%s, IRServerSuite send error %d (%d bytes)", __FUNCTION__, GetLastError(), n);
442 bool CRemoteControl::ReadPacket(CIrssMessage &message)
447 int iRead = ReadN(&sizebuf[0], 4);
448 if (iRead <= 0) return false; //nothing to read
451 CLog::Log(LOGERROR, "IRServerSuite: failed to read packetsize.");
455 memcpy(&size, &sizebuf[0], 4);
457 char* messagebytes = new char[size];
458 if (ReadN(messagebytes, size) != size)
460 CLog::Log(LOGERROR, "IRServerSuite: failed to read packet.");
463 if (!CIrssMessage::FromBytes(messagebytes, size, message))
465 CLog::Log(LOGERROR, "IRServerSuite: invalid packet received (size: %u).", size);
468 delete[] messagebytes;
473 CLog::Log(LOGERROR, "IRServerSuite: exception while processing packet.");
478 WORD CRemoteControl::GetButton()
483 unsigned int CRemoteControl::GetHoldTime() const
488 void CRemoteControl::setUsed(bool value)