aboutsummaryrefslogtreecommitdiff
path: root/guilib/common
diff options
context:
space:
mode:
authorAlTheKiller <AlTheKiller@svn>2009-09-23 01:49:50 +0000
committerAlTheKiller <AlTheKiller@svn>2009-09-23 01:49:50 +0000
commit45285e8a9300cd754a760560640b75b09f98035e (patch)
treead9f093885ad5c98e9dd4156674e7691c22ed0a2 /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.cpp449
-rw-r--r--guilib/common/IRServerSuite/IRServerSuite.h64
-rw-r--r--guilib/common/IRServerSuite/IrssMessage.cpp146
-rw-r--r--guilib/common/IRServerSuite/IrssMessage.h209
-rw-r--r--guilib/common/LIRC.cpp257
-rw-r--r--guilib/common/LIRC.h44
-rw-r--r--guilib/common/Makefile.in14
-rw-r--r--guilib/common/SDLJoystick.cpp388
-rw-r--r--guilib/common/SDLJoystick.h78
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