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