1 /////////////////////////////////////////
2 //
3 //   OpenLieroX
4 //
5 //   event queue
6 //
7 //   based on the work of JasonB
8 //   enhanced by Dark Charlie and Albert Zeyer
9 //
10 //   code under LGPL
11 //
12 /////////////////////////////////////////
13 
14 
15 #include <list>
16 #include <cassert>
17 #include "ThreadPool.h"
18 #include <SDL_events.h>
19 #include <time.h>
20 
21 #include "LieroX.h"
22 #include "EventQueue.h"
23 #include "ReadWriteLock.h"
24 #include "Debug.h"
25 #include "InputEvents.h"
26 
27 static void InitQuitSignalHandler();
28 
29 EventQueue* mainQueue = NULL;
30 
31 struct EventQueueIntern {
32 	SDL_mutex* mutex;
33 	SDL_cond* cond;
34 	std::list<EventItem> queue;
EventQueueInternEventQueueIntern35 	EventQueueIntern() : mutex(NULL), cond(NULL) {}
initEventQueueIntern36 	void init() {
37 		mutex = SDL_CreateMutex();
38 		cond = SDL_CreateCond();
39 	}
uninitEventQueueIntern40 	void uninit() { // WARNING: don't call this if any other thread could be using this queue
41 		while(true) {
42 			// We swapped them because some of the code we are calling here at the cleanup could again access us
43 			// and that would either cause deadlocks or crashes, thus we still need a vaild eventqueue at this point.
44 			std::list<EventItem> tmpList;
45 			{
46 				ScopedLock lock(mutex);
47 				tmpList.swap(queue);
48 			}
49 			if(tmpList.size() > 0)
50 				warnings << "there are still " << tmpList.size() << " pending events in the event queue" << endl;
51 			else
52 				// finally new events anymore -> quit
53 				break;
54 
55 			for(std::list<EventItem>::iterator i = tmpList.begin(); i != tmpList.end(); ++i) {
56 				/* We execute all custom events because we want to ensure that
57 				 * each thrown event is also execute.
58 				 * We do some important cleanup and we stop other threads there which
59 				 * would otherwise never stop, for example the Timer system. */
60 				if(i->type == SDL_USEREVENT && i->user.code == UE_CustomEventHandler) {
61 					((Action*)i->user.data1)->handle();
62 					delete (Action*)i->user.data1;
63 				}
64 			}
65 		}
66 
67 		SDL_DestroyMutex(mutex);
68 		mutex = NULL;
69 
70 		SDL_DestroyCond(cond);
71 		cond = NULL;
72 	}
73 };
74 
EventQueue()75 EventQueue::EventQueue() {
76 	data = new EventQueueIntern();
77 	data->init();
78 }
79 
~EventQueue()80 EventQueue::~EventQueue() {
81 	assert(data != NULL);
82 	data->uninit();
83 	delete data; data = NULL;
84 }
85 
86 
InitEventQueue()87 void InitEventQueue() {
88 	if(!mainQueue) mainQueue = new EventQueue();
89 	InitQuitSignalHandler();
90 }
91 
ShutdownEventQueue()92 void ShutdownEventQueue() {
93 	if(mainQueue) {
94 		delete mainQueue;
95 		mainQueue = NULL;
96 	}
97 }
98 
poll(EventItem & event)99 bool EventQueue::poll(EventItem& event) {
100 	ScopedLock lock(data->mutex);
101 
102 	if( data->queue.empty() )
103 		return false;
104 
105 	event = data->queue.front();
106 	data->queue.pop_front();
107 
108 	return true;
109 }
110 
wait(EventItem & event)111 bool EventQueue::wait(EventItem& event) {
112 	ScopedLock lock(data->mutex);
113 
114 	while( data->queue.empty() ) {
115 		SDL_CondWait( data->cond, data->mutex );
116 	}
117 
118 	event = data->queue.front();
119 	data->queue.pop_front();
120 
121 	return true;
122 }
123 
push(const EventItem & event)124 bool EventQueue::push(const EventItem& event) {
125 	ScopedLock lock(data->mutex);
126 
127 	// TODO: some limit if queue too full? it could happen that OLX eats up all mem if there is no limit
128 	data->queue.push_back(event);
129 
130 	SDL_CondSignal(data->cond);
131 
132 	return true;
133 }
134 
CustomEvent(Action * act)135 static EventItem CustomEvent(Action* act) {
136 	// TODO: this is a bit hacky because we still use the SDL_Event structure
137 	SDL_Event ev;
138 	ev.type = SDL_USEREVENT;
139 	ev.user.code = UE_CustomEventHandler;
140 	ev.user.data1 = act;
141 	ev.user.data2 = NULL;
142 	return ev;
143 }
144 
push(Action * act)145 bool EventQueue::push(Action* act) {
146 	return push(CustomEvent(act));
147 }
148 
copyCustomEvents(const _Event * oldOwner,_Event * newOwner)149 void EventQueue::copyCustomEvents(const _Event* oldOwner, _Event* newOwner) {
150 	ScopedLock lock(data->mutex);
151 
152 	for(std::list<EventItem>::iterator i = data->queue.begin(); i != data->queue.end(); ++i) {
153 		if(i->type == SDL_USEREVENT && i->user.code == UE_CustomEventHandler) {
154 			CustomEventHandler* hndl = dynamic_cast<CustomEventHandler*>( (Action*)i->user.data1 );
155 			if(hndl && hndl->owner() == oldOwner) {
156 				data->queue.insert(i, CustomEvent(hndl->copy(newOwner)));
157 			}
158 		}
159 	}
160 }
161 
removeCustomEvents(const _Event * owner)162 void EventQueue::removeCustomEvents(const _Event* owner) {
163 	ScopedLock lock(data->mutex);
164 
165 	for(std::list<EventItem>::iterator i = data->queue.begin(); i != data->queue.end(); ) {
166 		std::list<EventItem>::iterator last = i; ++i;
167 		const SDL_Event& ev = *last;
168 		if(ev.type == SDL_USEREVENT && ev.user.code == UE_CustomEventHandler) {
169 			CustomEventHandler* hndl = dynamic_cast<CustomEventHandler*>( (Action*)ev.user.data1 );
170 			if(hndl && hndl->owner() == owner) {
171 				delete (Action*)ev.user.data1;
172 				data->queue.erase(last);
173 			}
174 		}
175 	}
176 }
177 
178 
179 
180 
181 #if defined(WIN32)
182 
183 #include <windows.h>
184 
185 
QuitSignalHandler(DWORD fdwCtrlType)186 static BOOL QuitSignalHandler( DWORD fdwCtrlType )
187 {
188 	EventItem ev;
189 	ev.type = SDL_QUIT;
190 	mainQueue->push(ev);
191 	tLX->bQuitCtrlC = true; // Set the special CTRL-C flag, so Dedicated Server won't try to close the non-existant pipe
192 	tLX->bQuitGame = true;
193 	return TRUE;
194 }
195 
196 
InitQuitSignalHandler()197 static void InitQuitSignalHandler()
198 {
199 	SetConsoleCtrlHandler( (PHANDLER_ROUTINE) QuitSignalHandler, TRUE );
200 	SetConsoleMode(stdin, ENABLE_PROCESSED_INPUT);
201 }
202 
203 #else // MacOSX, Linux, Unix
204 
205 #include <signal.h>
206 
QuitSignalHandler(int sig)207 static void QuitSignalHandler(int sig)
208 {
209 	if(mainQueue) {
210 		SDL_Event event;
211 		event.type = SDL_QUIT;
212 		mainQueue->push(event);
213 	} else {
214 		warnings << "got quit-signal and mainQueue is not set" << endl;
215 	}
216 	if(tLX) tLX->bQuitGame = true;
217 }
218 
InitQuitSignalHandler()219 static void InitQuitSignalHandler()
220 {
221 	signal(SIGINT, QuitSignalHandler);
222 	signal(SIGTERM, QuitSignalHandler);
223 }
224 
225 #endif
226 
227