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