53454c95597db452ec3a19e1eee1221a7b71e6a0
[picodrive.git] / platform / uiq3 / engine / main.cpp
1 // mainloop with window server event handling\r
2 // event polling mechnism was taken from\r
3 // Peter van Sebille's projects\r
4 \r
5 // (c) Copyright 2006, notaz\r
6 // All Rights Reserved\r
7 \r
8 #include <e32base.h>\r
9 #include <hal.h>\r
10 #include <e32keys.h>\r
11 #include <w32std.h>\r
12 \r
13 #include <stdio.h>\r
14 #include <stdlib.h>\r
15 #include <sys/time.h>\r
16 \r
17 #include "debug.h"\r
18 #include "../Engine.h"\r
19 \r
20 #include <Pico/PicoInt.h>\r
21 #include "../../common/emu.h"\r
22 #include "../emu.h"\r
23 #include "vid.h"\r
24 #include "PolledAS.h"\r
25 //#include "audio.h"\r
26 #include "audio_mediaserver.h"\r
27 \r
28 //#include <ezlib.h>\r
29 #include <zlib/zlib.h>\r
30 \r
31 \r
32 //#define BENCHMARK\r
33 \r
34 \r
35 // scancodes we care about\r
36 enum TUsedScanCodes {\r
37         EStdKeyM600JogUp   = EStdKeyDevice1,\r
38         EStdKeyM600JogDown = EStdKeyDevice2,\r
39 };\r
40 \r
41 static unsigned char keyFlags[256];   // lsb->msb: key_down, pulse_only, ?, ?,  ?, ?, not_configurable, disabled\r
42 static unsigned char pressedKeys[11]; // List of pressed key scancodes, up to 10\r
43 \r
44 // list of areas\r
45 TPicoAreaConfigEntry areaConfig[] = {\r
46         { TRect(  0,   0,   0,   0) },\r
47         // small corner bottons\r
48         { TRect(  0,   0,  15,  15) },\r
49         { TRect(224,   0, 239,  15) },\r
50         { TRect(  0, 304,  15, 319) },\r
51         { TRect(224, 304, 239, 319) },\r
52         // normal buttons\r
53         { TRect(  0,   0,  79,  63) },\r
54         { TRect( 80,   0, 159,  63) },\r
55         { TRect(160,   0, 239,  63) },\r
56         { TRect(  0,  64,  79, 127) },\r
57         { TRect( 80,  64, 159, 127) },\r
58         { TRect(160,  64, 239, 127) },\r
59         { TRect(  0, 128,  79, 191) },\r
60         { TRect( 80, 128, 159, 191) },\r
61         { TRect(160, 128, 239, 191) },\r
62         { TRect(  0, 192,  79, 255) },\r
63         { TRect( 80, 192, 159, 255) },\r
64         { TRect(160, 192, 239, 255) },\r
65         { TRect(  0, 256,  79, 319) },\r
66         { TRect( 80, 256, 159, 319) },\r
67         { TRect(160, 256, 239, 319) },\r
68         { TRect(  0,   0,   0,   0) }\r
69 };\r
70 \r
71 // PicoPad[] format: SACB RLDU\r
72 const char *actionNames[] = {\r
73         "UP", "DOWN", "LEFT", "RIGHT", "B", "C", "A", "START",\r
74         0, 0, 0, 0, 0, 0, 0, 0, // Z, Y, X, MODE (enabled only when needed), ?, ?, ?, ?\r
75         0, 0, 0, 0, "VOLUME@UP", "VOLUME@DOWN", "NEXT@SAVE@SLOT", "PREV@SAVE@SLOT", // ?, ?, ?, ?, vol_up, vol_down, next_slot, prev_slot\r
76         0, 0, "PAUSE@EMU", "SAVE@STATE", "LOAD@STATE", 0, 0, "DONE" // ?, switch_renderer, [...], "FRAMESKIP@8", "AUTO@FRAMESKIP"\r
77 };\r
78 \r
79 \r
80 // globals are allowed, so why not to (ab)use them?\r
81 //TInt machineUid = 0;\r
82 int gamestate = PGS_Paused, gamestate_next = PGS_Paused;\r
83 char *loadrom_fname = NULL;\r
84 int   loadrom_result = 0;\r
85 static timeval noticeMsgTime = { 0, 0 };        // when started showing\r
86 static CGameAudioMS *gameAudio = 0;                     // the audio object itself\r
87 static int reset_timing = 0;\r
88 static int pico_was_reset = 0;\r
89 extern RSemaphore initSemaphore;\r
90 extern RSemaphore pauseSemaphore;\r
91 extern RSemaphore loadWaitSemaphore;\r
92 \r
93 // some forward declarations\r
94 static void MainInit();\r
95 static void MainExit();\r
96 static void DumpMemInfo();\r
97 \r
98 \r
99 class TPicoDirectScreenAccess : public MDirectScreenAccess\r
100 {\r
101 public: // implements MDirectScreenAccess\r
102         void Restart(RDirectScreenAccess::TTerminationReasons aReason);\r
103 public: // implements MAbortDirectScreenAccess\r
104         void AbortNow(RDirectScreenAccess::TTerminationReasons aReason);\r
105 };\r
106 \r
107 \r
108 // just for a nicer grouping of WS related stuff\r
109 class CGameWindow\r
110 {\r
111 public:\r
112         static void ConstructResourcesL(void);\r
113         static void FreeResources(void);\r
114         static void DoKeys(void);\r
115         static void DoKeysConfig(TUint &which);\r
116         static void RunEvents(TUint32 which);\r
117 \r
118         static RWsSession*                              iWsSession;\r
119         static RWindowGroup                             iWsWindowGroup;\r
120         static RWindow                                  iWsWindow;\r
121         static CWsScreenDevice*                 iWsScreen;\r
122         static CWindowGc*                               iWindowGc;\r
123         static TRequestStatus                   iWsEventStatus;\r
124 //      static TThreadId                                iLauncherThreadId;\r
125 //      static RDirectScreenAccess*             iDSA;\r
126 //      static TRequestStatus                   iDSAstatus;\r
127         static TPicoDirectScreenAccess  iPDSA;\r
128         static CDirectScreenAccess*             iDSA;\r
129 };\r
130 \r
131 \r
132 static void updateSound(int len)\r
133 {\r
134         PsndOut = gameAudio->NextFrameL(len);\r
135         if(!PsndOut) { // sound output problems?\r
136                 strcpy(noticeMsg, "SOUND@OUTPUT@ERROR;@SOUND@DISABLED");\r
137                 gettimeofday(&noticeMsgTime, 0);\r
138         }\r
139 }\r
140 \r
141 \r
142 static void SkipFrame(void)\r
143 {\r
144         PicoSkipFrame=1;\r
145         PicoFrame();\r
146         PicoSkipFrame=0;\r
147 }\r
148 \r
149 \r
150 static void simpleWait(int thissec, int lim_time)\r
151 {\r
152         struct timeval tval;\r
153         int sleep = 0;\r
154 \r
155         gettimeofday(&tval, 0);\r
156         if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
157 \r
158         sleep = lim_time - tval.tv_usec - 2000;\r
159         if (sleep > 0) {\r
160 //              User::After((sleep = lim_time - tval.tv_usec));\r
161                 User::AfterHighRes(sleep);\r
162         }\r
163 }\r
164 \r
165 \r
166 static void TargetEpocGameL()\r
167 {\r
168         char buff[24]; // fps count c string\r
169         struct timeval tval; // timing\r
170         int thissec = 0, frames_done = 0, frames_shown = 0;\r
171         int target_fps, target_frametime;\r
172         int i, lim_time;\r
173 \r
174         MainInit();\r
175         buff[0] = 0;\r
176 \r
177         // try to start pico\r
178         DEBUGPRINT(_L("PicoInit()"));\r
179         PicoInit();\r
180         PicoDrawSetColorFormat(2);\r
181         PicoWriteSound = updateSound;\r
182 \r
183         // loop?\r
184         for(;;)\r
185         {\r
186                 if (gamestate == PGS_Running)\r
187                 {\r
188                         #ifdef __DEBUG_PRINT\r
189                         TInt mem, cells = User::CountAllocCells();\r
190                         User::AllocSize(mem);\r
191                         DEBUGPRINT(_L("worker: cels=%d, size=%d KB"), cells, mem/1024);\r
192                         #endif\r
193 \r
194                         // switch context to other thread\r
195                         User::After(50000);\r
196                         // prepare window and stuff\r
197                         CGameWindow::ConstructResourcesL();\r
198 \r
199                         // if the system has something to do, it should better do it now\r
200                         User::After(50000);\r
201                         //CPolledActiveScheduler::Instance()->Schedule();\r
202 \r
203                         // pal/ntsc might have changed, reset related stuff\r
204                         if(Pico.m.pal) {\r
205                                 target_fps = 50;\r
206                                 if(!noticeMsgTime.tv_sec) strcpy(noticeMsg, "PAL@SYSTEM@/@50@FPS");\r
207                         } else {\r
208                                 target_fps = 60;\r
209                                 if(!noticeMsgTime.tv_sec) strcpy(noticeMsg, "NTSC@SYSTEM@/@60@FPS");\r
210                         }\r
211                         target_frametime = 1000000/target_fps;\r
212                         if (!noticeMsgTime.tv_sec && pico_was_reset)\r
213                                 gettimeofday(&noticeMsgTime, 0);\r
214 \r
215                         // prepare CD buffer\r
216                         if (PicoAHW & PAHW_MCD) PicoCDBufferInit();\r
217 \r
218                         pico_was_reset = 0;\r
219                         reset_timing = 1;\r
220 \r
221                         while (gamestate == PGS_Running)\r
222                         {\r
223                                 gettimeofday(&tval, 0);\r
224                                 if(reset_timing) {\r
225                                         reset_timing = 0;\r
226                                         thissec = tval.tv_sec;\r
227                                         frames_done = tval.tv_usec/target_frametime;\r
228                                 }\r
229 \r
230                                 // show notice message?\r
231                                 char *notice = 0;\r
232                                 if(noticeMsgTime.tv_sec) {\r
233                                         if((tval.tv_sec*1000000+tval.tv_usec) - (noticeMsgTime.tv_sec*1000000+noticeMsgTime.tv_usec) > 2000000) // > 2.0 sec\r
234                                                  noticeMsgTime.tv_sec = noticeMsgTime.tv_usec = 0;\r
235                                         else notice = noticeMsg;\r
236                                 }\r
237 \r
238                                 // second changed?\r
239                                 if (thissec != tval.tv_sec)\r
240                                 {\r
241 #ifdef BENCHMARK\r
242                                         static int bench = 0, bench_fps = 0, bench_fps_s = 0, bfp = 0, bf[4];\r
243                                         if(++bench == 10) {\r
244                                                 bench = 0;\r
245                                                 bench_fps_s = bench_fps;\r
246                                                 bf[bfp++ & 3] = bench_fps;\r
247                                                 bench_fps = 0;\r
248                                         }\r
249                                         bench_fps += frames_shown;\r
250                                         sprintf(buff, "%02i/%02i/%02i", frames_shown, bench_fps_s, (bf[0]+bf[1]+bf[2]+bf[3])>>2);\r
251 #else\r
252                                         if (currentConfig.EmuOpt & EOPT_SHOW_FPS) \r
253                                                 sprintf(buff, "%02i/%02i", frames_shown, frames_done);\r
254 #endif\r
255 \r
256 \r
257                                         thissec = tval.tv_sec;\r
258 \r
259                                         if(PsndOut == 0 && currentConfig.Frameskip >= 0) {\r
260                                                 frames_done = frames_shown = 0;\r
261                                         } else {\r
262                                                 // it is quite common for this implementation to leave 1 fame unfinished\r
263                                                 // when second changes, but we don't want buffer to starve.\r
264                                                 if(PsndOut && frames_done < target_fps && frames_done > target_fps-5) {\r
265                                                         SkipFrame(); frames_done++;\r
266                                                 }\r
267 \r
268                                                 frames_done  -= target_fps; if (frames_done  < 0) frames_done  = 0;\r
269                                                 frames_shown -= target_fps; if (frames_shown < 0) frames_shown = 0;\r
270                                                 if (frames_shown > frames_done) frames_shown = frames_done;\r
271                                         }\r
272                                         User::ResetInactivityTime();\r
273                                 }\r
274 \r
275 \r
276                                 lim_time = (frames_done+1) * target_frametime;\r
277                                 if (currentConfig.Frameskip >= 0) // frameskip enabled\r
278                                 {\r
279                                         for (i = 0; i < currentConfig.Frameskip && gamestate == PGS_Running; i++)\r
280                                         {\r
281                                                 CGameWindow::DoKeys();\r
282                                                 SkipFrame(); frames_done++;\r
283                                                 if (PsndOut) { // do framelimitting if sound is enabled\r
284                                                         gettimeofday(&tval, 0);\r
285                                                         if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
286                                                         if(tval.tv_usec < lim_time) { // we are too fast\r
287                                                                 simpleWait(thissec, lim_time);\r
288                                                         }\r
289                                                 }\r
290                                                 lim_time += target_frametime;\r
291                                         }\r
292                                 }\r
293                                 else if(tval.tv_usec > lim_time) { // auto frameskip\r
294                                         // no time left for this frame - skip\r
295                                         CGameWindow::DoKeys();\r
296                                         SkipFrame(); frames_done++;\r
297                                         continue;\r
298                                 }\r
299 \r
300                                 // we might have lost focus already\r
301                                 if (gamestate != PGS_Running) break;\r
302 \r
303                                 CGameWindow::DoKeys();\r
304                                 PicoFrame();\r
305 \r
306                                 // check time\r
307                                 gettimeofday(&tval, 0);\r
308                                 if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
309 \r
310                                 // sleep if we are still too fast\r
311                                 if(PsndOut != 0 || currentConfig.Frameskip < 0)\r
312                                 {\r
313                                         // TODO: check if User::After() is accurate\r
314                                         gettimeofday(&tval, 0);\r
315                                         if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
316                                         if(tval.tv_usec < lim_time)\r
317                                         {\r
318                                                 // we are too fast\r
319                                                 simpleWait(thissec, lim_time);\r
320                                         }\r
321                                 }\r
322 \r
323                                 CPolledActiveScheduler::Instance()->Schedule();\r
324 \r
325                                 if (gamestate != PGS_Paused)\r
326                                         vidDrawFrame(notice, buff, frames_shown);\r
327 \r
328                                 frames_done++; frames_shown++;\r
329                         } // while\r
330 \r
331                         if (PicoAHW & PAHW_MCD) PicoCDBufferFree();\r
332 \r
333                         // save SRAM\r
334                         if ((currentConfig.EmuOpt & EOPT_USE_SRAM) && SRam.changed) {\r
335                                 emu_SaveLoadGame(0, 1);\r
336                                 SRam.changed = 0;\r
337                         }\r
338                         CPolledActiveScheduler::Instance()->Schedule();\r
339                         CGameWindow::FreeResources();\r
340                 }\r
341                 else if(gamestate == PGS_Reset)\r
342                 {\r
343                         PicoReset();\r
344                         pico_was_reset = 1;\r
345                         gamestate = PGS_Running;\r
346                 }\r
347                 else if(gamestate == PGS_ReloadRom)\r
348                 {\r
349                         loadrom_result = emu_ReloadRom(loadrom_fname);\r
350                         pico_was_reset = 1;\r
351                         if (loadrom_result)\r
352                                 gamestate = PGS_Running;\r
353                         else {\r
354                                 extern char menuErrorMsg[];\r
355                                 gamestate = PGS_Paused;\r
356                                 lprintf("%s\n", menuErrorMsg);\r
357                         }\r
358                         DEBUGPRINT(_L("done loading ROM, retval=%i"), loadrom_result);\r
359                         loadWaitSemaphore.Signal();\r
360                         User::After(50000);\r
361                 }\r
362                 else if(gamestate == PGS_Paused) {\r
363                         DEBUGPRINT(_L("pausing.."));\r
364                         pauseSemaphore.Wait();\r
365                 }\r
366                 else if(gamestate == PGS_KeyConfig)\r
367                 {\r
368                         // switch context to other thread\r
369                         User::After(50000);\r
370                         // prepare window and stuff\r
371                         CGameWindow::ConstructResourcesL();\r
372 \r
373                         TUint whichAction = 0;\r
374                         while(gamestate == PGS_KeyConfig) {\r
375                                 CGameWindow::DoKeysConfig(whichAction);\r
376                                 CPolledActiveScheduler::Instance()->Schedule();\r
377                                 if (gamestate != PGS_Paused)\r
378                                         vidKeyConfigFrame(whichAction);\r
379                                 User::After(150000);\r
380                         }\r
381 \r
382                         emu_WriteConfig(0);\r
383                         CGameWindow::FreeResources();\r
384                 } else if(gamestate == PGS_Quit) {\r
385                         break;\r
386                 }\r
387         }\r
388 \r
389         // this thread has to close it's own handles,\r
390         // other one will crash trying to do that\r
391         PicoExit();\r
392 \r
393         MainExit();\r
394 }\r
395 \r
396 \r
397 // main initialization\r
398 static void MainInit()\r
399 {\r
400         DEBUGPRINT(_L("\r\n\r\nstarting.."));\r
401 \r
402         DEBUGPRINT(_L("CPolledActiveScheduler::NewL()"));\r
403         CPolledActiveScheduler::NewL(); // create Polled AS for the sound engine\r
404 \r
405 //      HAL::Get(HALData::EMachineUid, machineUid); // find out the machine UID\r
406 \r
407         DumpMemInfo();\r
408 \r
409 //      if (pauseSemaphore.Handle() <= 0)\r
410 //              pauseSemaphore.CreateLocal(0);\r
411         DEBUGPRINT(_L("initSemaphore.Signal()"));\r
412         initSemaphore.Signal();\r
413 }\r
414 \r
415 \r
416 // does not return\r
417 static void MainExit()\r
418 {\r
419         RThread thisThread;\r
420 \r
421         DEBUGPRINT(_L("%i: cleaning up.."), (TInt32) thisThread.Id());\r
422 \r
423 //      pauseSemaphore.Close();\r
424 \r
425         if(gameAudio) delete gameAudio;\r
426 \r
427         // Polled AS\r
428         delete CPolledActiveScheduler::Instance();\r
429 }\r
430 \r
431 static void DumpMemInfo()\r
432 {\r
433         TInt    ramSize, ramSizeFree, romSize;\r
434         \r
435         HAL::Get(HALData::EMemoryRAM, ramSize);\r
436         HAL::Get(HALData::EMemoryRAMFree, ramSizeFree);\r
437         HAL::Get(HALData::EMemoryROM, romSize);\r
438 \r
439         DEBUGPRINT(_L("ram=%dKB, ram_free=%dKB, rom=%dKB"), ramSize/1024, ramSizeFree/1024, romSize/1024);\r
440 }\r
441 \r
442 \r
443 extern "C" TInt my_SetExceptionHandler(TInt, TExceptionHandler, TUint32);\r
444 \r
445 TInt EmuThreadFunction(TAny*)\r
446 {\r
447         TInt ret;\r
448         const TUint32 exs = KExceptionAbort|KExceptionKill|KExceptionUserInterrupt|KExceptionFpe|KExceptionFault|KExceptionInteger|KExceptionDebug;\r
449         \r
450         DEBUGPRINT(_L("EmuThreadFunction(), def ExceptionHandler %08x, my %08x"),\r
451                 User::ExceptionHandler(), ExceptionHandler);\r
452         User::SetJustInTime(1);\r
453         ret = User::SetExceptionHandler(ExceptionHandler, exs/*(TUint32) -1*/); // does not work :(\r
454         // my_SetExceptionHandler(KCurrentThreadHandle, ExceptionHandler, 0xffffffff);\r
455         DEBUGPRINT(_L("SetExceptionHandler %i, %08x"), ret, User::ExceptionHandler());\r
456         User::ModifyExceptionMask(0, exs);\r
457 \r
458         //TInt pc, sp;\r
459         //asm volatile ("str pc, %0" : "=m" (pc) );\r
460         //asm volatile ("str sp, %0" : "=m" (sp) );\r
461         //RDebug::Print(_L("executing @ 0x%08x, sp=0x%08x"), pc, sp);\r
462 \r
463 /*\r
464         RDebug::Print(_L("Base     Bottom   Top      Size     RW Name"));\r
465         TBuf<4> l_r(_L("R")), l_w(_L("W")), l_d(_L("-"));\r
466         RChunk chunk;\r
467         TFullName chunkname;\r
468         TFindChunk findChunk(_L("*"));\r
469         while( findChunk.Next(chunkname) != KErrNotFound ) {\r
470                 chunk.Open(findChunk);\r
471                 RDebug::Print(_L("%08x %08x %08x %08x %S%S %S"), chunk.Base(), chunk.Base()+chunk.Bottom(), chunk.Base()+chunk.Top(), chunk.Size(), chunk.IsReadable() ? &l_r : &l_d, chunk.IsWritable() ? &l_w : &l_d, &chunkname);\r
472                 chunk.Close();\r
473         }\r
474 */\r
475 \r
476         // can't do that, will crash here\r
477 //      if(cleanup) {\r
478 //              DEBUGPRINT(_L("found old CTrapCleanup, deleting.."));\r
479 //              delete cleanup;\r
480 //      }\r
481         \r
482         CTrapCleanup *cleanup = CTrapCleanup::New();\r
483 \r
484         TRAPD(error, TargetEpocGameL());\r
485 \r
486         __ASSERT_ALWAYS(!error, User::Panic(_L("PicoDrive"), error));\r
487         delete cleanup;\r
488 \r
489         DEBUGPRINT(_L("exitting.."));   \r
490         return 1;\r
491 }\r
492 \r
493 \r
494 void TPicoDirectScreenAccess::Restart(RDirectScreenAccess::TTerminationReasons aReason)\r
495 {\r
496         DEBUGPRINT(_L("TPicoDirectScreenAccess::Restart(%i)"), aReason);\r
497 \r
498 //      if (CGameWindow::iDSA) {\r
499 //              TRAPD(error, CGameWindow::iDSA->StartL());\r
500 //              if (error) DEBUGPRINT(_L("iDSA->StartL() error: %i"), error);\r
501 //      }\r
502 }\r
503 \r
504 \r
505 void TPicoDirectScreenAccess::AbortNow(RDirectScreenAccess::TTerminationReasons aReason)\r
506 {\r
507         DEBUGPRINT(_L("TPicoDirectScreenAccess::AbortNow(%i)"), aReason);\r
508 \r
509         // the WS wants us to stop, so let's obey\r
510         gamestate = PGS_Paused;\r
511 }\r
512 \r
513 \r
514 void CGameWindow::ConstructResourcesL()\r
515 {\r
516         DEBUGPRINT(_L("ConstructResourcesL()"));\r
517         // connect to window server\r
518         // tried to create it globally and not re-connect everytime,\r
519         // but my window started to lose focus strangely\r
520         iWsSession = new(ELeave) RWsSession();\r
521         User::LeaveIfError(iWsSession->Connect());\r
522 \r
523         //       * Tell the Window Server not to mess about with our process priority\r
524         //       * Also, because of the way legacy games are written, they never sleep\r
525         //       * and thus never voluntarily yield the CPU. We set our process priority\r
526         //       * to EPriorityForeground and hope that a Telephony application on\r
527         //       * this device runs at EPriorityForeground as well. If not, tough! ;-)\r
528 \r
529         iWsSession->ComputeMode(RWsSession::EPriorityControlDisabled);\r
530         RProcess me;\r
531         me.SetPriority(EPriorityForeground);\r
532 \r
533         iWsScreen = new(ELeave) CWsScreenDevice(*iWsSession);\r
534         User::LeaveIfError(iWsScreen->Construct());\r
535 //      User::LeaveIfError(iWsScreen->CreateContext(iWindowGc));\r
536 \r
537         iWsWindowGroup = RWindowGroup(*iWsSession);\r
538         User::LeaveIfError(iWsWindowGroup.Construct((TUint32)&iWsWindowGroup));\r
539         //iWsWindowGroup.SetOrdinalPosition(0);\r
540         //iWsWindowGroup.SetName(KServerWGName);\r
541         iWsWindowGroup.EnableScreenChangeEvents(); // flip events (EEventScreenDeviceChanged)\r
542         iWsWindowGroup.EnableFocusChangeEvents(); // EEventFocusGroupChanged\r
543         iWsWindowGroup.SetOrdinalPosition(0, 1); // TInt aPos, TInt aOrdinalPriority\r
544 \r
545         iWsWindow=RWindow(*iWsSession);\r
546         User::LeaveIfError(iWsWindow.Construct(iWsWindowGroup, (TUint32)&iWsWindow));\r
547         iWsWindow.SetSize(iWsScreen->SizeInPixels());\r
548         iWsWindow.PointerFilter(EPointerFilterDrag, 0);\r
549         iWsWindow.SetPointerGrab(ETrue);\r
550         iWsWindow.SetVisible(ETrue);\r
551         iWsWindow.Activate();\r
552 \r
553 #if 0\r
554         // request access through RDirectScreenAccess api, but don't care about the result\r
555         // hangs?\r
556         RRegion *dsa_region = 0;\r
557         iDSA = new(ELeave) RDirectScreenAccess(*iWsSession);\r
558         if(iDSA->Construct() == KErrNone)\r
559                 iDSA->Request(dsa_region, iDSAstatus, iWsWindow);\r
560         DEBUGPRINT(_L("DSA: %i"), dsa_region ? dsa_region->Count() : -1);\r
561 #endif\r
562 \r
563         TInt ret;\r
564 \r
565         // request access through CDirectScreenAccess\r
566         iDSA = CDirectScreenAccess::NewL(*iWsSession, *iWsScreen, iWsWindow, iPDSA);\r
567 \r
568         // now get the screenbuffer\r
569         TScreenInfoV01                  screenInfo;\r
570         TPckg<TScreenInfoV01>   sI(screenInfo);\r
571         UserSvr::ScreenInfo(sI);\r
572 \r
573         if(!screenInfo.iScreenAddressValid)\r
574                 User::Leave(KErrNotSupported);\r
575 \r
576         DEBUGPRINT(_L("framebuffer=0x%08x (%dx%d)"), screenInfo.iScreenAddress,\r
577                                         screenInfo.iScreenSize.iWidth, screenInfo.iScreenSize.iHeight);\r
578         \r
579         // vidInit\r
580         DEBUGPRINT(_L("vidInit()"));\r
581         ret = vidInit((void *)screenInfo.iScreenAddress, 0);\r
582         DEBUGPRINT(_L("vidInit() done (%i)"), ret);\r
583 \r
584         User::LeaveIfError(ret);\r
585 \r
586         memset(keyFlags, 0, 256);\r
587         keyFlags[EStdKeyM600JogUp] = keyFlags[EStdKeyM600JogDown] = 2; // add "pulse only" for jog up/down\r
588         keyFlags[EStdKeyOff] = 0x40; // not configurable\r
589 \r
590         // try to start the audio engine\r
591         static int PsndRate_old = 0, PicoOpt_old = 0, pal_old = 0;\r
592 \r
593         if (gamestate == PGS_Running && (currentConfig.EmuOpt & EOPT_EN_SOUND))\r
594         {\r
595                 TInt err = 0;\r
596                 if(PsndRate != PsndRate_old || (PicoOpt&11) != (PicoOpt_old&11) || Pico.m.pal != pal_old) {\r
597                         // if rate changed, reset all enabled chips, else reset only those chips, which were recently enabled\r
598                         //sound_reset(PsndRate != PsndRate_old ? PicoOpt : (PicoOpt&(PicoOpt^PicoOpt_old)));\r
599                         PsndRerate(1);\r
600                 }\r
601                 if(!gameAudio || PsndRate != PsndRate_old || ((PicoOpt&8) ^ (PicoOpt_old&8)) || Pico.m.pal != pal_old) { // rate or stereo or pal/ntsc changed\r
602                         if(gameAudio) delete gameAudio; gameAudio = 0;\r
603                         DEBUGPRINT(_L("starting audio: %i len: %i stereo: %i, pal: %i"), PsndRate, PsndLen, PicoOpt&8, Pico.m.pal);\r
604                         TRAP(err, gameAudio = CGameAudioMS::NewL(PsndRate, (PicoOpt&8) ? 1 : 0,\r
605                                                 Pico.m.pal ? 50 : 60, currentConfig.volume));\r
606                 }\r
607                 if( gameAudio) {\r
608                         TRAP(err, PsndOut = gameAudio->ResumeL());\r
609                 }\r
610                 if(err) {\r
611                         if(gameAudio) delete gameAudio;\r
612                         gameAudio = 0;\r
613                         PsndOut = 0;\r
614                         strcpy(noticeMsg, "SOUND@STARTUP@FAILED");\r
615                         gettimeofday(&noticeMsgTime, 0);\r
616                 }\r
617                 PsndRate_old = PsndRate;\r
618                 PicoOpt_old  = PicoOpt;\r
619                 pal_old = Pico.m.pal;\r
620         } else {\r
621                 if(gameAudio) delete gameAudio;\r
622                 gameAudio = 0;\r
623                 PsndOut = 0;\r
624         }\r
625 \r
626         CPolledActiveScheduler::Instance()->Schedule();\r
627 \r
628         // start key WS event polling\r
629         iWsSession->EventReady(&iWsEventStatus);\r
630 \r
631         iWsSession->Flush(); // check: short hang in UIQ2\r
632         User::After(1);\r
633 \r
634         // I don't know why but the Window server sometimes hangs completely (hanging the phone too) after calling StartL()\r
635         // Is this a sync broblem? weird bug?\r
636         TRAP(ret, iDSA->StartL());\r
637         if (ret) DEBUGPRINT(_L("iDSA->StartL() error: %i"), ret);\r
638 \r
639 //      User::After(1);\r
640 //      CPolledActiveScheduler::Instance()->Schedule();\r
641 \r
642         DEBUGPRINT(_L("CGameWindow::ConstructResourcesL() finished."));\r
643 }\r
644 \r
645 // this may be run even if there is nothing to free\r
646 void CGameWindow::FreeResources()\r
647 {\r
648         if(gameAudio) gameAudio->Pause();\r
649 \r
650         //DEBUGPRINT(_L("CPolledActiveScheduler::Instance(): %08x"), CPolledActiveScheduler::Instance());\r
651         if(CPolledActiveScheduler::Instance())\r
652                 CPolledActiveScheduler::Instance()->Schedule();\r
653 \r
654 #if 0\r
655         // free RDirectScreenAccess stuff (seems to be deleted automatically after crash?)\r
656         if(iDSA) {\r
657                 iDSA->Cancel();\r
658                 iDSA->Close();\r
659                 delete iDSA;\r
660         }\r
661         iDSA = NULL;\r
662 #endif\r
663         if(iDSA) delete iDSA;\r
664         iDSA = 0;\r
665 \r
666         if(iWsSession->WsHandle() > 0 && iWsEventStatus != KRequestPending) // TODO: 2 UIQ2 (?)\r
667                 iWsSession->EventReadyCancel();\r
668 \r
669         if(iWsWindow.WsHandle() > 0)\r
670                 iWsWindow.Close();\r
671 \r
672         if(iWsWindowGroup.WsHandle() > 0)\r
673                 iWsWindowGroup.Close();\r
674 \r
675         // these must be deleted before calling iWsSession->Close()\r
676         if(iWsScreen) {\r
677                 delete iWsScreen;\r
678                 iWsScreen = NULL;\r
679         }\r
680 \r
681         if(iWsSession->WsHandle() > 0) {\r
682                 iWsSession->Close();\r
683                 delete iWsSession;\r
684         }\r
685         \r
686         vidFree();\r
687 }\r
688 \r
689 \r
690 void CGameWindow::DoKeys(void)\r
691 {\r
692         TWsEvent iWsEvent;\r
693         TInt iWsEventType;\r
694         unsigned long allActions = 0;\r
695         static unsigned long areaActions = 0, forceUpdate = 0;\r
696         int i, nEvents;\r
697 \r
698         for(nEvents = 0; iWsEventStatus != KRequestPending; nEvents++)\r
699         {\r
700                 iWsSession->GetEvent(iWsEvent);\r
701                 iWsEventType = iWsEvent.Type();\r
702 \r
703                 // pointer events?\r
704                 if(iWsEventType == EEventPointer) {\r
705                         if(iWsEvent.Pointer()->iType == TPointerEvent::EButton1Up) {\r
706                                 areaActions = 0; // remove all directionals\r
707                         } else { // if(iWsEvent.Pointer()->iType == TPointerEvent::EButton1Down) {\r
708                                 TPoint p = iWsEvent.Pointer()->iPosition;\r
709                                 const TPicoAreaConfigEntry *e = areaConfig + 1;\r
710                                 for(i = 0; !e->rect.IsEmpty(); e++, i++)\r
711                                         if(e->rect.Contains(p)) {\r
712                                                 areaActions = currentConfig.KeyBinds[i+256];\r
713                                                 break;\r
714                                         }\r
715                                 //DEBUGPRINT(_L("pointer event: %i %i"), p.iX, p.iY);\r
716                         }\r
717                 }\r
718                 else if(iWsEventType == EEventKeyDown || iWsEventType == EEventKeyUp) {\r
719                         TInt iScanCode = iWsEvent.Key()->iScanCode;\r
720                         //DEBUGPRINT(_L("key event: 0x%02x"), iScanCode);\r
721 \r
722                         if(iScanCode < 256)\r
723                         {\r
724                                 if(iWsEventType == EEventKeyDown) {\r
725                                         keyFlags[iScanCode] |=  1;\r
726                                         for(i=0; i < 10; i++) {\r
727                                                 if( pressedKeys[i] == (TUint8) iScanCode) break;\r
728                                                 if(!pressedKeys[i]) { pressedKeys[i] = (TUint8) iScanCode; break; }\r
729                                         }\r
730                                 } else if(!(keyFlags[iScanCode]&2)) {\r
731                                         keyFlags[iScanCode] &= ~1;\r
732                                         for(i=0; i < 10; i++) {\r
733                                                 if(pressedKeys[i] == (TUint8) iScanCode) { pressedKeys[i] = 0; break; }\r
734                                         }\r
735                                 }\r
736 \r
737                                 // power?\r
738                                 if(iScanCode == EStdKeyOff) gamestate = PGS_Paused;\r
739                         } else {\r
740                                 DEBUGPRINT(_L("weird scancode: 0x%02x"), iScanCode);\r
741                         }\r
742                 }\r
743                 else if(iWsEventType == EEventScreenDeviceChanged) {\r
744                         // ???\r
745                         //User::After(500000);\r
746                         //reset_timing = 1;\r
747                         DEBUGPRINT(_L("EEventScreenDeviceChanged, focus: %i, our: %i"),\r
748                                                 iWsSession->GetFocusWindowGroup(), iWsWindowGroup.Identifier());\r
749                 }\r
750                 else if(iWsEventType == EEventFocusGroupChanged) {\r
751                         TInt focusGrpId = iWsSession->GetFocusWindowGroup();\r
752                         DEBUGPRINT(_L("EEventFocusGroupChanged: %i, our: %i"),\r
753                                                 focusGrpId, iWsWindowGroup.Identifier());\r
754                         // if it is not us and not launcher that got focus, pause emu\r
755                         if(focusGrpId != iWsWindowGroup.Identifier())\r
756                                 gamestate = PGS_Paused;\r
757                 }\r
758 \r
759                 iWsEventStatus = KRequestPending;\r
760                 iWsSession->EventReady(&iWsEventStatus);\r
761         }\r
762 \r
763         if(nEvents || forceUpdate) {\r
764                 allActions = areaActions;\r
765                 forceUpdate = 0;\r
766 \r
767                 // add all pushed button actions\r
768                 for(i = 9; i >= 0; i--) {\r
769                         int scan = pressedKeys[i];\r
770                         if(scan) {\r
771                                 if(keyFlags[scan] & 1) allActions |= currentConfig.KeyBinds[scan];\r
772                                 if((keyFlags[scan]& 3)==3) forceUpdate = 1;\r
773                                 if(keyFlags[scan] & 2) keyFlags[scan] &= ~1;\r
774                         }\r
775                 }\r
776 \r
777                 PicoPad[0] = (unsigned short) allActions;\r
778                 if(allActions & 0xFFFF0000) {\r
779                         RunEvents(allActions >> 16);\r
780                         areaActions = 0;\r
781                 }\r
782         }\r
783 \r
784         if (movie_data) emu_updateMovie();\r
785 }\r
786 \r
787 \r
788 void CGameWindow::DoKeysConfig(TUint &which)\r
789 {\r
790         TWsEvent iWsEvent;\r
791         int i;\r
792 \r
793         while(iWsEventStatus != KRequestPending)\r
794         {\r
795                 TUint currentActCode = 1 << which;\r
796 \r
797                 iWsSession->GetEvent(iWsEvent);\r
798 \r
799                 // pointer events?\r
800                 if(iWsEvent.Type() == EEventPointer) {\r
801                         TPoint p = iWsEvent.Pointer()->iPosition;\r
802                         TRect prev(56,  0, 120, 26);\r
803                         TRect next(120, 0, 180, 26);\r
804 \r
805                         if(iWsEvent.Pointer()->iType == TPointerEvent::EButton1Down) {\r
806                                      if(prev.Contains(p)) do { which = (which-1) & 0x1F; } while(!actionNames[which]);\r
807                                 else if(next.Contains(p)) do { which = (which+1) & 0x1F; } while(!actionNames[which]);\r
808                                 else if(which == 31) gamestate = PGS_Paused; // done\r
809                                 else {\r
810                                         const TPicoAreaConfigEntry *e = areaConfig + 1;\r
811                                         for(i = 0; e->rect != TRect(0,0,0,0); e++, i++)\r
812                                                 if(e->rect.Contains(p)) {\r
813                                                         currentConfig.KeyBinds[i+256] ^= currentActCode;\r
814                                                         break;\r
815                                                 }\r
816                                 }\r
817                         }\r
818                 }\r
819                 else if(iWsEvent.Type() == EEventKeyDown || iWsEvent.Type() == EEventKeyUp)\r
820                 {\r
821                         TUint scan = (TUint) iWsEvent.Key()->iScanCode;\r
822 \r
823                         // key events?\r
824                         if(iWsEvent.Type() == EEventKeyDown) {\r
825                                 if(which == 31) {\r
826                                         gamestate = PGS_Paused;\r
827                                 } else if (scan < 256) {\r
828                                         if(!(keyFlags[scan]&0x40)) currentConfig.KeyBinds[scan] ^= currentActCode;\r
829                                 }\r
830                         }\r
831 \r
832                         // power?\r
833                         if(iWsEvent.Key()->iScanCode == EStdKeyOff) gamestate = PGS_Paused;\r
834                 }\r
835                 else if(iWsEvent.Type() == EEventFocusGroupChanged) {\r
836                         TInt focusGrpId = iWsSession->GetFocusWindowGroup();\r
837                         // if we lost focus, exit config mode\r
838                         if(focusGrpId != iWsWindowGroup.Identifier())\r
839                                 gamestate = PGS_Paused;\r
840                 }\r
841 \r
842 //              iWsEventStatus = KRequestPending;\r
843                 iWsSession->EventReady(&iWsEventStatus);\r
844         }\r
845 }\r
846 \r
847 \r
848 void CGameWindow::RunEvents(TUint32 which)\r
849 {\r
850         if (which & 0x4000) currentConfig.Frameskip = -1;\r
851         if (which & 0x2000) currentConfig.Frameskip =  8;\r
852         if (which & 0x1800) { // save or load (but not both)\r
853                 if(PsndOut) gameAudio->Pause(); // this may take a while, so we pause sound output\r
854 \r
855                 vidDrawNotice((which & 0x1000) ? "LOADING@GAME" : "SAVING@GAME");\r
856                 emu_SaveLoadGame(which & 0x1000, 0);\r
857 \r
858                 if(PsndOut) PsndOut = gameAudio->ResumeL();\r
859                 reset_timing = 1;\r
860         }\r
861         if (which & 0x0400) gamestate = PGS_Paused;\r
862         if (which & 0x0200) { // switch renderer\r
863                 if (!(currentConfig.scaling == TPicoConfig::PMFit &&\r
864                         (currentConfig.rotation == TPicoConfig::PRot0 || currentConfig.rotation == TPicoConfig::PRot180)))\r
865                 {\r
866                         PicoOpt^=0x10;\r
867                         vidInit(0, 1);\r
868 \r
869                         strcpy(noticeMsg, (PicoOpt&0x10) ? "ALT@RENDERER" : "DEFAULT@RENDERER");\r
870                         gettimeofday(&noticeMsgTime, 0);\r
871                 }\r
872         }\r
873         if(which & 0x00c0) {\r
874                 if(which&0x0080) {\r
875                         state_slot -= 1;\r
876                         if(state_slot < 0) state_slot = 9;\r
877                 } else {\r
878                         state_slot += 1;\r
879                         if(state_slot > 9) state_slot = 0;\r
880                 }\r
881                 sprintf(noticeMsg, "SAVE@SLOT@%i@SELECTED", state_slot);\r
882                 gettimeofday(&noticeMsgTime, 0);\r
883         }\r
884         if ((which & 0x0030) && gameAudio != NULL) {\r
885                 currentConfig.volume = gameAudio->ChangeVolume((which & 0x0010) ? 1 : 0);\r
886                 sprintf(noticeMsg, "VOL@%02i@", currentConfig.volume);\r
887                 gettimeofday(&noticeMsgTime, 0);\r
888         }\r
889 }\r
890 \r
891 \r
892 extern "C" void emu_noticeMsgUpdated(void)\r
893 {\r
894         char *p = noticeMsg;\r
895         while (*p) {\r
896                 if (*p == ' ') *p = '@';\r
897                 if (*p < '0' || *p > 'Z') { *p = 0; break; }\r
898                 p++;\r
899         }\r
900         gettimeofday(&noticeMsgTime, 0);\r
901 }\r
902 \r
903 // static class members\r
904 RWsSession*                             CGameWindow::iWsSession;\r
905 RWindowGroup                    CGameWindow::iWsWindowGroup;\r
906 RWindow                                 CGameWindow::iWsWindow;\r
907 CWsScreenDevice*                CGameWindow::iWsScreen = NULL;\r
908 CWindowGc*                              CGameWindow::iWindowGc = NULL;\r
909 TRequestStatus                  CGameWindow::iWsEventStatus = KRequestPending;\r
910 //RDirectScreenAccess*  CGameWindow::iDSA;\r
911 //TRequestStatus                        CGameWindow::iDSAstatus = KRequestPending;\r
912 TPicoDirectScreenAccess CGameWindow::iPDSA;\r
913 CDirectScreenAccess*    CGameWindow::iDSA = NULL;\r
914 \r