diff options
author | AlTheKiller <AlTheKiller@svn> | 2009-09-23 01:49:50 +0000 |
---|---|---|
committer | AlTheKiller <AlTheKiller@svn> | 2009-09-23 01:49:50 +0000 |
commit | 45285e8a9300cd754a760560640b75b09f98035e (patch) | |
tree | ad9f093885ad5c98e9dd4156674e7691c22ed0a2 /guilib/common |
step 3/4: Move linuxport to trunk. How'd I get roped into this?
git-svn-id: https://xbmc.svn.sourceforge.net/svnroot/xbmc/trunk@23097 568bbfeb-2a22-0410-94d2-cc84cf5bfa90
Diffstat (limited to 'guilib/common')
-rw-r--r-- | guilib/common/IRServerSuite/IRServerSuite.cpp | 449 | ||||
-rw-r--r-- | guilib/common/IRServerSuite/IRServerSuite.h | 64 | ||||
-rw-r--r-- | guilib/common/IRServerSuite/IrssMessage.cpp | 146 | ||||
-rw-r--r-- | guilib/common/IRServerSuite/IrssMessage.h | 209 | ||||
-rw-r--r-- | guilib/common/LIRC.cpp | 257 | ||||
-rw-r--r-- | guilib/common/LIRC.h | 44 | ||||
-rw-r--r-- | guilib/common/Makefile.in | 14 | ||||
-rw-r--r-- | guilib/common/SDLJoystick.cpp | 388 | ||||
-rw-r--r-- | guilib/common/SDLJoystick.h | 78 |
9 files changed, 1649 insertions, 0 deletions
diff --git a/guilib/common/IRServerSuite/IRServerSuite.cpp b/guilib/common/IRServerSuite/IRServerSuite.cpp new file mode 100644 index 0000000000..0e70b368d4 --- /dev/null +++ b/guilib/common/IRServerSuite/IRServerSuite.cpp @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "IRServerSuite.h" +#include "IrssMessage.h" +#include "ButtonTranslator.h" +#include "log.h" +#include "AdvancedSettings.h" + +#define IRSS_PORT 24000 + +CRemoteControl g_RemoteControl; + +CRemoteControl::CRemoteControl() +{ + m_socket = INVALID_SOCKET; + m_bInitialized = false; + m_isConnecting = false; + Reset(); +} + +CRemoteControl::~CRemoteControl() +{ + Close(); +} + +void CRemoteControl::Disconnect() +{ + StopThread(); + Close(); +} + +void CRemoteControl::Close() +{ + m_isConnecting = false; + if (m_socket != INVALID_SOCKET) + { + if (m_bInitialized) + { + m_bInitialized = false; + CIrssMessage message(IRSSMT_UnregisterClient, IRSSMF_Request | IRSSMF_ForceNotRespond); + SendPacket(message); + } + shutdown(m_socket, SD_BOTH); + closesocket(m_socket); + m_socket = INVALID_SOCKET; + } +} + +void CRemoteControl::Reset() +{ + m_isHolding = false; + m_button = 0; +} + +void CRemoteControl::Initialize() +{ + //trying to connect when there is nothing to connect to is kinda slow so kick it off in a thread. + Create(); + SetName("CRemoteControl"); +} + +void CRemoteControl::Process() +{ + int iTries = 1; + DWORD iMsRetryDelay = 5000; + DWORD time = timeGetTime() - iMsRetryDelay; + // try to connect 6 times @ a 5 second interval (30 seconds) + // multiple tries because irss service might be up and running a little later then xbmc on boot. + while (!m_bStop && iTries <= 6) + { + if (timeGetTime() - time >= iMsRetryDelay) + { + time = timeGetTime(); + if (Connect()) + break; + iTries++; + } + Sleep(10); + } +} + +bool CRemoteControl::Connect() +{ + m_socket = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_socket == INVALID_SOCKET) + { + return false; //Couldn't create the socket + } + // Get the local host information + hostent* localHost = gethostbyname(""); + char* localIP = inet_ntoa (*(struct in_addr *)*localHost->h_addr_list); + + SOCKADDR_IN target; + memset(&target, 0, sizeof(SOCKADDR_IN)); + + target.sin_family = AF_INET; + target.sin_addr.s_addr = inet_addr(localIP); + target.sin_port = htons (IRSS_PORT); + + if (connect(m_socket, (SOCKADDR *)&target, sizeof(target)) == SOCKET_ERROR) + { + Close(); + return false; //Couldn't connect, irss not available + } + + u_long iMode = 1; //non-blocking + if (ioctlsocket(m_socket, FIONBIO, &iMode) == SOCKET_ERROR) + { + CLog::Log(LOGERROR, "IRServerSuite: failed to set socket to non-blocking."); + Close(); + return false; + } + + //register + CIrssMessage mess(IRSSMT_RegisterClient, IRSSMF_Request); + if (!SendPacket(mess)) + { + CLog::Log(LOGERROR, "IRServerSuite: failed to send RegisterClient packet."); + return false; + } + m_isConnecting = true; + return true; +} + +bool CRemoteControl::SendPacket(CIrssMessage& message) +{ + int iSize = 0; + char* bytes = message.ToBytes(iSize); + char buffer[4]; + uint32_t len = htonl(iSize); + memcpy(&buffer[0], &len, 4); + bool bResult = WriteN(&buffer[0], 4); + if (bResult) + { + bResult = WriteN(bytes, iSize); + } + delete[] bytes; + if (!bResult) + { + Close(); + return false; + } + return true; +} + + +void CRemoteControl::Update() +{ + if ((!m_bInitialized && !m_isConnecting) || (m_socket == INVALID_SOCKET)) + { + return; + } + + CIrssMessage mess; + if (!ReadPacket(mess)) + { + return; + } + switch (mess.GetType()) + { + case IRSSMT_RegisterClient: + m_isConnecting = false; + if ((mess.GetFlags() & IRSSMF_Success) != IRSSMF_Success) + { + //uh oh, it failed to register + Close(); + CLog::Log(LOGERROR, "IRServerSuite: failed to register XBMC as a client."); + } + else + { + m_bInitialized = true; + //request info about receivers + CIrssMessage mess(IRSSMT_DetectedReceivers, IRSSMF_Request); + if (!SendPacket(mess)) + { + CLog::Log(LOGERROR, "IRServerSuite: failed to send AvailableReceivers packet."); + } + mess.SetType(IRSSMT_AvailableReceivers); + if (!SendPacket(mess)) + { + CLog::Log(LOGERROR, "IRServerSuite: failed to send AvailableReceivers packet."); + } + mess.SetType(IRSSMT_ActiveReceivers); + if (!SendPacket(mess)) + { + CLog::Log(LOGERROR, "IRServerSuite: failed to send AvailableReceivers packet."); + } + } + break; + case IRSSMT_RemoteEvent: + HandleRemoteEvent(mess); + break; + case IRSSMT_Error: + //I suppose the errormessage is in the packet somewhere... + CLog::Log(LOGERROR, "IRServerSuite: we got an error message."); + break; + case IRSSMT_ServerShutdown: + Close(); + break; + case IRSSMT_ServerSuspend: + //should we do something? + break; + case IRSSMT_ServerResume: + //should we do something? + break; + case IRSSMT_AvailableReceivers: + { + uint32_t size = mess.GetDataSize(); + if (size > 0) + { + char* data = mess.GetData(); + char* availablereceivers = new char[size + 1]; + memcpy(availablereceivers, data, size); + availablereceivers[size] = '\0'; + CLog::Log(LOGINFO, "IRServerSuite: Available receivers: %s", availablereceivers); + delete[] availablereceivers; + } + } + break; + case IRSSMT_DetectedReceivers: + { + uint32_t size = mess.GetDataSize(); + if (size > 0) + { + char* data = mess.GetData(); + char* detectedreceivers = new char[size + 1]; + memcpy(detectedreceivers, data, size); + detectedreceivers[size] = '\0'; + CLog::Log(LOGINFO, "IRServerSuite: Detected receivers: %s", detectedreceivers); + delete[] detectedreceivers; + } + } + break; + case IRSSMT_ActiveReceivers: + { + uint32_t size = mess.GetDataSize(); + if (size > 0) + { + char* data = mess.GetData(); + char* activereceivers = new char[size + 1]; + memcpy(activereceivers, data, size); + activereceivers[size] = '\0'; + CLog::Log(LOGINFO, "IRServerSuite: Active receivers: %s", activereceivers); + delete[] activereceivers; + } + } + break; + } +} + +bool CRemoteControl::HandleRemoteEvent(CIrssMessage& message) +{ + try + { + //flag should be notify, maybe check it? + char* data = message.GetData(); + uint32_t datalen = message.GetDataSize(); + char* deviceName; + char* keycode; + uint32_t devicenamelength; + uint32_t keycodelength; + if (datalen == 0) + { + CLog::Log(LOGERROR, "IRServerSuite: no data in remote message."); + return false; + } + if (datalen <= 8) + { + //seems to be version 1.0.4.1, only keycode is sent, use Microsoft MCE mapping?? + devicenamelength = 13; + deviceName = new char[devicenamelength + 1]; + sprintf(deviceName, "Microsoft MCE"); + keycodelength = datalen; + keycode = new char[keycodelength + 1]; + memcpy(keycode, data, keycodelength); + } + else + { + //first 4 bytes is devicename length + memcpy(&devicenamelength, data, 4); + //devicename itself + if (datalen < 4 + devicenamelength) + { + CLog::Log(LOGERROR, "IRServerSuite: invalid data in remote message (size: %u).", datalen); + return false; + } + deviceName = new char[devicenamelength + 1]; + memcpy(deviceName, data + 4, devicenamelength); + if (datalen < 8 + devicenamelength) + { + CLog::Log(LOGERROR, "IRServerSuite: invalid data in remote message (size: %u).", datalen); + return false; + } + //next 4 bytes is keycode length + memcpy(&keycodelength, data + 4 + devicenamelength, 4); + //keycode itself + if (datalen < 8 + devicenamelength + keycodelength) + { + CLog::Log(LOGERROR, "IRServerSuite: invalid data in remote message (size: %u).", datalen); + delete[] deviceName; + return false; + } + keycode = new char[keycodelength + 1]; + memcpy(keycode, data + 8 + devicenamelength, keycodelength); + } + deviceName[devicenamelength] = '\0'; + keycode[keycodelength] = '\0'; + //translate to a buttoncode xbmc understands + m_button = CButtonTranslator::GetInstance().TranslateLircRemoteString(deviceName, keycode); + if (g_advancedSettings.m_logLevel == LOG_LEVEL_DEBUG_FREEMEM) + { + CLog::Log(LOGINFO, "IRServerSuite, RemoteEvent: %s %s", deviceName, keycode); + } + delete[] deviceName; + delete[] keycode; + return true; + } + catch(...) + { + CLog::Log(LOGERROR, "IRServerSuite: exception while processing RemoteEvent."); + return false; + } +} + +int CRemoteControl::ReadN(char *buffer, int n) +{ + int nOriginalSize = n; + memset(buffer, 0, n); + char *ptr = buffer; + while (n > 0) + { + int nBytes = 0; + nBytes = recv(m_socket, ptr, n, 0); + + if (WSAGetLastError() == WSAEWOULDBLOCK) + { + return nOriginalSize - n; + } + if (nBytes < 0) + { + if (!m_isConnecting) + { + CLog::Log(LOGERROR, "%s, IRServerSuite recv error %d", __FUNCTION__, GetLastError()); + } + Close(); + return -1; + } + + if (nBytes == 0) + { + CLog::Log(LOGDEBUG,"%s, IRServerSuite socket closed by server", __FUNCTION__); + Close(); + break; + } + + n -= nBytes; + ptr += nBytes; + } + + return nOriginalSize - n; +} + +bool CRemoteControl::WriteN(const char *buffer, int n) +{ + const char *ptr = buffer; + while (n > 0) + { + int nBytes = send(m_socket, ptr, n, 0); + if (nBytes < 0) + { + CLog::Log(LOGERROR, "%s, IRServerSuite send error %d (%d bytes)", __FUNCTION__, GetLastError(), n); + Close(); + return false; + } + + if (nBytes == 0) + break; + + n -= nBytes; + ptr += nBytes; + } + + return n == 0; +} + +bool CRemoteControl::ReadPacket(CIrssMessage &message) +{ + try + { + char sizebuf[4]; + int iRead = ReadN(&sizebuf[0], 4); + if (iRead <= 0) return false; //nothing to read + if (iRead != 4) + { + CLog::Log(LOGERROR, "IRServerSuite: failed to read packetsize."); + return false; + } + uint32_t size = 0; + memcpy(&size, &sizebuf[0], 4); + size = ntohl(size); + char* messagebytes = new char[size]; + if (ReadN(messagebytes, size) != size) + { + CLog::Log(LOGERROR, "IRServerSuite: failed to read packet."); + return false; + } + if (!CIrssMessage::FromBytes(messagebytes, size, message)) + { + CLog::Log(LOGERROR, "IRServerSuite: invalid packet received (size: %u).", size); + return false; + } + delete[] messagebytes; + return true; + } + catch(...) + { + CLog::Log(LOGERROR, "IRServerSuite: exception while processing packet."); + return false; + } +} + +WORD CRemoteControl::GetButton() +{ + return m_button; +} + +bool CRemoteControl::IsHolding() +{ + return m_isHolding; +} diff --git a/guilib/common/IRServerSuite/IRServerSuite.h b/guilib/common/IRServerSuite/IRServerSuite.h new file mode 100644 index 0000000000..d2cb322640 --- /dev/null +++ b/guilib/common/IRServerSuite/IRServerSuite.h @@ -0,0 +1,64 @@ +#pragma once +/* + * Copyright (C) 2005-2008 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include <winsock2.h> +#include "StdString.h" +#include "IrssMessage.h" +#include "Thread.h" + +class CRemoteControl : CThread +{ +public: + CRemoteControl(); + ~CRemoteControl(); + void Initialize(); + void Disconnect(); + void Reset(); + void Update(); + WORD GetButton(); + bool IsHolding(); + bool IsInitialized() {return m_bInitialized;} + bool IsInUse() {return false;} + +protected: + virtual void Process(); + +private: + WORD m_button; + bool m_isHolding; + bool m_bInitialized; + SOCKET m_socket; + bool m_isConnecting; + CStdString m_deviceName; + CStdString m_keyCode; + + bool SendPacket(CIrssMessage& message); + bool ReadPacket(CIrssMessage& message); + int ReadN(char *buffer, int n); + bool WriteN(const char *buffer, int n); + bool Connect(); + void Close(); + + bool HandleRemoteEvent(CIrssMessage& message); +}; + +extern CRemoteControl g_RemoteControl; diff --git a/guilib/common/IRServerSuite/IrssMessage.cpp b/guilib/common/IRServerSuite/IrssMessage.cpp new file mode 100644 index 0000000000..7f8d40b174 --- /dev/null +++ b/guilib/common/IRServerSuite/IrssMessage.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "IrssMessage.h" + +CIrssMessage::CIrssMessage() +{ + m_type = IRSSMT_Unknown; + m_flags = 0; + m_data = NULL; + m_dataSize = 0; +} + +CIrssMessage::CIrssMessage(IRSS_MessageType type, uint32_t flags) +{ + m_type = type; + m_flags = flags; + m_data = NULL; + m_dataSize = 0; +} + +CIrssMessage::CIrssMessage(IRSS_MessageType type, uint32_t flags, char* data, int size) +{ + m_type = type; + m_flags = flags; + SetDataAsBytes(data, size); +} + +CIrssMessage::CIrssMessage(IRSS_MessageType type, uint32_t flags, const CStdString& data) +{ + m_type = type; + m_flags = flags; + SetDataAsString(data); +} + +CIrssMessage::~CIrssMessage() +{ + FreeData(); +} + +void CIrssMessage::SetDataAsBytes(char* data, int size) +{ + if (!data) + { + FreeData(); + } + else + { + m_data = (char*)malloc(size * sizeof(char)); + memcpy(m_data, data, size); + m_dataSize = size; + } +} + +void CIrssMessage::SetDataAsString(const CStdString& data) +{ + if (!data || data.IsEmpty()) + { + FreeData(); + } + else + { + m_data = strdup(data.c_str()); + m_dataSize = strlen(data.c_str()); + } +} + +void CIrssMessage::FreeData() +{ + free(m_data); + m_data = NULL; + m_dataSize = 0; +} + +char* CIrssMessage::ToBytes(int& size) +{ + int dataLength = 0; + if (m_data) + { + dataLength = m_dataSize; + } + + size = 8 + dataLength; + char* byteArray = new char[size]; + + memcpy(&byteArray[0], &m_type, 4); + memcpy(&byteArray[4], &m_flags, 4); + + if (m_data) + { + memcpy(&byteArray[8], &m_data, m_dataSize); + } + + return byteArray; +} + +bool CIrssMessage::FromBytes(char* from, int size, CIrssMessage& message) +{ + if (!from) + return false; + + if (size < 8) + return false; + + //IRSS_MessageType type = (MessageType)BitConverter.ToInt32(from, 0); + //IRSS_MessageFlags flags = (MessageFlags)BitConverter.ToInt32(from, 4); + uint32_t type; + memcpy(&type, from, 4); + uint32_t flags; + memcpy(&flags, from + 4, 4); + + message.SetType((IRSS_MessageType)type); + message.SetFlags(flags); + if (size > 8) + { + message.SetDataAsBytes(from + 8, size - 8); + } + return true; +} + +void CIrssMessage::SetType(IRSS_MessageType type) +{ + m_type = type; +} +void CIrssMessage::SetFlags(uint32_t flags) +{ + m_flags = flags; +} diff --git a/guilib/common/IRServerSuite/IrssMessage.h b/guilib/common/IRServerSuite/IrssMessage.h new file mode 100644 index 0000000000..d5ddefa0a7 --- /dev/null +++ b/guilib/common/IRServerSuite/IrssMessage.h @@ -0,0 +1,209 @@ +#pragma once +/* + * Copyright (C) 2005-2008 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "StdString.h" + + /// <summary> + /// Type of message. + /// </summary> + enum IRSS_MessageType + { + /// <summary> + /// Unknown message type. + /// </summary> + IRSSMT_Unknown = 0, + + /// <summary> + /// Register Client. + /// </summary> + IRSSMT_RegisterClient = 1, + /// <summary> + /// Unregister Client. + /// </summary> + IRSSMT_UnregisterClient = 2, + + /// <summary> + /// Register Repeater. + /// </summary> + IRSSMT_RegisterRepeater = 3, + /// <summary> + /// Unregister Repeater. + /// </summary> + IRSSMT_UnregisterRepeater = 4, + + /// <summary> + /// Learn IR Command. + /// </summary> + IRSSMT_LearnIR = 5, + /// <summary> + /// Blast IR Command. + /// </summary> + IRSSMT_BlastIR = 6, + + /// <summary> + /// Error. + /// </summary> + IRSSMT_Error = 7, + + /// <summary> + /// Server Shutdown. + /// </summary> + IRSSMT_ServerShutdown = 8, + /// <summary> + /// Server Suspend. + /// </summary> + IRSSMT_ServerSuspend = 9, + /// <summary> + /// Server Resume + /// </summary> + IRSSMT_ServerResume = 10, + + /// <summary> + /// Remote Event. + /// </summary> + IRSSMT_RemoteEvent = 11, + /// <summary> + /// Keyboard Event. + /// </summary> + IRSSMT_KeyboardEvent = 12, + /// <summary> + /// Mouse Event. + /// </summary> + IRSSMT_MouseEvent = 13, + + /// <summary> + /// Forward a Remote Event. + /// </summary> + IRSSMT_ForwardRemoteEvent = 14, + /// <summary> + /// Forward a Keyboard Event. + /// </summary> + IRSSMT_ForwardKeyboardEvent = 15, + /// <summary> + /// Forward a Mouse Event. + /// </summary> + IRSSMT_ForwardMouseEvent = 16, + + /// <summary> + /// Available Receivers. + /// </summary> + IRSSMT_AvailableReceivers = 17, + /// <summary> + /// Available Blasters. + /// </summary> + IRSSMT_AvailableBlasters = 18, + /// <summary> + /// Active Receivers. + /// </summary> + IRSSMT_ActiveReceivers = 19, + /// <summary> + /// Active Blasters. + /// </summary> + IRSSMT_ActiveBlasters = 20, + /// <summary> + /// Detected Receivers. + /// </summary> + IRSSMT_DetectedReceivers = 21, + /// <summary> + /// Detected Blasters. + /// </summary> + IRSSMT_DetectedBlasters = 22, + }; + + /// <summary> + /// Flags to determine more information about the message. + /// </summary> + enum IRSS_MessageFlags + { + /// <summary> + /// No Flags. + /// </summary> + IRSSMF_None = 0x0000, + + /// <summary> + /// Message is a Request. + /// </summary> + IRSSMF_Request = 0x0001, + /// <summary> + /// Message is a Response to a received Message. + /// </summary> + IRSSMF_Response = 0x0002, + /// <summary> + /// Message is a Notification. + /// </summary> + IRSSMF_Notify = 0x0004, + + /// <summary> + /// Operation Success. + /// </summary> + IRSSMF_Success = 0x0008, + /// <summary> + /// Operation Failure. + /// </summary> + IRSSMF_Failure = 0x0010, + /// <summary> + /// Operation Time-Out. + /// </summary> + IRSSMF_Timeout = 0x0020, + + //IRSSMF_Error = 0x0040, + + //IRSSMF_DataString = 0x0080, + //IRSSMF_DataBytes = 0x0100, + + //IRSSMF_ForceRespond = 0x0200, + + /// <summary> + /// Force the recipient not to respond. + /// </summary> + IRSSMF_ForceNotRespond = 0x0400, + }; + +class CIrssMessage +{ +public: + CIrssMessage(); + CIrssMessage(IRSS_MessageType type, uint32_t flags); + CIrssMessage(IRSS_MessageType type, uint32_t flags, char* data, int size); + CIrssMessage(IRSS_MessageType type, uint32_t flags, const CStdString& data); + ~CIrssMessage(); + + void SetDataAsBytes(char* data, int size); + void SetDataAsString(const CStdString& data); + char* ToBytes(int& size); + void SetType(IRSS_MessageType type); + void SetFlags(uint32_t flags); + IRSS_MessageType GetType() {return m_type;} + uint32_t GetFlags() {return m_flags;} + char* GetData() {return m_data;} + uint32_t GetDataSize() {return m_dataSize;} + static bool FromBytes(char* from, int size, CIrssMessage& message); + +private: + IRSS_MessageType m_type; + uint32_t m_flags; + + char* m_data; + int m_dataSize; + + void FreeData(); +}; diff --git a/guilib/common/LIRC.cpp b/guilib/common/LIRC.cpp new file mode 100644 index 0000000000..9b7bf2dcd2 --- /dev/null +++ b/guilib/common/LIRC.cpp @@ -0,0 +1,257 @@ +#include <sys/time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/inotify.h> +#include <limits.h> +#include <unistd.h> +#include "LIRC.h" +#include "ButtonTranslator.h" +#include "log.h" +#include "AdvancedSettings.h" +#include "FileSystem/File.h" + +#define LIRC_DEVICE "/dev/lircd" + +CRemoteControl g_RemoteControl; + +CRemoteControl::CRemoteControl() +{ + m_fd = -1; + m_file = NULL; + m_bInitialized = false; + m_skipHold = false; + m_button = 0; + m_isHolding = false; + m_used = true; + m_deviceName = LIRC_DEVICE; + m_inotify_fd = -1; + m_inotify_wd = -1; + m_bLogConnectFailure = true; + m_lastInitAttempt = -5000; + m_initRetryPeriod = 5000; + Reset(); +} + +CRemoteControl::~CRemoteControl() +{ + if (m_file != NULL) + fclose(m_file); +} + +void CRemoteControl::setUsed(bool value) +{ + m_used=value; + if (!value) + CLog::Log(LOGINFO, "LIRC %s: disabled", __FUNCTION__); +} + +void CRemoteControl::Reset() +{ + m_isHolding = false; + m_button = 0; +} + +void CRemoteControl::Disconnect() +{ + if (!m_used) + return; + + if (m_fd != -1) + { + m_bInitialized = false; + if (m_file != NULL) + fclose(m_file); + m_fd = -1; + m_file = NULL; + if (m_inotify_wd >= 0) { + inotify_rm_watch(m_inotify_fd, m_inotify_wd); + m_inotify_wd = -1; + } + if (m_inotify_fd >= 0) + close(m_inotify_fd); + } +} + +void CRemoteControl::setDeviceName(const CStdString& value) +{ + if (value.length()>0) + m_deviceName=value; + else + m_deviceName=LIRC_DEVICE; +} + +void CRemoteControl::Initialize() +{ + struct sockaddr_un addr; + int now = timeGetTime(); + + if (!m_used || now < m_lastInitAttempt + m_initRetryPeriod) + return; + m_lastInitAttempt = now; + + if (!XFILE::CFile::Exists(m_deviceName)) { + m_initRetryPeriod *= 2; + if (m_initRetryPeriod > 60000) + { + m_used = false; + CLog::Log(LOGDEBUG, "LIRC device %s does not exist. Giving up.", m_deviceName.c_str()); + } + else + CLog::Log(LOGDEBUG, "LIRC device %s does not exist. Retry in %ds.", m_deviceName.c_str(), m_initRetryPeriod/1000); + return; + } + + m_initRetryPeriod = 5000; + + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, m_deviceName.c_str()); + + // Open the socket from which we will receive the remote commands + if ((m_fd = socket(AF_UNIX, SOCK_STREAM, 0)) != -1) + { + // Connect to the socket + if (connect(m_fd, (struct sockaddr *)&addr, sizeof(addr)) != -1) + { + int opts; + m_bLogConnectFailure = true; + if ((opts = fcntl(m_fd,F_GETFL)) != -1) + { + // Set the socket to non-blocking + opts = (opts | O_NONBLOCK); + if (fcntl(m_fd,F_SETFL,opts) != -1) + { + if ((m_file = fdopen(m_fd, "r")) != NULL) + { + // Setup inotify so we can disconnect if lircd is restarted + if ((m_inotify_fd = inotify_init()) >= 0) + { + // Set the fd non-blocking + if ((opts = fcntl(m_inotify_fd, F_GETFL)) != -1) + { + opts |= O_NONBLOCK; + if (fcntl(m_inotify_fd, F_SETFL, opts) != -1) + { + // Set an inotify watch on the lirc device + if ((m_inotify_wd = inotify_add_watch(m_inotify_fd, m_deviceName.c_str(), IN_DELETE_SELF)) != -1) + { + m_bInitialized = true; + CLog::Log(LOGINFO, "LIRC %s: sucessfully started on: %s", __FUNCTION__, addr.sun_path); + } + else + CLog::Log(LOGDEBUG, "LIRC: Failed to initialize Inotify. LIRC device will not be monitored."); + } + } + } + } + else + CLog::Log(LOGERROR, "LIRC %s: fdopen failed: %s", __FUNCTION__, strerror(errno)); + } + else + CLog::Log(LOGERROR, "LIRC %s: fcntl(F_SETFL) failed: %s", __FUNCTION__, strerror(errno)); + } + else + CLog::Log(LOGERROR, "LIRC %s: fcntl(F_GETFL) failed: %s", __FUNCTION__, strerror(errno)); + } + else + { + if (m_bLogConnectFailure) + { + CLog::Log(LOGINFO, "LIRC %s: connect failed: %s", __FUNCTION__, strerror(errno)); + m_bLogConnectFailure = false; + } + } + } + else + CLog::Log(LOGINFO, "LIRC %s: socket failed: %s", __FUNCTION__, strerror(errno)); + if (!m_bInitialized) + Disconnect(); +} + +bool CRemoteControl::CheckDevice() { + if (m_inotify_fd < 0 || m_inotify_wd < 0) + return true; // inotify wasn't setup for some reason, assume all is well + int bufsize = sizeof(struct inotify_event) + PATH_MAX; + char buf[bufsize]; + int ret = read(m_inotify_fd, buf, bufsize); + for (int i = 0; i + (int)sizeof(struct inotify_event) <= ret;) { + struct inotify_event* e = (struct inotify_event*)(buf+i); + if (e->mask & IN_DELETE_SELF) { + CLog::Log(LOGDEBUG, "LIRC device removed, disconnecting..."); + Disconnect(); + return false; + } + i += sizeof(struct inotify_event)+e->len; + } + return true; +} + +void CRemoteControl::Update() +{ + if (!m_bInitialized || !m_used ) + return; + + if (!CheckDevice()) + return; + + Uint32 now = SDL_GetTicks(); + + // Read a line from the socket + while (fgets(m_buf, sizeof(m_buf), m_file) != NULL) + { + // Remove the \n + m_buf[strlen(m_buf)-1] = '\0'; + + // Parse the result. Sample line: + // 000000037ff07bdd 00 OK mceusb + char scanCode[128]; + char buttonName[128]; + char repeatStr[4]; + char deviceName[128]; + sscanf(m_buf, "%s %s %s %s", &scanCode[0], &repeatStr[0], &buttonName[0], &deviceName[0]); + + // Some template LIRC configuration have button names in apostrophes or quotes. + // If we got a quoted button name, strip 'em + unsigned int buttonNameLen = strlen(buttonName); + if ( buttonNameLen > 2 + && ( (buttonName[0] == '\'' && buttonName[buttonNameLen-1] == '\'') + || ((buttonName[0] == '"' && buttonName[buttonNameLen-1] == '"') ) ) ) + { + memmove( buttonName, buttonName + 1, buttonNameLen - 2 ); + buttonName[ buttonNameLen - 2 ] = '\0'; + } + + m_button = CButtonTranslator::GetInstance().TranslateLircRemoteString(deviceName, buttonName); + + if (strcmp(repeatStr, "00") == 0) + { + CLog::Log(LOGDEBUG, "LIRC: %s - NEW at %d:%s (%s)", __FUNCTION__, now, m_buf, buttonName); + m_firstClickTime = now; + m_isHolding = false; + m_skipHold = true; + return; + } + else if (now - m_firstClickTime >= (Uint32) g_advancedSettings.m_remoteRepeat && !m_skipHold) + { + m_isHolding = true; + } + else + { + m_isHolding = false; + m_button = 0; + } + } + if (feof(m_file) != 0) + Disconnect(); + m_skipHold = false; +} + +WORD CRemoteControl::GetButton() +{ + return m_button; +} + +bool CRemoteControl::IsHolding() +{ + return m_isHolding; +} diff --git a/guilib/common/LIRC.h b/guilib/common/LIRC.h new file mode 100644 index 0000000000..fc6882ab6d --- /dev/null +++ b/guilib/common/LIRC.h @@ -0,0 +1,44 @@ +#ifndef LIRC_H +#define LIRC_H + +#include "../system.h" +#include "StdString.h" + +class CRemoteControl +{ +public: + CRemoteControl(); + ~CRemoteControl(); + void Initialize(); + void Disconnect(); + void Reset(); + void Update(); + WORD GetButton(); + bool IsHolding(); + void setDeviceName(const CStdString& value); + void setUsed(bool value); + bool IsInUse() const { return m_used; } + bool IsInitialized() const { return m_bInitialized; } + +private: + int m_fd; + int m_inotify_fd; + int m_inotify_wd; + int m_lastInitAttempt; + int m_initRetryPeriod; + FILE* m_file; + bool m_isHolding; + WORD m_button; + char m_buf[128]; + bool m_bInitialized; + bool m_skipHold; + bool m_used; + bool m_bLogConnectFailure; + Uint32 m_firstClickTime; + CStdString m_deviceName; + bool CheckDevice(); +}; + +extern CRemoteControl g_RemoteControl; + +#endif diff --git a/guilib/common/Makefile.in b/guilib/common/Makefile.in new file mode 100644 index 0000000000..757c468768 --- /dev/null +++ b/guilib/common/Makefile.in @@ -0,0 +1,14 @@ +ARCH=@ARCH@ + +INCLUDES=-I. -I../ -I../../xbmc -I../../xbmc/linux -I../../xbmc/utils + +ifeq ($(findstring osx,$(ARCH)), osx) +SRCS=SDLJoystick.cpp +else +SRCS=SDLJoystick.cpp LIRC.cpp +endif + +LIB=gui_common.a + +include ../../Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) diff --git a/guilib/common/SDLJoystick.cpp b/guilib/common/SDLJoystick.cpp new file mode 100644 index 0000000000..9ec94f5040 --- /dev/null +++ b/guilib/common/SDLJoystick.cpp @@ -0,0 +1,388 @@ +#include "system.h" +#include "../Key.h" +#include "SDLJoystick.h" +#include "ButtonTranslator.h" +#include "utils/log.h" + +#include <math.h> + +#ifdef HAS_SDL_JOYSTICK + +using namespace std; + +CJoystick g_Joystick; // global + +CJoystick::CJoystick() +{ + Reset(); + m_NumAxes = 0; + m_AxisId = 0; + m_JoyId = 0; + m_ButtonId = 0; + m_HatId = 0; + m_HatState = SDL_HAT_CENTERED; + m_ActiveFlags = JACTIVE_NONE; + for (int i = 0 ; i<MAX_AXES ; i++) + m_Amount[i] = 0; + + SetSafeRange(2000); +} + +void CJoystick::Initialize(HWND hWnd) +{ + // clear old joystick names + m_JoystickNames.clear(); + + // any open ones? if so, close them. + if (m_Joysticks.size()>0) + { + for(size_t idJoy = 0; idJoy < m_Joysticks.size(); idJoy++) + { + // any joysticks unplugged? + if(SDL_JoystickOpened(idJoy)) + SDL_JoystickClose(m_Joysticks[idJoy]); + } + m_Joysticks.clear(); + m_JoyId = -1; + } + + // any joysticks connected? + if (SDL_NumJoysticks()>0) + { + // load joystick names and open all connected joysticks + for (int i = 0 ; i<SDL_NumJoysticks() ; i++) + { + SDL_Joystick *joy = SDL_JoystickOpen(i); + +#ifdef __APPLE__ + // On OS X, the 360 controllers are handled externally, since the SDL code is + // really buggy and doesn't handle disconnects. + // + if (std::string(SDL_JoystickName(i)).find("360") != std::string::npos) + { + CLog::Log(LOGNOTICE, "Ignoring joystick: %s", SDL_JoystickName(i)); + continue; + } +#endif + + m_Joysticks.push_back(joy); + if (joy) + { + CalibrateAxis(joy); + m_JoystickNames.push_back(string(SDL_JoystickName(i))); + CLog::Log(LOGNOTICE, "Enabled Joystick: %s", SDL_JoystickName(i)); + } + else + { + m_JoystickNames.push_back(string("")); + } + } + } + + // disable joystick events, since we'll be polling them + SDL_JoystickEventState(SDL_DISABLE); +} + +void CJoystick::CalibrateAxis(SDL_Joystick* joy) +{ + SDL_JoystickUpdate(); + + int numax = SDL_JoystickNumAxes(joy); + numax = (numax>MAX_AXES)?MAX_AXES:numax; + + // get default axis states + for (int a = 0 ; a<numax ; a++) + { + m_DefaultAmount[a+1] = SDL_JoystickGetAxis(joy, a); + CLog::Log(LOGDEBUG, "Calibrated Axis: %d , default amount %d\n", a, m_DefaultAmount[a+1]); + } +} + +void CJoystick::Reset(bool axis) +{ + if (axis) + { + SetAxisActive(false); + for (int i = 0 ; i<MAX_AXES ; i++) + { + ResetAxis(i); + } + } +} + +void CJoystick::Update() +{ + int buttonId = -1; + int axisId = -1; + int hatId = -1; + int numj = m_Joysticks.size(); + if (numj <= 0) + return; + + // update the state of all opened joysticks + SDL_JoystickUpdate(); + + // go through all joysticks + for (int j = 0; j<numj; j++) + { + SDL_Joystick *joy = m_Joysticks[j]; + int numb = SDL_JoystickNumButtons(joy); + int numhat = SDL_JoystickNumHats(joy); + int numax = SDL_JoystickNumAxes(joy); + numax = (numax>MAX_AXES)?MAX_AXES:numax; + int axisval; + Uint8 hatval; + + // get button states first, they take priority over axis + for (int b = 0 ; b<numb ; b++) + { + if (SDL_JoystickGetButton(joy, b)) + { + m_JoyId = j; + buttonId = b+1; + j = numj-1; + break; + } + } + + for (int h = 0; h < numhat; h++) + { + hatval = SDL_JoystickGetHat(joy, h); + if (hatval != SDL_HAT_CENTERED) + { + m_JoyId = j; + hatId = h + 1; + m_HatState = hatval; + j = numj-1; + break; + } + } + + // get axis states + m_NumAxes = numax; + for (int a = 0 ; a<numax ; a++) + { + axisval = SDL_JoystickGetAxis(joy, a); + axisId = a+1; + if (axisId<=0 || axisId>=MAX_AXES) + { + CLog::Log(LOGERROR, "Axis Id out of range. Maximum supported axis: %d", MAX_AXES); + } + else + { + m_Amount[axisId] = axisval; //[-32768 to 32767] + if (axisval!=m_DefaultAmount[axisId]) + { + m_JoyId = j; + } + } + } + m_AxisId = GetAxisWithMaxAmount(); + } + + if(hatId==-1) + { + if(m_HatId!=0) + CLog::Log(LOGDEBUG, "Joystick %d hat %u Centered", m_JoyId, hatId); + m_pressTicksHat = 0; + SetHatActive(false); + m_HatId = 0; + } + else + { + if(hatId!=m_HatId) + { + CLog::Log(LOGDEBUG, "Joystick %d hat %u Down", m_JoyId, hatId); + m_HatId = hatId; + m_pressTicksHat = SDL_GetTicks(); + } + SetHatActive(); + } + + if (buttonId==-1) + { + if (m_ButtonId!=0) + { + CLog::Log(LOGDEBUG, "Joystick %d button %d Up", m_JoyId, m_ButtonId); + } + m_pressTicksButton = 0; + SetButtonActive(false); + m_ButtonId = 0; + } + else + { + if (buttonId!=m_ButtonId) + { + CLog::Log(LOGDEBUG, "Joystick %d button %d Down", m_JoyId, buttonId); + m_ButtonId = buttonId; + m_pressTicksButton = SDL_GetTicks(); + } + SetButtonActive(); + } + +} + +void CJoystick::Update(SDL_Event& joyEvent) +{ + int buttonId = -1; + int axisId = -1; + int joyId = -1; + bool ignore = false; // not used for now + bool axis = false; + + printf("JoystickEvent %i\n", joyEvent.type); + + switch(joyEvent.type) + { + case SDL_JOYBUTTONDOWN: + m_JoyId = joyId = joyEvent.jbutton.which; + m_ButtonId = buttonId = joyEvent.jbutton.button + 1; + m_pressTicksButton = SDL_GetTicks(); + SetButtonActive(); + CLog::Log(LOGDEBUG, "Joystick %d button %d Down", joyId, buttonId); + break; + + case SDL_JOYAXISMOTION: + joyId = joyEvent.jaxis.which; + axisId = joyEvent.jaxis.axis + 1; + m_NumAxes = SDL_JoystickNumAxes(m_Joysticks[joyId]); + if (axisId<=0 || axisId>=MAX_AXES) + { + CLog::Log(LOGERROR, "Axis Id out of range. Maximum supported axis: %d", MAX_AXES); + ignore = true; + break; + } + axis = true; + m_JoyId = joyId; + if (joyEvent.jaxis.value==0) + { + ignore = true; + m_Amount[axisId] = 0; + } + else + { + m_Amount[axisId] = joyEvent.jaxis.value; //[-32768 to 32767] + } + m_AxisId = GetAxisWithMaxAmount(); + CLog::Log(LOGDEBUG, "Joystick %d Axis %d Amount %d", joyId, axisId, m_Amount[axisId]); + break; + + case SDL_JOYHATMOTION: + m_JoyId = joyId = joyEvent.jbutton.which; + m_HatId = joyEvent.jhat.hat + 1; + m_pressTicksHat = SDL_GetTicks(); + m_HatState = joyEvent.jhat.value; + SetHatActive(m_HatState != SDL_HAT_CENTERED); + CLog::Log(LOGDEBUG, "Joystick %d Hat %d Down with position %d", joyId, buttonId, m_HatState); + break; + + case SDL_JOYBALLMOTION: + ignore = true; + break; + + case SDL_JOYBUTTONUP: + m_pressTicksButton = 0; + SetButtonActive(false); + CLog::Log(LOGDEBUG, "Joystick %d button %d Up", joyEvent.jbutton.which, m_ButtonId); + + default: + ignore = true; + break; + } +} + +bool CJoystick::GetHat(int &id, int &position,bool consider_repeat) +{ + if (!IsHatActive()) + return false; + position = m_HatState; + id = m_HatId; + if (!consider_repeat) + return true; + + static Uint32 lastPressTicks = 0; + static Uint32 lastTicks = 0; + static Uint32 nowTicks = 0; + + if ((m_HatId>=0) && m_pressTicksHat) + { + // return the id if it's the first press + if (lastPressTicks!=m_pressTicksHat) + { + lastPressTicks = m_pressTicksHat; + return true; + } + nowTicks = SDL_GetTicks(); + if ((nowTicks-m_pressTicksHat)<500) // 500ms delay before we repeat + return false; + if ((nowTicks-lastTicks)<100) // 100ms delay before successive repeats + return false; + + lastTicks = nowTicks; + } + + return true; +} + +bool CJoystick::GetButton(int &id, bool consider_repeat) +{ + if (!IsButtonActive()) + return false; + if (!consider_repeat) + { + id = m_ButtonId; + return true; + } + + static Uint32 lastPressTicks = 0; + static Uint32 lastTicks = 0; + static Uint32 nowTicks = 0; + + if ((m_ButtonId>=0) && m_pressTicksButton) + { + // return the id if it's the first press + if (lastPressTicks!=m_pressTicksButton) + { + lastPressTicks = m_pressTicksButton; + id = m_ButtonId; + return true; + } + nowTicks = SDL_GetTicks(); + if ((nowTicks-m_pressTicksButton)<500) // 500ms delay before we repeat + { + return false; + } + if ((nowTicks-lastTicks)<100) // 100ms delay before successive repeats + { + return false; + } + lastTicks = nowTicks; + } + id = m_ButtonId; + return true; +} + +int CJoystick::GetAxisWithMaxAmount() +{ + static int maxAmount; + static int axis; + axis = 0; + maxAmount = m_SafeRange; + int tempf; + for (int i = 1 ; i<=m_NumAxes ; i++) + { + tempf = abs(m_DefaultAmount[i] - m_Amount[i]); + if (tempf>maxAmount) + { + maxAmount = tempf; + axis = i; + } + } + if (maxAmount==0) + SetAxisActive(false); + else + SetAxisActive(); + return axis; +} + +#endif diff --git a/guilib/common/SDLJoystick.h b/guilib/common/SDLJoystick.h new file mode 100644 index 0000000000..1aa89fecd4 --- /dev/null +++ b/guilib/common/SDLJoystick.h @@ -0,0 +1,78 @@ +#ifndef SDL_JOYSTICK_H +#define SDL_JOYSTICK_H + +#include "../system.h" +#include <vector> +#include <string> + +#ifdef HAS_SDL_JOYSTICK + +#include <SDL/SDL_joystick.h> +#include <SDL/SDL_events.h> + +#define MAX_AXES 64 + +#define JACTIVE_BUTTON 0x00000001 +#define JACTIVE_AXIS 0x00000002 +#define JACTIVE_HAT 0x00000004 +#define JACTIVE_NONE 0x00000000 + +// Class to manage all connected joysticks + +class CJoystick +{ +public: + CJoystick(); + + void Initialize(HWND hwnd); + void Reset(bool axis=false); + void CalibrateAxis(SDL_Joystick *joy); + void ResetAxis(int axisId) { m_Amount[axisId] = 0; } + void Update(); + void Update(SDL_Event& event); + float GetAmount(int axis) + { + if (m_Amount[axis]>0) + return (float)(m_Amount[axis]-m_SafeRange)/(32768.0f-(float)m_SafeRange); + return (float)(m_Amount[axis]+m_SafeRange)/(32768.0f-(float)m_SafeRange); + } + float GetAmount() + { + return GetAmount(m_AxisId); + } + bool GetButton (int& id, bool consider_repeat=true); + bool GetAxis (int &id) { if (!IsAxisActive()) return false; id=m_AxisId; return true; } + bool GetHat (int &id, int &position, bool consider_repeat=true); + std::string GetJoystick() { return (m_JoyId>-1)?m_JoystickNames[m_JoyId]:""; } + int GetAxisWithMaxAmount(); + void SetSafeRange(int val) { m_SafeRange=(val>32767)?32767:val; } + +private: + void SetAxisActive(bool active=true) { m_ActiveFlags = active?(m_ActiveFlags|JACTIVE_AXIS):(m_ActiveFlags&(~JACTIVE_AXIS)); } + void SetButtonActive(bool active=true) { m_ActiveFlags = active?(m_ActiveFlags|JACTIVE_BUTTON):(m_ActiveFlags&(~JACTIVE_BUTTON)); } + void SetHatActive(bool active=true) { m_ActiveFlags = active?(m_ActiveFlags|JACTIVE_HAT):(m_ActiveFlags&(~JACTIVE_HAT)); } + bool IsButtonActive() { return (m_ActiveFlags & JACTIVE_BUTTON) == JACTIVE_BUTTON; } + bool IsAxisActive() { return (m_ActiveFlags & JACTIVE_AXIS) == JACTIVE_AXIS; } + bool IsHatActive() { return (m_ActiveFlags & JACTIVE_HAT) == JACTIVE_HAT; } + + int m_Amount[MAX_AXES]; + int m_DefaultAmount[MAX_AXES]; + int m_AxisId; + int m_ButtonId; + Uint8 m_HatState; + int m_HatId; + int m_JoyId; + int m_NumAxes; + int m_SafeRange; // dead zone + Uint32 m_pressTicksButton; + Uint32 m_pressTicksHat; + Uint8 m_ActiveFlags; + std::vector<SDL_Joystick*> m_Joysticks; + std::vector<std::string> m_JoystickNames; +}; + +extern CJoystick g_Joystick; + +#endif + +#endif |