]> Shamusworld >> Repos - virtualjaguar/blob - src/gui.cpp
The GUI is coming!
[virtualjaguar] / src / gui.cpp
1 //
2 // GUI.CPP
3 //
4 // Graphical User Interface support
5 // by James L. Hammons
6 //
7
8 #include <dirent.h>
9 #include <SDL.h>
10 #include <string>
11 #include <vector>
12 #include <algorithm>
13 #include "types.h"
14 #include "settings.h"
15 #include "tom.h"
16 #include "video.h"
17 #include "font1.h"
18 #include "gui.h"
19
20 using namespace std;                                                            // For STL stuff
21
22 // Private function prototypes
23
24 void DrawTransparentBitmap(int16 * screen, uint32 x, uint32 y, uint16 * bitmap);
25 void DrawStringTrans(int16 * screen, uint32 x, uint32 y, uint16 color, uint8 opacity, const char * text, ...);
26 void DrawStringOpaque(int16 * screen, uint32 x, uint32 y, uint16 color1, uint16 color2, const char * text, ...);
27 void LoadROM(void);
28 void RunEmu(void);
29 void Quit(void);
30 void About(void);
31
32 // Local global variables
33
34 int mouseX, mouseY;
35
36 uint16 mousePic[] = {
37         6, 8,
38
39         0x03E0,0x0000,0x0000,0x0000,0x0000,0x0000,              // +
40         0x0300,0x03E0,0x0000,0x0000,0x0000,0x0000,              // @+
41         0x0300,0x03E0,0x03E0,0x0000,0x0000,0x0000,              // @++
42         0x0300,0x0300,0x03E0,0x03E0,0x0000,0x0000,              // @@++
43         0x0300,0x0300,0x03E0,0x03E0,0x03E0,0x0000,              // @@+++
44         0x0300,0x0300,0x0300,0x03E0,0x03E0,0x03E0,              // @@@+++
45         0x0300,0x0300,0x0300,0x0000,0x0000,0x0000,              // @@@
46         0x0300,0x0000,0x0000,0x0000,0x0000,0x0000               // @
47 /*
48         0xFFFF,0x0000,0x0000,0x0000,0x0000,0x0000,              // +
49         0xE318,0xFFFF,0x0000,0x0000,0x0000,0x0000,              // @+
50         0xE318,0xFFFF,0xFFFF,0x0000,0x0000,0x0000,              // @++
51         0xE318,0xE318,0xFFFF,0xFFFF,0x0000,0x0000,              // @@++
52         0xE318,0xE318,0xFFFF,0xFFFF,0xFFFF,0x0000,              // @@+++
53         0xE318,0xE318,0xE318,0xFFFF,0xFFFF,0xFFFF,              // @@@+++
54         0xE318,0xE318,0xE318,0x0000,0x0000,0x0000,              // @@@
55         0xE318,0x0000,0x0000,0x0000,0x0000,0x0000               // @
56 */
57 };
58 // 1 111 00 11 100 1 1100 -> F39C
59 // 1 100 00 10 000 1 0000 -> C210
60 // 1 110 00 11 000 1 1000 -> E318
61 // 0 000 00 11 111 0 0000 -> 03E0
62 // 0 000 00 11 000 0 0000 -> 0300
63
64 #define NUM_MENU_ITEMS          3
65 char * menu[NUM_MENU_ITEMS] = { "File", "Settings", "Options" };
66
67 char * menu1[4] = { "Load...", "Reset", "Run", "Quit" };
68 char * menu2[3] = { "Video...", "Audio...", "Misc..." };
69 char * menu3[1] = { "About..." };
70
71 char ** subMenu[NUM_MENU_ITEMS] = { menu1, menu2, menu3 };
72 uint8 subMenuNumItems[NUM_MENU_ITEMS] = { 4, 3, 1 };
73
74 void (* menu1Action[4])(void) = { LoadROM, NULL, RunEmu, Quit };
75 void (* menu2Action[3])(void) = { NULL, NULL, NULL };
76 void (* menu3Action[1])(void) = { About };
77 void (** subMenuAction[NUM_MENU_ITEMS])(void) = { menu1Action, menu2Action, menu3Action };
78
79 //
80 // Local GUI classes
81 //
82
83 class Element
84 {
85         public:
86                 virtual void HandleKey(SDLKey key) = 0;
87                 virtual void HandleMouseMove(uint32 x, uint32 y) = 0;
88                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) = 0;
89                 virtual void Draw(uint32, uint32) = 0;
90 //Needed?               virtual ~Element() = 0;
91 //We're not allocating anything in the base class, so the answer would be NO.
92                 bool Inside(uint32 x, uint32 y);
93                 // Class method
94                 static void SetScreenAndPitch(int16 * s, uint32 p) { screenBuffer = s, pitch = p; }
95
96         protected:
97                 SDL_Rect extents;
98                 // Class variables...
99                 static int16 * screenBuffer;
100                 static uint32 pitch;
101 };
102
103 int16 * Element::screenBuffer = NULL;
104 uint32 Element::pitch = 0;
105
106 bool Element::Inside(uint32 x, uint32 y)
107 {
108         return (x >= (uint32)extents.x && x < (uint32)(extents.x + extents.w)
109                 && y >= (uint32)extents.y && y < (uint32)(extents.y + extents.h) ? true : false);
110 }
111
112 class Button: public Element
113 {
114         public:
115                 Button(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): clicked(false),
116                         inside(false), fgColor(0xFFFF), bgColor(0x03E0)
117                         { extents.x = x, extents.y = y, extents.w = w, extents.h = h; }
118                 virtual void HandleKey(SDLKey key) {}
119                 virtual void HandleMouseMove(uint32 x, uint32 y);
120                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
121                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
122
123         protected:
124                 bool clicked, inside;
125                 uint16 fgColor, bgColor;
126 };
127
128 void Button::HandleMouseMove(uint32 x, uint32 y)
129 {
130         inside = Inside(x, y);
131 }
132
133 void Button::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
134 {
135         if (inside && mouseDown)
136                 clicked = true;
137         else
138                 clicked = false;
139 }
140
141 void Button::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
142 {
143         uint32 addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
144
145         for(uint32 y=0; y<extents.h; y++)
146         {
147                 for(uint32 x=0; x<extents.w; x++)
148                 {
149                         // Doesn't clip in y axis! !!! FIX !!!
150                         if (extents.x + x < pitch)
151                                 screenBuffer[addr + x + (y * pitch)] 
152                                         = (clicked ? fgColor : (inside ? 0x43F0 : bgColor));
153                 }
154         }
155 }
156
157 class Window: public Element
158 {
159         public:
160                 Window(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): /*clicked(false),
161                         inside(false),*/ fgColor(0x4FF0), bgColor(0xFE10)
162                         { extents.x = x, extents.y = y, extents.w = w, extents.h = h; }
163                 virtual void HandleKey(SDLKey key) {}
164                 virtual void HandleMouseMove(uint32 x, uint32 y);
165                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
166                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
167                 void AddElement(Element * e);
168
169         protected:
170 //              bool clicked, inside;
171                 uint16 fgColor, bgColor;
172                 vector<Element *> list;
173 };
174
175 void Window::HandleMouseMove(uint32 x, uint32 y)
176 {
177         // Handle the items this window contains...
178         for(uint32 i=0; i<list.size(); i++)
179                 // Make coords relative to upper right corner of this window...
180                 list[i]->HandleMouseMove(x - extents.x, y - extents.y);
181 }
182
183 void Window::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
184 {
185         // Handle the items this window contains...
186         for(uint32 i=0; i<list.size(); i++)
187                 // Make coords relative to upper right corner of this window...
188                 list[i]->HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
189 }
190
191 void Window::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
192 {
193         uint32 addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
194
195         for(uint32 y=0; y<extents.h; y++)
196         {
197                 for(uint32 x=0; x<extents.w; x++)
198                 {
199                         // Doesn't clip in y axis! !!! FIX !!!
200                         if (extents.x + x < pitch)
201                                 screenBuffer[addr + x + (y * pitch)] = bgColor;
202                 }
203         }
204
205         // Handle the items this window contains...
206         for(uint32 i=0; i<list.size(); i++)
207                 list[i]->Draw(extents.x, extents.y);
208 }
209
210 void Window::AddElement(Element * e)
211 {
212         list.push_back(e);
213 }
214
215 struct NameAction
216 {
217         string name;
218         void (* action)(void);
219         bool isWindow;
220
221         NameAction(string n, void (* a)(void) = NULL, bool w = false): name(n), action(a),
222                 isWindow(w) {}
223 };
224
225 class MenuItems
226 {
227         public:
228                 MenuItems(): charLength(0) {}
229
230                 string title;
231                 vector<NameAction> item;
232                 uint32 charLength;
233                 SDL_Rect extents;
234 };
235
236 class Menu: public Element
237 {
238         public:
239                 Menu(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 8,
240                         uint16 fgc = 0x1CFF, uint16 bgc = 0x000F, uint16 fgch = 0x421F,
241                         uint16 bgch = 0x1CFF): clicked(false), inside(0), fgColor(fgc), bgColor(bgc),
242                         fgColorHL(fgch), bgColorHL(bgch), menuChosen(-1), menuItemChosen(-1)
243                         { extents.x = x, extents.y = y, extents.w = w, extents.h = h; }
244                 virtual void HandleKey(SDLKey key);
245                 virtual void HandleMouseMove(uint32 x, uint32 y);
246                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
247                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
248                 void Add(MenuItems mi);
249
250         protected:
251                 bool clicked;
252                 uint32 inside;
253                 uint16 fgColor, bgColor, fgColorHL, bgColorHL;
254                 int menuChosen, menuItemChosen;
255
256         private:
257                 vector<MenuItems> itemList;
258 };
259
260 void Menu::HandleKey(SDLKey Key)
261 {
262 }
263
264 void Menu::HandleMouseMove(uint32 x, uint32 y)
265 {
266         if (!Inside(x, y))
267                 inside = 0;
268         else
269         {
270                 // Find out *where* we are inside the menu bar
271                 uint32 xpos = extents.x;
272
273                 for(uint32 i=0; i<itemList.size(); i++)
274                 {
275                         uint32 width = (itemList[i].title.length() + 2) * 8;
276
277                         if (x >= xpos && x < xpos + width)
278                         {
279                                 inside = i + 1;
280                                 break;
281                         }
282
283                         xpos += width;
284                 }
285         }
286 }
287
288 void Menu::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
289 {
290         if (inside && mouseDown)
291                 clicked = true, menuChosen = inside - 1;
292 //      else
293         if (!inside && mouseDown)
294                 clicked = false, menuChosen = -1;
295 }
296
297 void Menu::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
298 {
299         uint32 xpos = extents.x + offsetX;
300
301         for(uint32 i=0; i<itemList.size(); i++)
302         {
303                 uint16 color1 = fgColor, color2 = bgColor;
304                 if (inside == (i + 1) || (menuChosen != -1 && (uint32)menuChosen == i))
305                         color1 = fgColorHL, color2 = bgColorHL;
306
307                 DrawStringOpaque(screenBuffer, xpos, extents.y + offsetY, color1, color2,
308                         " %s ", itemList[i].title.c_str());
309                 xpos += (itemList[i].title.length() + 2) * 8;
310         }
311
312         // Draw sub menu
313         if (clicked)
314         {
315 //              menuItemChosen = -1;
316                 uint32 ypos = extents.y + 9;
317
318                 for(uint32 i=0; i<itemList[menuChosen].item.size(); i++)
319                 {
320                         uint16 color1 = fgColor, color2 = bgColor;
321
322 //This won't work...
323 //                      if (((uint32)mouseX >= menuXPos && (uint32)mouseX < menuXPos + (length + 2) * 8)
324 //                              && mouseY >= (extents.y + 9 + i * 8) && mouseY < (extents.y + 9 + (i + 1) * 8))
325 //                              color1 = fgColorHL, color2 = bgColorHL, menuItemChosen = i;
326
327                         DrawStringOpaque(screenBuffer, itemList[menuChosen].extents.x, ypos,
328                                 color1, color2, " %-*.*s ", itemList[menuChosen].charLength,
329                                 itemList[menuChosen].charLength, itemList[menuChosen].item[i].name.c_str());
330                         ypos += 8;
331                 }
332         }
333 }
334
335 void Menu::Add(MenuItems mi)
336 {
337         for(uint32 i=0; i<mi.item.size(); i++)
338                 if (mi.item[i].name.length() > mi.charLength)
339                         mi.charLength = mi.item[i].name.length();
340
341         // Set extents here as well...
342         mi.extents.x = extents.x + extents.w, mi.extents.y = extents.y + 9;
343         mi.extents.w = (mi.charLength + 2) * 8, mi.extents.h = mi.item.size() * 8;
344
345         itemList.push_back(mi);
346         extents.w += (mi.title.length() + 2) * 8;
347 }
348
349 class RootWindow: public Window
350 {
351         public:
352                 RootWindow(Menu * m, Window * w = NULL): menu(m), window(w) {}
353 //Do we even need to care about this crap?
354 //                      { extents.x = extents.y = 0, extents.w = 320, extents.h = 240; }
355                 virtual void HandleKey(SDLKey key) {}
356                 virtual void HandleMouseMove(uint32 x, uint32 y) {}
357                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
358                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0) {}
359
360         private:
361                 Menu * menu;
362                 Window * window;
363                 int16 * rootImage[1280 * 240 * 2];
364 };
365
366
367
368 //
369 // GUI stuff--it's not crunchy, it's GUI! ;-)
370 //
371
372 void InitGUI(void)
373 {
374         SDL_ShowCursor(SDL_DISABLE);
375         SDL_GetMouseState(&mouseX, &mouseY);
376 }
377
378 void GUIDone(void)
379 {
380 }
381
382 //
383 // Draw text at the given x/y coordinates. Can invert text as well.
384 //
385 void DrawString(int16 * screen, uint32 x, uint32 y, bool invert, const char * text, ...)
386 {
387         char string[4096];
388         va_list arg;
389
390         va_start(arg, text);
391         vsprintf(string, text, arg);
392         va_end(arg);
393
394         uint32 pitch = GetSDLScreenPitch() / 2;                 // Returns pitch in bytes but we need words...
395         uint32 length = strlen(string), address = x + (y * pitch);
396
397         for(uint32 i=0; i<length; i++)
398         {
399                 uint32 fontAddr = (uint32)string[i] * 64;
400
401                 for(uint32 yy=0; yy<8; yy++)
402                 {
403                         for(uint32 xx=0; xx<8; xx++)
404                         {
405                                 if ((font1[fontAddr] && !invert) || (!font1[fontAddr] && invert))
406                                         *(screen + address + xx + (yy * pitch)) = 0xFE00;
407                                 fontAddr++;
408                         }
409                 }
410
411                 address += 8;
412         }
413 }
414
415 //
416 // Draw text at the given x/y coordinates, using FG/BG colors.
417 //
418 void DrawStringOpaque(int16 * screen, uint32 x, uint32 y, uint16 color1, uint16 color2, const char * text, ...)
419 {
420         char string[4096];
421         va_list arg;
422
423         va_start(arg, text);
424         vsprintf(string, text, arg);
425         va_end(arg);
426
427         uint32 pitch = GetSDLScreenPitch() / 2;                 // Returns pitch in bytes but we need words...
428         uint32 length = strlen(string), address = x + (y * pitch);
429
430         for(uint32 i=0; i<length; i++)
431         {
432                 uint32 fontAddr = (uint32)string[i] * 64;
433
434                 for(uint32 yy=0; yy<8; yy++)
435                 {
436                         for(uint32 xx=0; xx<8; xx++)
437                         {
438                                 *(screen + address + xx + (yy * pitch)) = (font1[fontAddr] ? color1 : color2);
439                                 fontAddr++;
440                         }
441                 }
442
443                 address += 8;
444         }
445 }
446
447 //
448 // Draw text at the given x/y coordinates. Can invert text as well.
449 //
450 void DrawStringTrans(int16 * screen, uint32 x, uint32 y, uint16 color, uint8 trans, const char * text, ...)
451 {
452         char string[4096];
453         va_list arg;
454
455         va_start(arg, text);
456         vsprintf(string, text, arg);
457         va_end(arg);
458
459         uint32 pitch = GetSDLScreenPitch() / 2;                 // Returns pitch in bytes but we need words...
460         uint32 length = strlen(string), address = x + (y * pitch);
461
462         for(uint32 i=0; i<length; i++)
463         {
464                 uint32 fontAddr = (uint32)string[i] * 64;
465
466                 for(uint32 yy=0; yy<8; yy++)
467                 {
468                         for(uint32 xx=0; xx<8; xx++)
469                         {
470                                 if (font1[fontAddr])
471                                 {
472                                         uint16 existingColor = *(screen + address + xx + (yy * pitch));
473
474                                         uint8 eRed = (existingColor >> 10) & 0x1F,
475                                                 eGreen = (existingColor >> 5) & 0x1F,
476                                                 eBlue = existingColor & 0x1F,
477 //This could be done ahead of time, instead of on each pixel...
478                                                 nRed = (color >> 10) & 0x1F,
479                                                 nGreen = (color >> 5) & 0x1F,
480                                                 nBlue = color & 0x1F;
481
482 //This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
483 //Here we've modified it to have 33 levels of transparency (could have any # we want!)
484 //because dividing by 32 is faster than dividing by 31!
485                                         uint8 invTrans = 32 - trans;
486                                         uint16 bRed = (eRed * trans + nRed * invTrans) / 32;
487                                         uint16 bGreen = (eGreen * trans + nGreen * invTrans) / 32;
488                                         uint16 bBlue = (eBlue * trans + nBlue * invTrans) / 32;
489
490                                         uint16 blendedColor = (bRed << 10) | (bGreen << 5) | bBlue;
491
492                                         *(screen + address + xx + (yy * pitch)) = blendedColor;
493                                 }
494
495                                 fontAddr++;
496                         }
497                 }
498
499                 address += 8;
500         }
501 }
502
503 //
504 // GUI Main loop
505 //
506 enum { GUI_TOP_MENU, GUI_MENU_CLICKED, GUI_WINDOW };
507 bool GUIMain(void)
508 {
509         extern int16 * backbuffer;
510         bool done = false;
511         SDL_Event event;
512         int32 menuChosen = -1;
513         uint32 menuXPos = 0;
514         int32 menuItemChosen = -1;
515         uint32 GUIState = GUI_TOP_MENU;
516
517         Element::SetScreenAndPitch(backbuffer, GetSDLScreenPitch() / 2);
518
519         Button closeButton(45, 90, 16, 16);
520         Window someWindow(15, 16, 60, 60);
521         Button button1(50, 1, 9, 9), button2(10, 10, 8, 8), button3(25, 48, 15, 8);
522         someWindow.AddElement(&button1);
523         someWindow.AddElement(&button2);
524         someWindow.AddElement(&button3);
525
526         MenuItems mi;
527         Menu mainMenu(0, 160);
528         mi.title = "File";
529         mi.item.push_back(NameAction("Load...", LoadROM));
530         mi.item.push_back(NameAction("Reset"));
531         mi.item.push_back(NameAction("Run", RunEmu));
532         mi.item.push_back(NameAction(""));
533         mi.item.push_back(NameAction("Quit", Quit));
534         mainMenu.Add(mi);
535         mi.title = "Settings";
536         mi.item.clear();
537         mainMenu.Add(mi);
538         mi.title = "Options";
539         mainMenu.Add(mi);
540
541         bool showMouse = true;
542
543 //This is crappy!!! !!! FIX !!!
544         jaguar_reset();
545
546         while (!done)
547         {
548                 while (SDL_PollEvent(&event))
549                 {
550                         if (event.type == SDL_ACTIVEEVENT)
551                         {
552                                 if (event.active.state == SDL_APPMOUSEFOCUS)
553                                         showMouse = (event.active.gain ? true : false);
554                         }
555                         if (event.type == SDL_KEYDOWN)
556                         {
557 //                              if (event.key.keysym.sym == SDLK_ESCAPE)
558 //                                      done = true;
559 //                                      return false;
560                         }
561                         else if (event.type == SDL_MOUSEMOTION)
562                         {
563                                 mouseX = event.motion.x, mouseY = event.motion.y;
564
565                                 if (vjs.useOpenGL)
566                                         mouseX /= 2, mouseY /= 2;
567
568                                 closeButton.HandleMouseMove(mouseX, mouseY);
569                                 someWindow.HandleMouseMove(mouseX, mouseY);
570                                 mainMenu.HandleMouseMove(mouseX, mouseY);
571                         }
572                         else if (event.type == SDL_MOUSEBUTTONDOWN)
573                         {
574                                 uint32 mx = event.button.x, my = event.button.y;
575                                 if (vjs.useOpenGL)
576                                         mx /= 2, my /= 2;
577
578                                 // Handle that click!
579                                 if (GUIState == GUI_TOP_MENU)
580                                 {
581                                         if (menuChosen != -1)
582                                                 GUIState = GUI_MENU_CLICKED;
583                                 }
584                                 else if (GUIState == GUI_MENU_CLICKED)
585                                 {
586                                         if (menuItemChosen != -1)
587                                         {
588 //                                              GUIState = GUI_WINDOW;
589                                                 if (subMenuAction[menuChosen][menuItemChosen] != NULL)
590                                                         subMenuAction[menuChosen][menuItemChosen]();
591                                         }
592 //                                      else
593                                                 GUIState = GUI_TOP_MENU;
594                                 }
595
596                                 closeButton.HandleMouseButton(mx, my, true);
597                                 someWindow.HandleMouseButton(mx, my, true);
598                                 mainMenu.HandleMouseButton(mx, my, true);
599                         }
600                         else if (event.type == SDL_MOUSEBUTTONUP)
601                         {
602                                 uint32 mx = event.button.x, my = event.button.y;
603
604                                 if (vjs.useOpenGL)
605                                         mx /= 2, my /= 2;
606
607                                 closeButton.HandleMouseButton(mx, my, false);
608                                 someWindow.HandleMouseButton(mx, my, false);
609                                 mainMenu.HandleMouseButton(mx, my, false);
610                         }
611
612                         // Draw the GUI...
613
614 // The way we do things here is kinda stupid (redrawing the screen every frame), but
615 // it's simple. Perhaps there may be a reason down the road to be more selective with
616 // our clearing, but for now, this will suffice.
617                         memset(backbuffer, 0x11, tom_getVideoModeWidth() * 240 * 2);
618
619                         closeButton.Draw();
620                         someWindow.Draw();
621                         mainMenu.Draw();
622
623                         // We always draw the top level menu...
624                         if (GUIState == GUI_TOP_MENU)
625                                 menuChosen = -1;
626
627                         uint32 xpos = 0;
628                         for(uint32 i=0; i<NUM_MENU_ITEMS; i++)
629                         {
630                                 uint16 colorFG = 0x1CFF, colorBG = 0x000F;
631                                 uint32 length = strlen(menu[i]) + 2;
632
633                                 if (((uint32)mouseX >= xpos && (uint32)mouseX < xpos + length * 8)
634                                         && mouseY < 8)
635                                         colorFG = 0x421F, colorBG = 0x1CFF, menuChosen = i, menuXPos = xpos;
636
637                                 if (GUIState != GUI_TOP_MENU && i == (uint32)menuChosen)
638                                         colorFG = 0x421F, colorBG = 0x1CFF;
639 // BG: 0 00011 00011 11111 -> 0000 1100 0111 1111
640 // FG: 0 10000 10000 11111 -> 0100 0010 0001 1111
641                                 DrawStringOpaque(backbuffer, xpos, 0, colorFG, colorBG, " %s ", menu[i]);
642                                 xpos += length * 8;
643                         }
644
645                         // We don't always draw the submenus...
646                         if (GUIState == GUI_MENU_CLICKED)
647                         {
648                                 menuItemChosen = -1;
649                                 uint32 length = 0;
650                                 for(int i=0; i<subMenuNumItems[menuChosen]; i++)
651                                         if (strlen(subMenu[menuChosen][i]) > length)
652                                                 length = strlen(subMenu[menuChosen][i]);
653
654                                 uint32 ypos = 9;
655                                 for(int i=0; i<subMenuNumItems[menuChosen]; i++)
656                                 {
657                                         uint16 colorFG = 0x1CFF, colorBG = 0x000F;
658
659                                         if (((uint32)mouseX >= menuXPos && (uint32)mouseX < menuXPos + (length + 2) * 8)
660                                                 && mouseY >= (9 + i * 8) && mouseY < (9 + (i + 1) * 8))
661                                                 colorFG = 0x421F, colorBG = 0x1CFF, menuItemChosen = i;
662
663                                         DrawStringOpaque(backbuffer, menuXPos, ypos, colorFG, colorBG, " %-*.*s ", length, length, subMenu[menuChosen][i]);
664                                         ypos += 8;
665                                 }
666                         }
667                         // Windows? Isn't that an illegal monopoly or something? ;-)
668                         else if (GUIState == GUI_WINDOW)
669                         {
670                         }
671
672                         if (showMouse)
673                                 DrawTransparentBitmap(backbuffer, mouseX, mouseY, mousePic);
674
675                         RenderBackbuffer();
676                 }
677         }
678
679         return true;
680 }
681
682 //
683 // GUI "action" functions
684 //
685 void LoadROM(void)
686 {
687 }
688
689 void RunEmu(void)
690 {
691 //This is crappy... !!! FIX !!!
692         extern int16 * backbuffer;
693         extern bool finished;
694         extern bool showGUI;
695         uint32 nFrame = 0, nFrameskip = 0;
696         uint32 totalFrames = 0;
697         finished = false;
698         bool showMessage = true;
699         uint32 showMsgFrames = 60;
700         uint8 transparency = 0;
701
702         while (!finished)
703         {
704                 // Set up new backbuffer with new pixels and data
705                 JaguarExecute(backbuffer, true);
706                 totalFrames++;
707 //WriteLog("Frame #%u...\n", totalFrames);
708 //extern bool doDSPDis;
709 //if (totalFrames == 373)
710 //      doDSPDis = true;
711
712                 // Some QnD GUI stuff here...
713                 if (showGUI)
714                 {
715                         extern uint32 gpu_pc, dsp_pc;
716                         DrawString(backbuffer, 8, 8, false, "GPU PC: %08X", gpu_pc);
717                         DrawString(backbuffer, 8, 16, false, "DSP PC: %08X", dsp_pc);
718                 }
719
720                 if (showMessage)
721                 {
722                         DrawStringTrans(backbuffer, 8, 24*8, 0xFF0F, transparency, "Running...");
723
724                         if (showMsgFrames == 0)
725                         {                       
726                                 transparency++;
727
728                                 if (transparency == 33)
729                                         showMessage = false;
730                         }
731                         else
732                                 showMsgFrames--;
733                 }
734
735                 // Simple frameskip
736                 if (nFrame == nFrameskip)
737                 {
738                         RenderBackbuffer();
739                         nFrame = 0;
740                 }
741                 else
742                         nFrame++;
743
744                 joystick_exec();
745         }
746 }
747
748 void Quit(void)
749 {
750         WriteLog("GUI: Quitting due to user request.\n");
751         log_done();
752         exit(0);
753 }
754
755 void About(void)
756 {
757         extern int16 * backbuffer;
758         SDL_Event event;
759         uint16 * bgSave = (uint16 *)malloc(tom_getVideoModeWidth() * 240 * 2);
760         memcpy(bgSave, backbuffer, tom_getVideoModeWidth() * 240 * 2);
761
762         bool done = false;
763         while (!done)
764         {
765                 while (SDL_PollEvent(&event))
766                 {
767                         if (event.type == SDL_KEYDOWN)
768                         {
769                                 if (event.key.keysym.sym == SDLK_ESCAPE || event.key.keysym.sym == SDLK_RETURN)
770                                         done = true;
771                         }
772                         else if (event.type == SDL_MOUSEMOTION)
773                         {
774                                 mouseX = event.motion.x, mouseY = event.motion.y;
775                                 if (vjs.useOpenGL)
776                                         mouseX /= 2, mouseY /= 2;
777                         }
778                         else if (event.type == SDL_MOUSEBUTTONDOWN)
779                         {
780                                 uint32 mx = event.button.x, my = event.button.y;
781                                 if (vjs.useOpenGL)
782                                         mx /= 2, my /= 2;
783
784                                 done = true;
785                         }
786
787                         // Draw "About" box
788                         memcpy(backbuffer, bgSave, tom_getVideoModeWidth() * 240 * 2);
789
790                         DrawStringOpaque(backbuffer, 64, 64, 0x1CFF, 0x000F, "                              ");
791                         DrawStringOpaque(backbuffer, 64, 72, 0x1CFF, 0x000F, " Virtual Jaguar by JLH & crew ");
792                         DrawStringOpaque(backbuffer, 64, 80, 0x1CFF, 0x000F, "                              ");
793
794                         DrawTransparentBitmap(backbuffer, mouseX, mouseY, mousePic);
795
796                         RenderBackbuffer();
797                 }
798         }
799
800         free(bgSave);
801 }
802
803 //
804 // Draw "picture"
805 // Uses zero as transparent color
806 //
807 void DrawTransparentBitmap(int16 * screen, uint32 x, uint32 y, uint16 * bitmap)
808 {
809         uint16 width = bitmap[0], height = bitmap[1];
810         bitmap += 2;
811
812         uint32 pitch = GetSDLScreenPitch() / 2;                 // Returns pitch in bytes but we need words...
813         uint32 address = x + (y * pitch);
814
815         for(int yy=0; yy<height; yy++)
816         {
817                 for(int xx=0; xx<width; xx++)
818                 {
819                                 if (*bitmap && x + xx < pitch)          // NOTE: Still doesn't clip the Y val...
820                                         *(screen + address + xx + (yy * pitch)) = *bitmap;
821                                 bitmap++;
822                 }
823         }
824 }
825
826 //
827 // Very very crude GUI file selector
828 //
829 bool UserSelectFile(char * path, char * filename)
830 {
831 //Testing...
832 //GUIMain();
833         
834         extern int16 * backbuffer;
835         vector<string> fileList;
836
837         // Read in the candidate files from the directory pointed to by "path"
838
839         DIR * dp = opendir(path);
840         dirent * de;
841
842         while ((de = readdir(dp)) != NULL)
843         {
844                 char * ext = strrchr(de->d_name, '.');
845
846                 if (ext != NULL)
847                         if (stricmp(ext, ".zip") == 0 || stricmp(ext, ".jag") == 0)
848                                 fileList.push_back(string(de->d_name));
849         }
850
851         closedir(dp);
852
853         // Main GUI selection loop
854
855         uint32 cursor = 0, startFile = 0;
856
857         if (fileList.size() > 1)        // Only go GUI if more than one possibility!
858         {
859                 sort(fileList.begin(), fileList.end());
860
861                 bool done = false;
862                 uint32 limit = (fileList.size() > 30 ? 30 : fileList.size());
863                 SDL_Event event;
864
865                 // Ensure that the GUI is drawn before any user input...
866                 event.type = SDL_USEREVENT;
867                 SDL_PushEvent(&event);
868
869                 while (!done)
870                 {
871                         while (SDL_PollEvent(&event))
872                         {
873                                 if (event.type == SDL_KEYDOWN)
874                                 {
875                                         SDLKey key = event.key.keysym.sym;
876
877                                         if (key == SDLK_DOWN)
878                                         {
879                                                 if (cursor != limit - 1)        // Cursor is within its window
880                                                         cursor++;
881                                                 else                                            // Otherwise, scroll the window...
882                                                 {
883                                                         if (cursor + startFile != fileList.size() - 1)
884                                                                 startFile++;
885                                                 }
886                                         }
887                                         if (key == SDLK_UP)
888                                         {
889                                                 if (cursor != 0)
890                                                         cursor--;
891                                                 else
892                                                 {
893                                                         if (startFile != 0)
894                                                                 startFile--;
895                                                 }
896                                         }
897                                         if (key == SDLK_PAGEDOWN)
898                                         {
899                                                 if (cursor != limit - 1)
900                                                         cursor = limit - 1;
901                                                 else
902                                                 {
903                                                         startFile += limit;
904                                                         if (startFile > fileList.size() - limit)
905                                                                 startFile = fileList.size() - limit;
906                                                 }
907                                         }
908                                         if (key == SDLK_PAGEUP)
909                                         {
910                                                 if (cursor != 0)
911                                                         cursor = 0;
912                                                 else
913                                                 {
914                                                         if (startFile < limit)
915                                                                 startFile = 0;
916                                                         else
917                                                                 startFile -= limit;
918                                                 }
919                                         }
920                                         if (key == SDLK_RETURN)
921                                                 done = true;
922                                         if (key == SDLK_ESCAPE)
923                                         {
924                                                 WriteLog("GUI: Aborting VJ by user request.\n");
925                                                 return false;                                           // Bail out!
926                                         }
927                                         if (key >= SDLK_a && key <= SDLK_z)
928                                         {
929                                                 // Advance cursor to filename with first letter pressed...
930                                                 uint8 which = (key - SDLK_a) + 65;      // Convert key to A-Z char
931
932                                                 for(uint32 i=0; i<fileList.size(); i++)
933                                                 {
934                                                         if ((fileList[i][0] & 0xDF) == which)
935                                                         {
936                                                                 cursor = i - startFile;
937                                                                 if (i > startFile + limit - 1)
938                                                                         startFile = i - limit + 1,
939                                                                         cursor = limit - 1;
940                                                                 if (i < startFile)
941                                                                         startFile = i,
942                                                                         cursor = 0;
943                                                                 break;
944                                                         }
945                                                 }
946                                         }
947                                 }
948                                 else if (event.type == SDL_MOUSEMOTION)
949                                 {
950                                         mouseX = event.motion.x, mouseY = event.motion.y;
951                                         if (vjs.useOpenGL)
952                                                 mouseX /= 2, mouseY /= 2;
953                                 }
954                                 else if (event.type == SDL_MOUSEBUTTONDOWN)
955                                 {
956                                         uint32 mx = event.button.x, my = event.button.y;
957                                         if (vjs.useOpenGL)
958                                                 mx /= 2, my /= 2;
959                                         cursor = my / 8;
960                                 }
961
962                                 // Draw the GUI...
963 //                              memset(backbuffer, 0x11, tom_getVideoModeWidth() * tom_getVideoModeHeight() * 2);
964                                 memset(backbuffer, 0x11, tom_getVideoModeWidth() * 240 * 2);
965
966                                 for(uint32 i=0; i<limit; i++)
967                                 {
968                                         // Clip our strings to guarantee that they fit on the screen...
969                                         // (and strip off the extension too)
970                                         string s(fileList[startFile + i], 0, fileList[startFile + i].length() - 4);
971                                         if (s.length() > 38)
972                                                 s[38] = 0;
973                                         DrawString(backbuffer, 0, i*8, (cursor == i ? true : false), " %s ", s.c_str());
974                                 }
975
976                                 DrawTransparentBitmap(backbuffer, mouseX, mouseY, mousePic);
977
978                                 RenderBackbuffer();
979                         }
980                 }
981         }
982
983         strcpy(filename, path);
984
985         if (strlen(path) > 0)
986                 if (path[strlen(path) - 1] != '/')
987                         strcat(filename, "/");
988
989         strcat(filename, fileList[startFile + cursor].c_str());
990
991         return true;
992 }