4 // Graphical User Interface support
15 #include "sdlemu_opengl.h"
20 // Once these are split off, these may not be needed anymore...
28 #include "pushbutton.h"
29 #include "slideswitch.h"
34 // Private function prototypes
36 Window * LoadROM(void);
37 Window * ResetJaguar(void);
38 Window * ResetJaguarCD(void);
39 Window * RunEmu(void);
42 Window * MiscOptions(void);
44 // Local global variables
47 bool exitGUI = false; // GUI (emulator) done variable
48 int mouseX = 0, mouseY = 0;
49 uint32 background[1280 * 256]; // GUI background buffer
50 bool showMessage = false;
51 //uint32 showMessageTimeout;
52 //char messageBuffer[200];
53 bool finished = false;
56 // GUI stuff--it's not crunchy, it's GUI! ;-)
61 SDL_ShowCursor(SDL_DISABLE);
62 SDL_GetMouseState(&mouseX, &mouseY);
73 bool GUIMain(char * filename)
75 WriteLog("GUI: Inside GUIMain...\n");
77 uint32 pointerBGSave[6 * 8 + 2];
81 // Need to set things up so that it loads and runs a file if given on the command line. !!! FIX !!! [DONE]
82 // extern uint32 * backbuffer;
85 Window * mainWindow = NULL;
87 // Set up the GUI classes...
88 // Element::SetScreenAndPitch(backbuffer, GetSDLScreenWidthInPixels());
89 Element::SetScreenAndPitch((uint32 *)sdlemuGetOverlayPixels(), sdlemuGetOverlayWidthInPixels());
90 sdlemuEnableOverlay();
95 mi.item.push_back(NameAction("Load...", LoadROM, SDLK_l));
96 mi.item.push_back(NameAction("Reset", ResetJaguar, SDLK_r));
98 mi.item.push_back(NameAction("Reset CD", ResetJaguarCD, SDLK_c));
99 mi.item.push_back(NameAction("Run", RunEmu, SDLK_ESCAPE));
100 mi.item.push_back(NameAction(""));
101 mi.item.push_back(NameAction("Quit", Quit, SDLK_q));
103 mi.title = "Settings";
105 mi.item.push_back(NameAction("Video..."));
106 mi.item.push_back(NameAction("Audio..."));
107 mi.item.push_back(NameAction("Misc...", MiscOptions, SDLK_m));
111 mi.item.push_back(NameAction("About...", About));
114 bool showMouse = true;
116 // Grab the BG where the mouse will be painted (prime the backstore)
120 Bitmap ptr = { 6, 8, 4,
121 ""//"000011112222333344445555"
122 //"000011112222333344445555"
123 //"000011112222333344445555"
124 //"000011112222333344445555"
125 //"000011112222333344445555"
126 //"000011112222333344445555"
127 //"000011112222333344445555"
128 //"000011112222333344445555"
130 uint32 * overlayPixels = (uint32 *)sdlemuGetOverlayPixels();
133 for(uint32 y=0; y<pointerBGSave[1]; y++)
134 for(uint32 x=0; x<pointerBGSave[0]; x++)
135 pointerBGSave[count++] = overlayPixels[((mouseY + y) * sdlemuGetOverlayWidthInPixels()) + (mouseX + x)];
137 uint32 oldMouseX = mouseX, oldMouseY = mouseY;
139 //This is crappy!!! !!! FIX !!!
140 //Is this even needed any more? Hmm. Maybe. Dunno.
141 WriteLog("GUI: Resetting Jaguar...\n");
144 WriteLog("GUI: Clearing BG save...\n");
145 // Set up our background save...
146 // memset(background, 0x11, tom_getVideoModeWidth() * 240 * 2);
147 //1111 -> 000100 01000 10001 -> 0001 0000 0100 0010 1000 1100 -> 10 42 8C
148 for(uint32 i=0; i<TOMGetVideoModeWidth()*240; i++)
149 // background[i] = 0xFF8C4210;
150 backbuffer[i] = 0xFF8C4210;
152 /* uint32 * overlayPix = (uint32 *)sdlemuGetOverlayPixels();
153 for(uint32 i=0; i<sdlemuGetOverlayWidthInPixels()*480; i++)
154 overlayPix[i] = 0x00000000;*/
156 // Handle loading file passed in on the command line...! [DONE]
160 if (JaguarLoadFile(filename))
162 // event.type = SDL_USEREVENT, event.user.code = MENU_ITEM_CHOSEN;
163 // event.user.data1 = (void *)ResetJaguar;
164 // SDL_PushEvent(&event);
165 // Make it so that if passed in on the command line, we quit right
166 // away when pressing ESC
167 WriteLog("GUI: Bypassing GUI since ROM passed in on command line...\n");
173 // Create error dialog...
175 sprintf(errText, "The file %40s could not be loaded.", filename);
177 mainWindow = new Window(8, 16, 304, 160);
178 mainWindow->AddElement(new Text(8, 8, "Error!"));
179 mainWindow->AddElement(new Text(8, 24, errText));
183 WriteLog("GUI: Entering main loop...\n");
186 if (SDL_PollEvent(&event))
188 if (event.type == SDL_USEREVENT)
190 if (event.user.code == WINDOW_CLOSE)
195 else if (event.user.code == MENU_ITEM_CHOSEN)
197 // Confused? Let me enlighten... What we're doing here is casting
198 // data1 as a pointer to a function which returns a Window pointer and
199 // which takes no parameters (the "(Window *(*)(void))" part), then
200 // derefencing it (the "*" in front of that) in order to call the
201 // function that it points to. Clear as mud? Yeah, I hate function
202 // pointers too, but what else are you gonna do?
203 mainWindow = (*(Window *(*)(void))event.user.data1)();
205 while (SDL_PollEvent(&event)); // Flush the event queue...
206 event.type = SDL_MOUSEMOTION;
208 SDL_GetMouseState(&mx, &my);
209 event.motion.x = mx, event.motion.y = my;
210 SDL_PushEvent(&event); // & update mouse position...!
212 oldMouseX = mouseX, oldMouseY = mouseY;
213 mouseX = mx, mouseY = my; // This prevents "mouse flash"...
216 else if (event.type == SDL_ACTIVEEVENT)
218 if (event.active.state == SDL_APPMOUSEFOCUS)
219 showMouse = (event.active.gain ? true : false);
221 else if (event.type == SDL_KEYDOWN)
223 // Ugly kludge for windowed<-->fullscreen switching...
224 uint8 * keystate = SDL_GetKeyState(NULL);
226 if ((keystate[SDLK_LALT] || keystate[SDLK_RALT]) & keystate[SDLK_RETURN])
230 mainWindow->HandleKey(event.key.keysym.sym);
232 mainMenu.HandleKey(event.key.keysym.sym);
234 else if (event.type == SDL_MOUSEMOTION)
236 oldMouseX = mouseX, oldMouseY = mouseY;
237 mouseX = event.motion.x, mouseY = event.motion.y;
240 mainWindow->HandleMouseMove(mouseX, mouseY);
242 mainMenu.HandleMouseMove(mouseX, mouseY);
244 else if (event.type == SDL_MOUSEBUTTONDOWN)
246 uint32 mx = event.button.x, my = event.button.y;
249 mainWindow->HandleMouseButton(mx, my, true);
251 mainMenu.HandleMouseButton(mx, my, true);
253 else if (event.type == SDL_MOUSEBUTTONUP)
255 uint32 mx = event.button.x, my = event.button.y;
258 mainWindow->HandleMouseButton(mx, my, false);
260 mainMenu.HandleMouseButton(mx, my, false);
263 //PROBLEM: In order to use the dirty rectangle approach here, we need some way of
264 // handling it in mainMenu.Draw() and mainWindow->Draw(). !!! FIX !!!
266 // When mouse is moving and not on menu or window, can do straight dirty rect.
267 // When mouse is on menu, need to update screen. Same for buttons on windows...
268 // What the menu & windows should do is only redraw on a state change. IOW, they
269 // should call their own/child window's Draw() function instead of doing it top
271 //#define NEW_BACKSTORE_METHOD
274 // The way we do things here is kinda stupid (redrawing the screen every frame), but
275 // it's simple. Perhaps there may be a reason down the road to be more selective with
276 // our clearing, but for now, this will suffice.
277 // memset(backbuffer, 0x11, tom_getVideoModeWidth() * 240 * 2);
278 // memcpy(backbuffer, background, tom_getVideoModeWidth() * 256 * 2);
279 // memcpy(backbuffer, background, tom_getVideoModeWidth() * 256 * 4);
280 #ifndef NEW_BACKSTORE_METHOD
281 memset(sdlemuGetOverlayPixels(), 0, sdlemuGetOverlayWidthInPixels() * 480 * 4);
284 //Could do multiple windows here by using a vector + priority info...
285 //Though the way ZSNES does it seems to be by a bool (i.e., they're always active, just not shown)
290 /*uint32 pBGS[6 * 8 + 3] = { 6, 8, 4,
300 //This isn't working... Why????
301 //It's because DrawTransparentBitmap does alpha blending if it detects zero in the alpha channel.
302 //So why do it that way? Hm.
303 overlayPixels = (uint32 *)sdlemuGetOverlayPixels();
305 #ifdef NEW_BACKSTORE_METHOD
306 // DrawTransparentBitmapDeprecated(overlayPixels, oldMouseX, oldMouseY, pointerBGSave);
307 // DrawTransparentBitmap(overlayPixels, oldMouseX, oldMouseY, pBGS);
308 for(uint32 y=0; y<pointerBGSave[1]; y++)
309 for(uint32 x=0; x<pointerBGSave[0]; x++)
310 overlayPixels[((oldMouseY + y) * sdlemuGetOverlayWidthInPixels()) + (oldMouseX + x)] = 0x00000000;
314 for(uint32 y=0; y<pointerBGSave[1]; y++)
315 for(uint32 x=0; x<pointerBGSave[0]; x++)
316 pointerBGSave[count++] = overlayPixels[((mouseY + y) * sdlemuGetOverlayWidthInPixels()) + (mouseX + x)];
320 // DrawTransparentBitmapDeprecated(backbuffer, mouseX, mouseY, mousePic);
321 DrawTransparentBitmapDeprecated(overlayPixels, mouseX, mouseY, mousePic);
331 // GUI "action" functions
334 Window * LoadROM(void)
336 FileList * fileList = new FileList(20, 20, 600, 440);
338 return (Window *)fileList;
341 Window * ResetJaguar(void)
348 Window * ResetJaguarCD(void)
350 memcpy(jaguarMainRom, jaguarCDBootROM, 0x40000);
351 jaguarRunAddress = 0x802000;
352 jaguarMainRomCRC32 = crc32_calcCheckSum(jaguarMainRom, 0x40000);
354 //This is a quick kludge to get the CDBIOS to boot properly...
355 //Wild speculation: It could be that this memory location is wired into the CD unit
356 //somehow, which lets it know whether or not a cart is present in the unit...
357 jaguarMainRom[0x0040B] = 0x03;
365 bool debounceRunKey = true;
366 Window * RunEmu(void)
368 extern uint32 * backbuffer;
369 //Temporary, to test the new timer based code...
370 sdlemuDisableOverlay();
372 sdlemuEnableOverlay();
373 // Save the background for the GUI...
374 // In this case, we squash the color to monochrome, then force it to blue + green...
375 for(uint32 i=0; i<tom_getVideoModeWidth() * 256; i++)
377 uint32 pixel = backbuffer[i];
378 uint8 b = (pixel >> 16) & 0xFF, g = (pixel >> 8) & 0xFF, r = pixel & 0xFF;
379 pixel = ((r + g + b) / 3) & 0x00FF;
380 backbuffer[i] = 0xFF000000 | (pixel << 16) | (pixel << 8);
384 //This is crappy... !!! FIX !!!
385 extern bool finished, showGUI;
387 // uint32 nFrame = 0, nFrameskip = 0;
388 uint32 totalFrames = 0;
390 bool showMessage = true;
391 uint32 showMsgFrames = 120;
392 uint8 transparency = 0;
393 // Pass a message to the "joystick" code to debounce the ESC key...
394 debounceRunKey = true;
397 if (jaguarRomSize == 0x200000)
399 else if (jaguarRomSize == 0x400000)
401 else if (jaguar_mainRom_crc32 == 0x687068D5)
403 else if (jaguar_mainRom_crc32 == 0x55A0669C)
406 char * cartTypeName[5] = { "2M Cartridge", "4M Cartridge", "CD BIOS", "CD Dev BIOS", "Homebrew" };
407 uint32 elapsedTicks = SDL_GetTicks(), frameCount = 0, framesPerSecond = 0;
411 // Set up new backbuffer with new pixels and data
412 JaguarExecute(backbuffer, true);
413 // JaguarExecuteNew();
415 //WriteLog("Frame #%u...\n", totalFrames);
416 //extern bool doDSPDis;
417 //if (totalFrames == 373)
420 //This sucks... !!! FIX !!!
422 //This is done here so that the crud below doesn't get on our GUI background...
426 // Some QnD GUI stuff here...
429 extern uint32 gpu_pc, dsp_pc;
430 DrawString(backbuffer, 8, 8, false, "GPU PC: %08X", gpu_pc);
431 DrawString(backbuffer, 8, 16, false, "DSP PC: %08X", dsp_pc);
432 DrawString(backbuffer, 8, 32, false, "%u FPS", framesPerSecond);
437 // FF0F -> 1111 11 11 000 0 1111 -> 3F 18 0F
438 // 3FE3 -> 0011 11 11 111 0 0011 -> 0F 3F 03
439 /* DrawStringTrans((uint32 *)backbuffer, 8, 24*8, 0xFF0F, transparency, "Running...");
440 DrawStringTrans((uint32 *)backbuffer, 8, 26*8, 0x3FE3, transparency, "%s, run address: %06X", cartTypeName[cartType], jaguarRunAddress);
441 DrawStringTrans((uint32 *)backbuffer, 8, 27*8, 0x3FE3, transparency, "CRC: %08X", jaguar_mainRom_crc32);//*/
442 //first has wrong color. !!! FIX !!!
443 DrawStringTrans(backbuffer, 8, 24*8, 0xFF7F63FF, transparency, "Running...");
444 DrawStringTrans(backbuffer, 8, 26*8, 0xFF1FFF3F, transparency, "%s, run address: %06X", cartTypeName[cartType], jaguarRunAddress);
445 DrawStringTrans(backbuffer, 8, 27*8, 0xFF1FFF3F, transparency, "CRC: %08X", jaguar_mainRom_crc32);
447 if (showMsgFrames == 0)
451 if (transparency == 33)
454 /*extern bool doGPUDis;
466 if (SDL_GetTicks() - elapsedTicks > 250)
467 elapsedTicks += 250, framesPerSecond = frameCount * 4, frameCount = 0;
470 // Reset the pitch, since it may have been changed in-game...
471 Element::SetScreenAndPitch((uint32 *)backbuffer, GetSDLScreenWidthInPixels());
473 // Save the background for the GUI...
474 // memcpy(background, backbuffer, tom_getVideoModeWidth() * 240 * 2);
475 // In this case, we squash the color to monochrome, then force it to blue + green...
476 for(uint32 i=0; i<tom_getVideoModeWidth() * 256; i++)
478 uint32 pixel = backbuffer[i];
479 uint8 b = (pixel >> 16) & 0xFF, g = (pixel >> 8) & 0xFF, r = pixel & 0xFF;
480 pixel = ((r + g + b) / 3) & 0x00FF;
481 background[i] = 0xFF000000 | (pixel << 16) | (pixel << 8);
489 bool debounceRunKey = true;
490 Window * RunEmu(void)
492 // extern uint32 * backbuffer;
493 uint32 * overlayPixels = (uint32 *)sdlemuGetOverlayPixels();
494 memset(overlayPixels, 0x00, 640 * 480 * 4); // Clear out overlay...
496 //This is crappy... !!! FIX !!!
497 // extern bool finished, showGUI;
499 sdlemuDisableOverlay();
501 // uint32 nFrame = 0, nFrameskip = 0;
502 uint32 totalFrames = 0;
504 bool showMessage = true;
505 uint32 showMsgFrames = 120;
506 uint8 transparency = 0xFF;
507 // Pass a message to the "joystick" code to debounce the ESC key...
508 debounceRunKey = true;
511 if (jaguarRomSize == 0x200000)
513 else if (jaguarRomSize == 0x400000)
515 else if (jaguarMainRomCRC32 == 0x687068D5)
517 else if (jaguarMainRomCRC32 == 0x55A0669C)
520 const char * cartTypeName[5] = { "2M Cartridge", "4M Cartridge", "CD BIOS", "CD Dev BIOS", "Homebrew" };
521 uint32 elapsedTicks = SDL_GetTicks(), frameCount = 0, framesPerSecond = 0;
525 // Set up new backbuffer with new pixels and data
528 //WriteLog("Frame #%u...\n", totalFrames);
529 //extern bool doDSPDis;
530 //if (totalFrames == 373)
533 //Problem: Need to do this *only* when the state changes from visible to not...
534 //Also, need to clear out the GUI when not on (when showMessage is active...)
535 if (showGUI || showMessage)
536 sdlemuEnableOverlay();
538 sdlemuDisableOverlay();
540 //Add in a new function for clearing patches of screen (ClearOverlayRect)
542 // Also: Take frame rate into account when calculating fade time...
544 // Some QnD GUI stuff here...
547 FillScreenRectangle(overlayPixels, 8, 1*FONT_HEIGHT, 128, 4*FONT_HEIGHT, 0x00000000);
548 extern uint32 gpu_pc, dsp_pc;
549 DrawString(overlayPixels, 8, 1*FONT_HEIGHT, false, "GPU PC: %08X", gpu_pc);
550 DrawString(overlayPixels, 8, 2*FONT_HEIGHT, false, "DSP PC: %08X", dsp_pc);
551 DrawString(overlayPixels, 8, 4*FONT_HEIGHT, false, "%u FPS", framesPerSecond);
556 DrawString2(overlayPixels, 8, 24*FONT_HEIGHT, 0x007F63FF, transparency, "Running...");
557 DrawString2(overlayPixels, 8, 26*FONT_HEIGHT, 0x001FFF3F, transparency, "%s, run address: %06X", cartTypeName[cartType], jaguarRunAddress);
558 DrawString2(overlayPixels, 8, 27*FONT_HEIGHT, 0x001FFF3F, transparency, "CRC: %08X", jaguarMainRomCRC32);
560 if (showMsgFrames == 0)
564 if (transparency == 0)
567 /*extern bool doGPUDis;
578 if (SDL_GetTicks() - elapsedTicks > 250)
579 elapsedTicks += 250, framesPerSecond = frameCount * 4, frameCount = 0;
582 // Save the background for the GUI...
583 // In this case, we squash the color to monochrome, then force it to blue + green...
584 for(uint32 i=0; i<TOMGetVideoModeWidth() * 256; i++)
586 uint32 pixel = backbuffer[i];
587 uint8 b = (pixel >> 16) & 0xFF, g = (pixel >> 8) & 0xFF, r = pixel & 0xFF;
588 pixel = ((r + g + b) / 3) & 0x00FF;
589 backbuffer[i] = 0xFF000000 | (pixel << 16) | (pixel << 8);
592 sdlemuEnableOverlay();
602 WriteLog("GUI: Quitting due to user request.\n");
611 sprintf(buf, "SVN %s", __DATE__);
612 //fprintf(fp, "VirtualJaguar v1.0.8 (Last full build was on %s %s)\n", __DATE__, __TIME__);
613 //VirtualJaguar v1.0.8 (Last full build was on Dec 30 2004 20:01:31)
614 //Hardwired, bleh... !!! FIX !!!
615 uint32 width = 55 * FONT_WIDTH, height = 18 * FONT_HEIGHT;
616 uint32 xpos = (640 - width) / 2, ypos = (480 - height) / 2;
617 // Window * window = new Window(8, 16, 50 * FONT_WIDTH, 21 * FONT_HEIGHT);
618 Window * window = new Window(xpos, ypos, width, height);
619 // window->AddElement(new Text(8, 8, "Virtual Jaguar 1.0.8"));
620 // window->AddElement(new Text(8, 8, "Virtual Jaguar CVS 20050110", 0xFF3030FF, 0xFF000000));
621 // window->AddElement(new Text(208, 8+0*FONT_HEIGHT, buf, 0xFF3030FF, 0xFF000000));
622 window->AddElement(new Text(248, 8+4*FONT_HEIGHT+5, buf, 0xFF3030FF, 0xFF000000));
623 window->AddElement(new Text(8, 8+0*FONT_HEIGHT, "Coders:"));
624 window->AddElement(new Text(16, 8+1*FONT_HEIGHT, "James L. Hammons (shamus)"));
625 window->AddElement(new Text(16, 8+2*FONT_HEIGHT, "Niels Wagenaar (nwagenaar)"));
626 window->AddElement(new Text(16, 8+3*FONT_HEIGHT, "Carwin Jones (Caz)"));
627 window->AddElement(new Text(16, 8+4*FONT_HEIGHT, "Adam Green"));
628 window->AddElement(new Text(8, 8+6*FONT_HEIGHT, "Testers:"));
629 window->AddElement(new Text(16, 8+7*FONT_HEIGHT, "Guruma"));
630 window->AddElement(new Text(8, 8+9*FONT_HEIGHT, "Thanks go out to:"));
631 window->AddElement(new Text(16, 8+10*FONT_HEIGHT, "Aaron Giles for the original CoJag"));
632 window->AddElement(new Text(16, 8+11*FONT_HEIGHT, "David Raingeard for the original VJ"));
633 window->AddElement(new Text(16, 8+12*FONT_HEIGHT, "Karl Stenerud for his Musashi 68K emu"));
634 window->AddElement(new Text(16, 8+13*FONT_HEIGHT, "Sam Lantinga for his amazing SDL libs"));
635 window->AddElement(new Text(16, 8+14*FONT_HEIGHT, "Ryan C. Gordon for VJ's web presence"));
636 window->AddElement(new Text(16, 8+15*FONT_HEIGHT, "Curt Vendel for various Jaguar goodies"));
637 window->AddElement(new Text(16, 8+16*FONT_HEIGHT, "The guys over at Atari Age ;-)"));
638 // window->AddElement(new Image(8, 8, &vj_title_small));
639 // window->AddElement(new Image(width - (vj_title_small.width + 8), 8, &vj_title_small));
640 window->AddElement(new Image(width - (((Bitmap *)vj_title_small)->width + 8), 8, &vj_title_small));
645 Window * MiscOptions(void)
647 Window * window = new Window(8, 16, 304, 192);
648 window->AddElement(new PushButton(8, 8, &vjs.useJaguarBIOS, "BIOS"));
649 window->AddElement(new SlideSwitch(8, 32, &vjs.hardwareTypeNTSC, "PAL", "NTSC"));
650 window->AddElement(new PushButton(8, 64, &vjs.DSPEnabled, "DSP"));
651 window->AddElement(new SlideSwitch(24, 88, &vjs.usePipelinedDSP, "Original", "Pipelined"));
652 window->AddElement(new SlideSwitch(8, 120, (bool *)&vjs.glFilter, "Sharp", "Blurry"));
653 window->AddElement(new SlideSwitch(8, 152, (bool *)&vjs.renderType, "Normal render", "TV style"));
655 window->AddElement(new TextEdit(88, 8, vjs.ROMPath, 20, 0xFF8484FF, 0xFF000000));
657 /*TextEdit(uint32 x, uint32 y, string s, uint32 mss = 10, uint32 fg = 0xFF8484FF,
658 uint32 bg = 0xFF84FF4D): Element(x, y, 0, 0), fgColor(fg), bgColor(bg), text(s),
659 caretPos(0), maxScreenSize(mss) {}*/
668 // * Window/fullscreen
674 // Function prototype
675 Window * CrashGracefullyCallback(void);
677 //NOTE: Probably should set a flag as well telling it to do a full reset
678 // of the Jaguar hardware if this happens...
679 void GUICrashGracefully(const char * reason)
681 finished = true; // We're finished for now!
683 // Since this is used in the menu code as well, we could create another
684 // internal function called "PushWindowOnQueue" or somesuch
686 event.type = SDL_USEREVENT;
687 event.user.code = MENU_ITEM_CHOSEN;
688 event.user.data1 = (void *)CrashGracefullyCallback;
689 SDL_PushEvent(&event);
692 Window * CrashGracefullyCallback(void)
694 Window * window = new Window(8, 16, 304, 192);
696 window->AddElement(new Text(8, 8+0*FONT_HEIGHT, "We CRASHED!!!"));