changed: update version strings for beta4
[xbmc.git] / xbmc / utils / EventServer.cpp
blob6089b2c31ef7aebeb1d8782f772989c58944fba3
1 /*
2 * Copyright (C) 2005-2008 Team XBMC
3 * http://www.xbmc.org
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)
8 * any later version.
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, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
22 #include "system.h"
24 #ifdef HAS_EVENT_SERVER
26 #include "EventServer.h"
27 #include "EventPacket.h"
28 #include "EventClient.h"
29 #include "Socket.h"
30 #include "CriticalSection.h"
31 #include "Application.h"
32 #include "Builtins.h"
33 #include "ButtonTranslator.h"
34 #include "SingleLock.h"
35 #include "Zeroconf.h"
36 #include "GUIAudioManager.h"
37 #include <map>
38 #include <queue>
40 using namespace EVENTSERVER;
41 using namespace EVENTPACKET;
42 using namespace EVENTCLIENT;
43 using namespace SOCKETS;
44 using namespace std;
46 /************************************************************************/
47 /* CEventServer */
48 /************************************************************************/
49 CEventServer* CEventServer::m_pInstance = NULL;
50 CEventServer::CEventServer()
52 m_pSocket = NULL;
53 m_pPacketBuffer = NULL;
54 m_bStop = false;
55 m_bRunning = false;
56 m_bRefreshSettings = false;
58 // default timeout in ms for receiving a single packet
59 m_iListenTimeout = 1000;
62 void CEventServer::RemoveInstance()
64 if (m_pInstance)
66 delete m_pInstance;
67 m_pInstance=NULL;
71 CEventServer* CEventServer::GetInstance()
73 if (!m_pInstance)
75 m_pInstance = new CEventServer();
77 return m_pInstance;
80 void CEventServer::StartServer()
82 CSingleLock lock(m_critSection);
83 if(m_bRunning)
84 return;
86 // set default port
87 string port = (const char*)g_guiSettings.GetString("services.esport");
88 assert(port.length());
89 m_iPort = atoi(port.c_str());
90 assert(m_iPort <= 65535 && m_iPort >= 1);
92 // max clients
93 m_iMaxClients = g_guiSettings.GetInt("services.esmaxclients");
94 if (m_iMaxClients < 0)
96 CLog::Log(LOGERROR, "ES: Invalid maximum number of clients specified %d", m_iMaxClients);
97 m_iMaxClients = 20;
100 CThread::Create();
101 CThread::SetName("EventServer");
104 void CEventServer::StopServer(bool bWait)
106 CZeroconf::GetInstance()->RemoveService("services.eventserver");
107 StopThread(bWait);
110 void CEventServer::Cleanup()
112 if (m_pSocket)
114 m_pSocket->Close();
115 delete m_pSocket;
116 m_pSocket = NULL;
119 if (m_pPacketBuffer)
121 free(m_pPacketBuffer);
122 m_pPacketBuffer = NULL;
124 CSingleLock lock(m_critSection);
126 map<unsigned long, CEventClient*>::iterator iter = m_clients.begin();
127 while (iter != m_clients.end())
129 if (iter->second)
131 delete iter->second;
133 m_clients.erase(iter);
134 iter = m_clients.begin();
138 int CEventServer::GetNumberOfClients()
140 CSingleLock lock(m_critSection);
141 return m_clients.size();
144 void CEventServer::Process()
146 while(!m_bStop)
148 Run();
149 if (!m_bStop)
150 Sleep(1000);
154 void CEventServer::Run()
156 CAddress any_addr;
157 CSocketListener listener;
158 int packetSize = 0;
160 #ifndef _XBOX
161 if (!g_guiSettings.GetBool("services.esallinterfaces"))
162 any_addr.SetAddress ("127.0.0.1"); // only listen on localhost
163 #endif
165 CLog::Log(LOGNOTICE, "ES: Starting UDP Event server on %s:%d", any_addr.Address(), m_iPort);
167 Cleanup();
169 // create socket and initialize buffer
170 m_pSocket = CSocketFactory::CreateUDPSocket();
171 if (!m_pSocket)
173 CLog::Log(LOGERROR, "ES: Could not create socket, aborting!");
174 return;
176 m_pPacketBuffer = (unsigned char *)malloc(PACKET_SIZE);
178 if (!m_pPacketBuffer)
180 CLog::Log(LOGERROR, "ES: Out of memory, could not allocate packet buffer");
181 return;
184 // bind to IP and start listening on port
185 int port_range = g_guiSettings.GetInt("services.esportrange");
186 if (port_range < 1 || port_range > 100)
188 CLog::Log(LOGERROR, "ES: Invalid port range specified %d, defaulting to 10", port_range);
189 port_range = 10;
191 if (!m_pSocket->Bind(any_addr, m_iPort, port_range))
193 CLog::Log(LOGERROR, "ES: Could not listen on port %d", m_iPort);
194 return;
197 // publish service
198 CZeroconf::GetInstance()->PublishService("servers.eventserver",
199 "_xbmc-events._udp",
200 "XBMC Event Server",
201 m_iPort);
203 // add our socket to the 'select' listener
204 listener.AddSocket(m_pSocket);
206 m_bRunning = true;
208 while (!m_bStop)
212 // start listening until we timeout
213 if (listener.Listen(m_iListenTimeout))
215 CAddress addr;
216 if ((packetSize = m_pSocket->Read(addr, PACKET_SIZE, (void *)m_pPacketBuffer)) > -1)
218 ProcessPacket(addr, packetSize);
222 catch (...)
224 CLog::Log(LOGERROR, "ES: Exception caught while listening for socket");
225 break;
228 // process events and queue the necessary actions and button codes
229 ProcessEvents();
231 // refresh client list
232 RefreshClients();
234 // broadcast
235 // BroadcastBeacon();
238 CLog::Log(LOGNOTICE, "ES: UDP Event server stopped");
239 m_bRunning = false;
240 Cleanup();
243 void CEventServer::ProcessPacket(CAddress& addr, int pSize)
245 // check packet validity
246 CEventPacket* packet = new CEventPacket(pSize, m_pPacketBuffer);
247 if(packet == NULL)
249 CLog::Log(LOGERROR, "ES: Out of memory, cannot accept packet");
250 return;
253 unsigned int clientToken;
255 if (!packet->IsValid())
257 CLog::Log(LOGDEBUG, "ES: Received invalid packet");
258 delete packet;
259 return;
262 clientToken = packet->ClientToken();
263 if (!clientToken)
264 clientToken = addr.ULong(); // use IP if packet doesn't have a token
266 CSingleLock lock(m_critSection);
268 // first check if we have a client for this address
269 map<unsigned long, CEventClient*>::iterator iter = m_clients.find(clientToken);
271 if ( iter == m_clients.end() )
273 if ( m_clients.size() >= (unsigned int)m_iMaxClients)
275 CLog::Log(LOGWARNING, "ES: Cannot accept any more clients, maximum client count reached");
276 delete packet;
277 return;
280 // new client
281 CEventClient* client = new CEventClient ( addr );
282 if (client==NULL)
284 CLog::Log(LOGERROR, "ES: Out of memory, cannot accept new client connection");
285 delete packet;
286 return;
289 m_clients[clientToken] = client;
291 m_clients[clientToken]->AddPacket(packet);
294 void CEventServer::RefreshClients()
296 CSingleLock lock(m_critSection);
297 map<unsigned long, CEventClient*>::iterator iter = m_clients.begin();
299 while ( iter != m_clients.end() )
301 if (! (iter->second->Alive()))
303 CLog::Log(LOGNOTICE, "ES: Client %s from %s timed out", iter->second->Name().c_str(),
304 iter->second->Address().Address());
305 delete iter->second;
306 m_clients.erase(iter);
307 iter = m_clients.begin();
309 else
311 if (m_bRefreshSettings)
313 iter->second->RefreshSettings();
315 iter++;
318 m_bRefreshSettings = false;
321 void CEventServer::ProcessEvents()
323 CSingleLock lock(m_critSection);
324 map<unsigned long, CEventClient*>::iterator iter = m_clients.begin();
326 while (iter != m_clients.end())
328 iter->second->ProcessEvents();
329 iter++;
333 bool CEventServer::ExecuteNextAction()
335 EnterCriticalSection(&m_critSection);
337 CEventAction actionEvent;
338 map<unsigned long, CEventClient*>::iterator iter = m_clients.begin();
340 while (iter != m_clients.end())
342 if (iter->second->GetNextAction(actionEvent))
344 // Leave critical section before processing action
345 LeaveCriticalSection(&m_critSection);
346 switch(actionEvent.actionType)
348 case AT_EXEC_BUILTIN:
349 CBuiltins::Execute(actionEvent.actionName);
350 break;
352 case AT_BUTTON:
354 int actionID;
355 CButtonTranslator::TranslateActionString(actionEvent.actionName.c_str(), actionID);
356 CAction action(actionID, 1.0f, 0.0f, actionEvent.actionName);
357 g_audioManager.PlayActionSound(action);
358 g_application.OnAction(action);
360 break;
362 return true;
364 iter++;
366 LeaveCriticalSection(&m_critSection);
367 return false;
370 unsigned short CEventServer::GetButtonCode(std::string& strMapName, bool& isAxis, float& fAmount)
372 CSingleLock lock(m_critSection);
373 map<unsigned long, CEventClient*>::iterator iter = m_clients.begin();
374 unsigned short bcode = 0;
376 while (iter != m_clients.end())
378 bcode = iter->second->GetButtonCode(strMapName, isAxis, fAmount);
379 if (bcode)
380 return bcode;
381 iter++;
383 return bcode;
386 bool CEventServer::GetMousePos(float &x, float &y)
388 CSingleLock lock(m_critSection);
389 map<unsigned long, CEventClient*>::iterator iter = m_clients.begin();
391 while (iter != m_clients.end())
393 if (iter->second->GetMousePos(x, y))
394 return true;
395 iter++;
397 return false;
400 #endif // HAS_EVENT_SERVER