proper timeout handling for input subsys
[picodrive.git] / platform / uiq3 / Engine.cpp
1 /*******************************************************************\r
2  *\r
3  *      File:           Engine.cpp\r
4  *\r
5  *      Author:         Peter van Sebille (peter@yipton.net)\r
6  *\r
7  *  Modified/adapted for picodriveN by notaz, 2006\r
8  *\r
9  *  (c) Copyright 2006, notaz\r
10  *      (c) Copyright 2002, Peter van Sebille\r
11  *      All Rights Reserved\r
12  *\r
13  *******************************************************************/\r
14 \r
15 \r
16 #include "Engine.h"\r
17 #include <w32std.h>\r
18 #include <eikenv.h>\r
19 #include <e32svr.h>\r
20 #include <e32math.h>\r
21 #include <e32uid.h>\r
22 \r
23 #include <string.h>\r
24 \r
25 #include "version.h"\r
26 #include <pico/pico_int.h>\r
27 #include "../common/emu.h"\r
28 #include "engine/debug.h"\r
29 #include "App.h"\r
30 \r
31 // this is where we start to break a bunch of symbian rules\r
32 extern TInt machineUid;\r
33 extern int gamestate, gamestate_next;\r
34 extern char *loadrom_fname;\r
35 extern int   loadrom_result;\r
36 extern const char *actionNames[];\r
37 RSemaphore initSemaphore;\r
38 RSemaphore pauseSemaphore;\r
39 RSemaphore loadWaitSemaphore;\r
40 static CPicolAppView *appView = 0;\r
41 \r
42 \r
43 TInt CPicoGameSession::Do(const TPicoServRqst what, TAny *param)\r
44 {\r
45         switch (what)\r
46         {\r
47                 case PicoMsgLoadState: \r
48                         if(!rom_loaded) return -1; // no ROM\r
49                         return emu_SaveLoadGame(1, 0);\r
50 \r
51                 case PicoMsgSaveState:\r
52                         if(!rom_loaded) return -1;\r
53                         return emu_SaveLoadGame(0, 0);\r
54 \r
55                 case PicoMsgLoadROM:\r
56                         return loadROM((TPtrC16 *)param);\r
57                 \r
58                 case PicoMsgResume:\r
59                         DEBUGPRINT(_L("resume"));\r
60                         if(rom_loaded) {\r
61                                 return ChangeRunState(PGS_Running);\r
62                         }\r
63                         return 1;\r
64 \r
65                 case PicoMsgReset: \r
66                         if(rom_loaded) {\r
67                                 return ChangeRunState(PGS_Reset);\r
68                         }\r
69                         return 1;\r
70 \r
71                 case PicoMsgKeys:\r
72                         return ChangeRunState(PGS_KeyConfig);\r
73 \r
74                 case PicoMsgPause:\r
75                         return ChangeRunState(PGS_Paused);\r
76 \r
77                 case PicoMsgQuit:\r
78                         DEBUGPRINT(_L("got quit msg."));\r
79                         return ChangeRunState(PGS_Quit);\r
80 \r
81                 // config change\r
82                 case PicoMsgConfigChange:\r
83                         return changeConfig((TPicoConfig *)param);\r
84 \r
85                 case PicoMsgSetAppView:\r
86                         appView = (CPicolAppView *)param;\r
87                         return 1;\r
88 \r
89                 default:\r
90                         return 1;\r
91         }\r
92 }\r
93 \r
94 TInt EmuThreadFunction(TAny* anArg);\r
95 \r
96 TInt CPicoGameSession::StartEmuThread()\r
97 {\r
98         TInt res=KErrNone;\r
99         iEmuRunning = EFalse;\r
100 \r
101         if (initSemaphore.Handle() > 0)\r
102                 initSemaphore.Close();\r
103         initSemaphore.CreateLocal(0);\r
104         if (pauseSemaphore.Handle() <= 0)\r
105                 pauseSemaphore.CreateLocal(0);\r
106         if (loadWaitSemaphore.Handle() <= 0)\r
107                 loadWaitSemaphore.CreateLocal(0);\r
108 \r
109         RThread thread;\r
110         if(iThreadWatcher && (res = thread.Open(iThreadWatcher->iTid)) == KErrNone) {\r
111                 // should be a dead thread in some strange state.\r
112                 DEBUGPRINT(_L("found thread with the same id (id=%i, RequestCount=%i), killing.."),\r
113                                 (TInt32)thread.Id(), thread.RequestCount());\r
114                 // what can we do in this situation? Nothing seems to help, it just stays in this state.\r
115                 delete iThreadWatcher;\r
116                 iThreadWatcher = 0;\r
117                 thread.Kill(1);\r
118                 thread.Terminate(1);\r
119                 thread.Close();\r
120         }\r
121 \r
122         res=thread.Create(_L("PicoEmuThread"),   // create new server thread\r
123                 EmuThreadFunction, // thread's main function\r
124                 KDefaultStackSize,\r
125                 KMinHeapSize,\r
126                 KPicoMaxHeapSize,\r
127                 0 // &semaphore // passed as TAny* argument to thread function\r
128                 );\r
129 \r
130         if(res == KErrNone) { // thread created ok - now start it going\r
131                 thread.SetPriority(EPriorityMore);\r
132                 iEmuRunning = ETrue;\r
133                 if (iThreadWatcher) delete iThreadWatcher;\r
134                 iThreadWatcher = CThreadWatcher::NewL(thread.Id());\r
135                 thread.Resume(); // start it going\r
136                 DEBUGPRINT(_L("initSemaphore.Wait()"));\r
137                 res = initSemaphore.Wait(3*1000*1000); // wait until it's initialized\r
138                 DEBUGPRINT(_L("initSemaphore resume, ExitReason() == %i"), thread.ExitReason());\r
139                 res |= thread.ExitReason();\r
140                 thread.Close(); // we're no longer interested in the other thread\r
141                 if(res != KErrNone) iEmuRunning = EFalse;\r
142                 return res;\r
143         }\r
144 \r
145         return res;\r
146 }\r
147 \r
148 TInt CPicoGameSession::ChangeRunState(TPicoGameState newstate, TPicoGameState newstate_next)\r
149 {\r
150         if (!iEmuRunning) {\r
151                 gamestate = PGS_Paused;\r
152                 TInt res = StartEmuThread();\r
153                 if(res != KErrNone) DEBUGPRINT(_L("StartEmuThread() returned %i"), res);\r
154                 if (!iEmuRunning) return PicoErrEmuThread;\r
155         }\r
156 \r
157         int oldstate = gamestate;\r
158         gamestate = newstate;\r
159         gamestate_next = newstate_next ? newstate_next : PGS_Paused;\r
160         if (oldstate == PGS_Paused) pauseSemaphore.Signal();\r
161         return 0;\r
162 }\r
163 \r
164 \r
165 TInt CPicoGameSession::loadROM(TPtrC16 *pptr)\r
166 {\r
167         TInt ret;\r
168         char buff[150];\r
169 \r
170         // make sure emu thread is ok\r
171         ret = ChangeRunState(PGS_Paused);\r
172         if(ret) return ret;\r
173 \r
174         // read the contents of the client pointer into a TPtr.\r
175         static TBuf8<KMaxFileName> writeBuf;\r
176         writeBuf.Copy(*pptr);\r
177 \r
178         // push the emu thead to a load state. This is done so that it owns all file handles.\r
179         // If successful, in will enter PGS_Running state by itself.\r
180         loadrom_fname = (char *)writeBuf.PtrZ();\r
181         loadrom_result = 0;\r
182         loadWaitSemaphore.Wait(1); // make sure sem is not set\r
183         ret = ChangeRunState(PGS_ReloadRom);\r
184         if(ret) return ret;\r
185 \r
186         loadWaitSemaphore.Wait(60*1000*1000);\r
187 \r
188         if (loadrom_result == 0)\r
189                 return PicoErrRomOpenFailed;\r
190 \r
191         emu_getGameName(buff);\r
192         TPtrC8 buff8((TUint8*) buff);\r
193         iRomInternalName.Copy(buff8);\r
194 \r
195         DEBUGPRINT(_L("done waiting for ROM load"));\r
196 \r
197         // debug\r
198         #ifdef __DEBUG_PRINT\r
199         TInt mem, cells = User::CountAllocCells();\r
200         User::AllocSize(mem);\r
201         DEBUGPRINT(_L("comm:   cels=%d, size=%d KB"), cells, mem/1024);\r
202         #endif\r
203 \r
204         return 0;\r
205 }\r
206 \r
207 \r
208 TInt CPicoGameSession::changeConfig(TPicoConfig *aConfig)\r
209 {\r
210         // 6 button pad, enable XYZM config if needed\r
211         if (PicoOpt & POPT_6BTN_PAD)\r
212         {\r
213                 actionNames[8]  = "Z";\r
214                 actionNames[9]  = "Y";\r
215                 actionNames[10] = "X";\r
216                 actionNames[11] = "MODE";\r
217         } else {\r
218                 actionNames[8] = actionNames[9] = actionNames[10] = actionNames[11] = 0;\r
219         }\r
220 \r
221         // if we are in center 90||270 modes, we can bind renderer switcher\r
222         if (currentConfig.scaling == TPicoConfig::PMFit &&\r
223                 (currentConfig.rotation == TPicoConfig::PRot0 || currentConfig.rotation == TPicoConfig::PRot180))\r
224                          actionNames[25] = 0;\r
225         else actionNames[25] = "RENDERER";\r
226 \r
227         return 0;\r
228 }\r
229 \r
230 \r
231 #ifdef __DEBUG_PRINT_FILE\r
232 extern RMutex logMutex;\r
233 #endif\r
234 \r
235 void CPicoGameSession::freeResources()\r
236 {\r
237         RThread thread;\r
238         TInt i;\r
239 \r
240         DEBUGPRINT(_L("CPicoGameSession::freeResources()"));\r
241 \r
242         if(iThreadWatcher && thread.Open(iThreadWatcher->iTid) == KErrNone)\r
243         {\r
244                 // try to stop our emu thread\r
245                 gamestate = PGS_Quit;\r
246                 if(pauseSemaphore.Handle() > 0)\r
247                         pauseSemaphore.Signal();\r
248 \r
249                 if(thread.Handle() > 0)\r
250                 {\r
251                         // tried reopening thread handle here over time intervals to detect if thread is alive,\r
252                         // but would run into handle panics.\r
253 \r
254                         for(i = 0; i < 8; i++) {\r
255                                 User::After(100 * 1000);\r
256                                 if(thread.ExitReason() != 0) break;\r
257                         }\r
258 \r
259                         if(thread.ExitReason() == 0) {\r
260                                 // too late, time to die\r
261                                 DEBUGPRINT(_L("thread %i not responding, killing.."), (TInt32) thread.Id());\r
262                                 thread.Terminate(1);\r
263                         }\r
264                         thread.Close();\r
265                 }\r
266 \r
267         }\r
268 \r
269         if (iThreadWatcher != NULL)\r
270         {\r
271                 DEBUGPRINT(_L("delete iThreadWatcher"));\r
272                 delete iThreadWatcher;\r
273                 DEBUGPRINT(_L("after delete iThreadWatcher"));\r
274                 iThreadWatcher = NULL;\r
275         }\r
276 \r
277         if (initSemaphore.Handle() > 0)\r
278                 initSemaphore.Close();\r
279         if (pauseSemaphore.Handle() > 0)\r
280                 pauseSemaphore.Close();\r
281         if (loadWaitSemaphore.Handle() > 0)\r
282                 loadWaitSemaphore.Close();\r
283         DEBUGPRINT(_L("freeResources() returning"));\r
284 #ifdef __DEBUG_PRINT_FILE\r
285         if (logMutex.Handle() > 0)\r
286                 logMutex.Close();\r
287 #endif\r
288 }\r
289 \r
290 TBool CPicoGameSession::iEmuRunning = EFalse;\r
291 CThreadWatcher *CPicoGameSession::iThreadWatcher = 0;\r
292 TBuf<150> CPicoGameSession::iRomInternalName;\r
293 \r
294 \r
295 // CThreadWatcher\r
296 CThreadWatcher::~CThreadWatcher()\r
297 {\r
298         Cancel();\r
299         DEBUGPRINT(_L("after CThreadWatcher::Cancel();"));\r
300 }\r
301 \r
302 CThreadWatcher::CThreadWatcher(const TThreadId& aTid)\r
303 : CActive(CActive::EPriorityStandard), iTid(aTid)\r
304 {\r
305 }\r
306 \r
307 \r
308 CThreadWatcher* CThreadWatcher::NewL(const TThreadId& aTid)\r
309 {\r
310         CThreadWatcher* self = new(ELeave) CThreadWatcher(aTid);\r
311         CleanupStack::PushL(self);\r
312         self->ConstructL();\r
313         CleanupStack::Pop();                    // self\r
314         return self;\r
315 }\r
316 \r
317 void CThreadWatcher::ConstructL()\r
318 {\r
319         CActiveScheduler::Add(this);\r
320         RThread thread;\r
321         if(thread.Open(iTid) == KErrNone) {\r
322                 thread.Logon(iStatus);\r
323                 thread.Close();\r
324                 SetActive();\r
325         }\r
326 }\r
327 \r
328 void CThreadWatcher::RunL()\r
329 {\r
330         DEBUGPRINT(_L("CThreadWatcher::RunL()"));\r
331         CPicoGameSession::iEmuRunning = EFalse;\r
332         if(appView) appView->UpdateCommandList();\r
333         //initSemaphore.Signal(); // no point to do that here, AS can't get here if it is waiting\r
334 }\r
335 \r
336 void CThreadWatcher::DoCancel()\r
337 {\r
338         RThread thread;\r
339         DEBUGPRINT(_L("CThreadWatcher::DoCancel()"));\r
340         if(thread.Open(iTid) == KErrNone) {\r
341                 DEBUGPRINT(_L("thread.LogonCancel(iStatus);"));\r
342                 thread.LogonCancel(iStatus);\r
343                 thread.Close();\r
344         }\r
345 }\r
346 \r
347 extern "C" void cache_flush_d_inval_i(const void *start_addr, const void *end_addr)\r
348 {\r
349         // TODO\r
350         User::IMB_Range((TAny *)start_addr, (TAny *)end_addr);\r
351 }\r
352 \r