]> Shamusworld >> Repos - virtualjaguar/blob - src/gui.cpp
a8805c9e5f226ed2545ca0e590dd0e84d9b72454
[virtualjaguar] / src / gui.cpp
1 //
2 // GUI.CPP
3 //
4 // Graphical User Interface support
5 // by James L. Hammons
6 //
7
8 #include "gui.h"
9
10 #include <algorithm>
11 #include <ctype.h>                                                                      // For toupper()
12 #include <SDL.h>
13 #include <string>
14 #include <sys/types.h>                                                          // For MacOS <dirent.h> dependency
15 #include <dirent.h>
16 #include <vector>
17 #include "crc32.h"
18 #include "event.h"
19 #include "file.h"
20 #include "font1.h"
21 #include "font14pt.h"                                                           // Also 15, 16, 17, 18
22 #include "guielements.h"
23 #include "jaguar.h"
24 #include "log.h"
25 #include "sdlemu_opengl.h"
26 #include "settings.h"
27 #include "tom.h"
28 #include "video.h"
29
30 using namespace std;                                                            // For STL stuff
31
32 // Private function prototypes
33
34 class Window;                                                                           // Forward declaration...
35
36 //void DrawTransparentBitmap(uint32 * screen, uint32 x, uint32 y, uint32 * bitmap, uint8 * alpha = NULL);
37 void DrawTransparentBitmapDeprecated(uint32 * screen, uint32 x, uint32 y, uint32 * bitmap);
38 void DrawTransparentBitmap(uint32 * screen, uint32 x, uint32 y, const void * bitmap);
39 void DrawBitmap(uint32 * screen, uint32 x, uint32 y, const void * bitmap);
40 //Should call this FillScreenRectangle with a number representing the RGBA value to fill. !!! FIX !!!
41 //void ClearScreenRectangle(uint32 * screen, uint32 x, uint32 y, uint32 w, uint32 h);
42 void FillScreenRectangle(uint32 * screen, uint32 x, uint32 y, uint32 w, uint32 h, uint32 color);
43 void DrawStringTrans(uint32 * screen, uint32 x, uint32 y, uint32 color, uint8 opacity, const char * text, ...);
44 void DrawStringOpaque(uint32 * screen, uint32 x, uint32 y, uint32 color1, uint32 color2, const char * text, ...);
45 void DrawString(uint32 * screen, uint32 x, uint32 y, bool invert, const char * text, ...);
46 void DrawString2(uint32 * screen, uint32 x, uint32 y, uint32 color, uint8 transparency, const char * text, ...);
47 Window * LoadROM(void);
48 Window * ResetJaguar(void);
49 Window * ResetJaguarCD(void);
50 Window * RunEmu(void);
51 Window * Quit(void);
52 Window * About(void);
53 Window * MiscOptions(void);
54
55 // Local global variables
56
57 bool showGUI = false;
58 bool exitGUI = false;                                                           // GUI (emulator) done variable
59 int mouseX = 0, mouseY = 0;
60 uint32 background[1280 * 256];                                          // GUI background buffer
61 bool showMessage = false;
62 //uint32 showMessageTimeout;
63 //char messageBuffer[200];
64 bool finished = false;
65
66 const char separator[] = "--------------------------------------------------------";
67
68 //
69 // Case insensitive string compare function
70 // Taken straight out of Thinking In C++ by Bruce Eckel. Thanks Bruce!
71 //
72
73 int stringCmpi(const string &s1, const string &s2)
74 {
75         // Select the first element of each string:
76         string::const_iterator p1 = s1.begin(), p2 = s2.begin();
77
78         while (p1 != s1.end() && p2 != s2.end())                // Don�t run past the end
79         {
80                 if (toupper(*p1) != toupper(*p2))                       // Compare upper-cased chars
81                         return (toupper(*p1) < toupper(*p2) ? -1 : 1);// Report which was lexically greater
82
83                 p1++;
84                 p2++;
85         }
86
87         // If they match up to the detected eos, say which was longer. Return 0 if the same.
88         return s2.size() - s1.size();
89 }
90
91 //
92 // Local GUI classes
93 //
94
95 enum { WINDOW_CLOSE, MENU_ITEM_CHOSEN };
96
97 class Element
98 {
99         public:
100                 Element(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0)
101                         { extents.x = x, extents.y = y, extents.w = w, extents.h = h; }
102                 virtual void HandleKey(SDLKey key) = 0;         // These are "pure" virtual functions...
103                 virtual void HandleMouseMove(uint32 x, uint32 y) = 0;
104                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) = 0;
105                 virtual void Draw(uint32, uint32) = 0;
106                 virtual void Notify(Element *) = 0;
107 //Needed?               virtual ~Element() = 0;
108 //We're not allocating anything in the base class, so the answer would be NO.
109                 bool Inside(uint32 x, uint32 y);
110                 // Class method
111 //              static void SetScreenAndPitch(int16 * s, uint32 p) { screenBuffer = s, pitch = p; }
112                 static void SetScreenAndPitch(uint32 * s, uint32 p) { screenBuffer = s, pitch = p; }
113
114         protected:
115                 SDL_Rect extents;
116                 uint32 state;
117                 // Class variables...
118 //              static int16 * screenBuffer;
119                 static uint32 * screenBuffer;
120                 static uint32 pitch;
121 };
122
123 // Initialize class variables (Element)
124 //int16 * Element::screenBuffer = NULL;
125 uint32 * Element::screenBuffer = NULL;
126 uint32 Element::pitch = 0;
127
128 bool Element::Inside(uint32 x, uint32 y)
129 {
130         return (x >= (uint32)extents.x && x < (uint32)(extents.x + extents.w)
131                 && y >= (uint32)extents.y && y < (uint32)(extents.y + extents.h) ? true : false);
132 }
133
134
135 //
136 // Button class
137 //
138
139 class Button: public Element
140 {
141         public:
142                 Button(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
143                         activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
144                         bgColor(0xFF00FF00), pic(NULL), elementToTell(NULL) {}
145                 Button(uint32 x, uint32 y, uint32 w, uint32 h, uint32 * p): Element(x, y, w, h),
146                         activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
147                         bgColor(0xFF00FF00), pic(p), elementToTell(NULL) {}
148 //              Button(uint32 x, uint32 y, uint32 * p): Element(x, y, 0, 0),
149                 Button(uint32 x, uint32 y, uint32 * p, uint32 * pH = NULL, uint32 * pD = NULL): Element(x, y, 0, 0),
150                         activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
151                         bgColor(0xFF00FF00), pic(p), picHover(pH), picDown(pD), elementToTell(NULL)
152                         { if (pic) extents.w = pic[0], extents.h = pic[1]; }
153                 Button(uint32 x, uint32 y, uint32 w, uint32 h, string s): Element(x, y, w, h),
154                         activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
155                         bgColor(0xFF00FF00), pic(NULL), text(s), elementToTell(NULL) {}
156                 Button(uint32 x, uint32 y, string s): Element(x, y, 0, FONT_HEIGHT),
157                         activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
158                         bgColor(0xFF00FF00), pic(NULL), text(s), elementToTell(NULL)
159                         { extents.w = s.length() * FONT_WIDTH; }
160                 virtual void HandleKey(SDLKey key) {}
161                 virtual void HandleMouseMove(uint32 x, uint32 y);
162                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
163                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
164                 virtual void Notify(Element *) {}
165                 bool ButtonClicked(void) { return activated; }
166                 void SetNotificationElement(Element * e) { elementToTell = e; }
167
168         protected:
169                 bool activated, clicked, inside;
170                 uint32 fgColor, bgColor;
171                 uint32 * pic, * picHover, * picDown;
172                 string text;
173                 Element * elementToTell;
174 };
175
176 void Button::HandleMouseMove(uint32 x, uint32 y)
177 {
178         inside = Inside(x, y);
179 }
180
181 void Button::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
182 {
183         if (inside)
184         {
185                 if (mouseDown)
186                         clicked = true;
187
188                 if (clicked && !mouseDown)
189                 {
190                         clicked = false, activated = true;
191
192                         // Send a message that we're activated (if there's someone to tell, that is)
193                         if (elementToTell)
194                                 elementToTell->Notify(this);
195                 }
196         }
197         else
198                 clicked = activated = false;
199 }
200
201 void Button::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
202 {
203         uint32 addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
204
205         if (text.length() > 0)                                                  // Simple text button
206 //      if (pic == NULL)
207         {
208                 for(uint32 y=0; y<extents.h; y++)
209                 {
210                         for(uint32 x=0; x<extents.w; x++)
211                         {
212                                 // Doesn't clip in y axis! !!! FIX !!!
213                                 if (extents.x + x < pitch)
214                                         screenBuffer[addr + x + (y * pitch)]
215 //                                      = (clicked && inside ? fgColor : (inside ? 0x43F0 : bgColor));
216 //43F0 -> 010000 11111 10000 -> 0100 0001 1111 1111 1000 0100 -> 41 FF 84
217                                                 = (clicked && inside ? fgColor : (inside ? 0xFF84FF41 : bgColor));
218                         }
219                 }
220
221                 DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY, false, "%s", text.c_str());
222         }
223         else                                                                                    // Graphical button
224         {
225                 uint32 * picToShow = pic;
226
227                 if (picHover != NULL && inside && !clicked)
228                         picToShow = picHover;
229
230                 if (picDown != NULL && inside && clicked)
231                         picToShow = picDown;
232
233                 DrawTransparentBitmapDeprecated(screenBuffer, extents.x + offsetX, extents.y + offsetY, picToShow);
234         }
235 }
236
237
238 //
239 // PushButton class
240 //
241
242 class PushButton: public Element
243 {
244 // How to handle?
245 // Save state externally?
246 //We pass in a state variable if we want to track it externally, otherwise we use our own
247 //internal state var. Still need to do some kind of callback for pushbuttons that do things
248 //like change from fullscreen to windowed... !!! FIX !!!
249
250         public:
251 //              PushButton(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
252 //                      activated(false), clicked(false), inside(false), fgColor(0xFFFF),
253 //                      bgColor(0x03E0), pic(NULL), elementToTell(NULL) {}
254 //              PushButton(uint32 x, uint32 y, bool * st, string s): Element(x, y, 8, 8), state(st),
255 //                      inside(false), text(s) { if (st == NULL) state = &internalState; }
256                 PushButton(uint32 x, uint32 y, bool * st, string s): Element(x, y, 16, 16), state(st),
257                         inside(false), text(s) { if (st == NULL) state = &internalState; }
258 /*              Button(uint32 x, uint32 y, uint32 w, uint32 h, uint32 * p): Element(x, y, w, h),
259                         activated(false), clicked(false), inside(false), fgColor(0xFFFF),
260                         bgColor(0x03E0), pic(p), elementToTell(NULL) {}
261                 Button(uint32 x, uint32 y, uint32 * p): Element(x, y, 0, 0),
262                         activated(false), clicked(false), inside(false), fgColor(0xFFFF),
263                         bgColor(0x03E0), pic(p), elementToTell(NULL)
264                         { if (pic) extents.w = pic[0], extents.h = pic[1]; }
265                 Button(uint32 x, uint32 y, uint32 w, uint32 h, string s): Element(x, y, w, h),
266                         activated(false), clicked(false), inside(false), fgColor(0xFFFF),
267                         bgColor(0x03E0), pic(NULL), text(s), elementToTell(NULL) {}
268                 PushButton(uint32 x, uint32 y, string s): Element(x, y, 0, 8),
269                         activated(false), clicked(false), inside(false), fgColor(0xFFFF),
270                         bgColor(0x03E0), pic(NULL), text(s), elementToTell(NULL)
271                         { extents.w = s.length() * 8; }*/
272                 virtual void HandleKey(SDLKey key) {}
273                 virtual void HandleMouseMove(uint32 x, uint32 y);
274                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
275                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
276                 virtual void Notify(Element *) {}
277 //              bool ButtonClicked(void) { return activated; }
278 //              void SetNotificationElement(Element * e) { elementToTell = e; }
279
280         protected:
281                 bool * state;
282                 bool inside;
283 //              bool activated, clicked, inside;
284 //              uint16 fgColor, bgColor;
285 //              uint32 * pic;
286                 string text;
287 //              Element * elementToTell;
288                 bool internalState;
289 };
290
291 void PushButton::HandleMouseMove(uint32 x, uint32 y)
292 {
293         inside = Inside(x, y);
294 }
295
296 void PushButton::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
297 {
298         if (inside && mouseDown)
299         {
300 /*              if (mouseDown)
301                         clicked = true;
302
303                 if (clicked && !mouseDown)
304                 {
305                         clicked = false, activated = true;
306
307                         // Send a message that we're activated (if there's someone to tell, that is)
308                         if (elementToTell)
309                                 elementToTell->Notify(this);
310                 }*/
311                 *state = !(*state);
312         }
313 //      else
314 //              clicked = activated = false;
315 }
316
317 void PushButton::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
318 {
319 /*      uint32 addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
320
321         for(uint32 y=0; y<extents.h; y++)
322         {
323                 for(uint32 x=0; x<extents.w; x++)
324                 {
325                         // Doesn't clip in y axis! !!! FIX !!!
326                         if (extents.x + x < pitch)
327                                 screenBuffer[addr + x + (y * pitch)]
328                                         = (clicked && inside ? fgColor : (inside ? 0x43F0 : bgColor));
329                 }
330         }*/
331
332         if (*state)
333                 DrawTransparentBitmap(screenBuffer, extents.x + offsetX, extents.y + offsetY, &pbDown);
334         else
335                 DrawTransparentBitmap(screenBuffer, extents.x + offsetX, extents.y + offsetY, &pbUp);
336
337         if (text.length() > 0)
338                 DrawString(screenBuffer, extents.x + offsetX + 24, extents.y + offsetY, false, "%s", text.c_str());
339 }
340
341
342 //
343 // SlideSwitch class
344 //
345
346 class SlideSwitch: public Element
347 {
348 // How to handle?
349 // Save state externally?
350 //Seems to be handled the same as PushButton, but without sanity checks. !!! FIX !!!
351
352         public:
353                 SlideSwitch(uint32 x, uint32 y, bool * st, string s1, string s2): Element(x, y, 16, 32), state(st),
354                         inside(false), text1(s1), text2(s2) {}
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                 virtual void Notify(Element *) {}
360 //              bool ButtonClicked(void) { return activated; }
361 //              void SetNotificationElement(Element * e) { elementToTell = e; }
362
363         protected:
364                 bool * state;
365                 bool inside;
366 //              bool activated, clicked, inside;
367 //              uint16 fgColor, bgColor;
368 //              uint32 * pic;
369                 string text1, text2;
370 //              Element * elementToTell;
371 };
372
373 void SlideSwitch::HandleMouseMove(uint32 x, uint32 y)
374 {
375         inside = Inside(x, y);
376 }
377
378 void SlideSwitch::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
379 {
380         if (inside && mouseDown)
381         {
382 /*              if (mouseDown)
383                         clicked = true;
384
385                 if (clicked && !mouseDown)
386                 {
387                         clicked = false, activated = true;
388
389                         // Send a message that we're activated (if there's someone to tell, that is)
390                         if (elementToTell)
391                                 elementToTell->Notify(this);
392                 }*/
393                 *state = !(*state);
394         }
395 //      else
396 //              clicked = activated = false;
397 }
398
399 void SlideSwitch::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
400 {
401         DrawTransparentBitmapDeprecated(screenBuffer, extents.x + offsetX, extents.y + offsetY, (*state ? slideSwitchDown : slideSwitchUp));
402
403         if (text1.length() > 0)
404                 DrawString(screenBuffer, extents.x + offsetX + 24, extents.y + offsetY, false, "%s", text1.c_str());
405
406         if (text2.length() > 0)
407                 DrawString(screenBuffer, extents.x + offsetX + 24, extents.y + offsetY + 16, false, "%s", text2.c_str());
408 }
409
410
411 //
412 // Window class
413 //
414
415 class Window: public Element
416 {
417         public:
418 /*              Window(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
419                         fgColor(0x4FF0), bgColor(0xFE10)
420                         { close = new Button(w - 8, 1, closeBox); list.push_back(close); }*/
421                 Window(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0,
422                         void (* f)(Element *) = NULL): Element(x, y, w, h),
423 //                      /*clicked(false), inside(false),*/ fgColor(0x4FF0), bgColor(0x1E10),
424 //4FF0 -> 010011 11111 10000 -> 0100 1101 1111 1111 1000 0100 -> 4D FF 84
425 //1E10 -> 000111 10000 10000 -> 0001 1111 1000 0100 1000 0100 -> 1F 84 84
426                         /*clicked(false), inside(false),*/ fgColor(0xFF84FF4D), bgColor(0xFF84841F),
427                         handler(f)
428                         { close = new Button(w - (CLOSEBOX_WIDTH + 1), 1, closeBox, closeBoxHover, closeBoxDown);
429                           list.push_back(close);
430                           close->SetNotificationElement(this); }
431                 virtual ~Window();
432                 virtual void HandleKey(SDLKey key);
433                 virtual void HandleMouseMove(uint32 x, uint32 y);
434                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
435                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
436                 virtual void Notify(Element * e);
437                 void AddElement(Element * e);
438 //              bool WindowActive(void) { return true; }//return !close->ButtonClicked(); }
439
440         protected:
441 //              bool clicked, inside;
442                 uint32 fgColor, bgColor;
443                 void (* handler)(Element *);
444                 Button * close;
445 //We have to use a list of Element *pointers* because we can't make a list that will hold
446 //all the different object types in the same list...
447                 vector<Element *> list;
448 };
449
450 Window::~Window()
451 {
452         for(uint32 i=0; i<list.size(); i++)
453                 if (list[i])
454                         delete list[i];
455 }
456
457 void Window::HandleKey(SDLKey key)
458 {
459         if (key == SDLK_ESCAPE)
460         {
461                 SDL_Event event;
462                 event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
463                 SDL_PushEvent(&event);
464         }
465
466         // Handle the items this window contains...
467         for(uint32 i=0; i<list.size(); i++)
468                 // Make coords relative to upper right corner of this window...
469                 list[i]->HandleKey(key);
470 }
471
472 void Window::HandleMouseMove(uint32 x, uint32 y)
473 {
474         // Handle the items this window contains...
475         for(uint32 i=0; i<list.size(); i++)
476                 // Make coords relative to upper right corner of this window...
477                 list[i]->HandleMouseMove(x - extents.x, y - extents.y);
478 }
479
480 void Window::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
481 {
482         // Handle the items this window contains...
483         for(uint32 i=0; i<list.size(); i++)
484                 // Make coords relative to upper right corner of this window...
485                 list[i]->HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
486 }
487
488 void Window::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
489 {
490         uint32 addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
491
492         for(uint32 y=0; y<extents.h; y++)
493         {
494                 for(uint32 x=0; x<extents.w; x++)
495                 {
496                         // Doesn't clip in y axis! !!! FIX !!!
497                         if (extents.x + x < pitch)
498                                 screenBuffer[addr + x + (y * pitch)] = bgColor;
499                 }
500         }
501
502         // Handle the items this window contains...
503         for(uint32 i=0; i<list.size(); i++)
504                 list[i]->Draw(extents.x, extents.y);
505 }
506
507 void Window::AddElement(Element * e)
508 {
509         list.push_back(e);
510 }
511
512 void Window::Notify(Element * e)
513 {
514         if (e == close)
515         {
516                 SDL_Event event;
517                 event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
518                 SDL_PushEvent(&event);
519         }
520 }
521
522
523 //
524 // Static text class
525 //
526
527 class Text: public Element
528 {
529         public:
530 //              Text(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
531 //                      fgColor(0x4FF0), bgColor(0xFE10) {}
532 //              Text(uint32 x, uint32 y, string s, uint16 fg = 0x4FF0, uint16 bg = 0xFE10): Element(x, y, 0, 0),
533 //                      fgColor(fg), bgColor(bg), text(s) {}
534 //4FF0 -> 010011 11111 10000 -> 0100 1101 1111 1111 1000 0100 -> 4D FF 84
535 //FE10 -> 111111 10000 10000 -> 1111 1111 1000 0100 1000 0100 -> FF 84 84
536                 Text(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
537                         fgColor(0xFF8484FF), bgColor(0xFF84FF4D) {}
538                 Text(uint32 x, uint32 y, string s, uint32 fg = 0xFF8484FF, uint32 bg = 0xFF84FF4D):
539                         Element(x, y, 0, 0), fgColor(fg), bgColor(bg), text(s) {}
540                 virtual void HandleKey(SDLKey key) {}
541                 virtual void HandleMouseMove(uint32 x, uint32 y) {}
542                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
543                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
544                 virtual void Notify(Element *) {}
545
546         protected:
547                 uint32 fgColor, bgColor;
548                 string text;
549 };
550
551 void Text::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
552 {
553         if (text.length() > 0)
554 //              DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY, false, "%s", text.c_str());
555                 DrawStringOpaque(screenBuffer, extents.x + offsetX, extents.y + offsetY, fgColor, bgColor, "%s", text.c_str());
556 }
557
558
559 //
560 // Static image class
561 //
562
563 class Image: public Element
564 {
565         public:
566                 Image(uint32 x, uint32 y, const void * img): Element(x, y, 0, 0), image(img) {}
567                 virtual void HandleKey(SDLKey key) {}
568                 virtual void HandleMouseMove(uint32 x, uint32 y) {}
569                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
570                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
571                 virtual void Notify(Element *) {}
572
573         protected:
574                 uint32 fgColor, bgColor;
575                 const void * image;
576 };
577
578 void Image::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
579 {
580         if (image != NULL)
581                 DrawTransparentBitmap(screenBuffer, extents.x + offsetX, extents.y + offsetY, image);
582 }
583
584
585 //
586 // TextEdit class
587 //
588
589 class TextEdit: public Element
590 {
591         public:
592                 TextEdit(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
593                         fgColor(0xFF8484FF), bgColor(0xFF84FF4D), text(""), caretPos(0),
594                         maxScreenSize(10) {}
595                 TextEdit(uint32 x, uint32 y, string s, uint32 mss = 10, uint32 fg = 0xFF8484FF,
596                         uint32 bg = 0xFF84FF4D): Element(x, y, 0, 0), fgColor(fg), bgColor(bg), text(s),
597                         caretPos(0), maxScreenSize(mss) {}
598                 virtual void HandleKey(SDLKey key);
599                 virtual void HandleMouseMove(uint32 x, uint32 y) {}
600                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
601                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
602                 virtual void Notify(Element *) {}
603
604         protected:
605                 uint32 fgColor, bgColor;
606                 string text;
607                 uint32 caretPos;
608                 uint32 maxScreenSize;
609 };
610
611 //Set different filters depending on type passed in on construction, e.g., filename, amount, etc...?
612 void TextEdit::HandleKey(SDLKey key)
613 {
614         if ((key >= SDLK_a && key <= SDLK_z) || (key >= SDLK_0 && key <= SDLK_9) || key == SDLK_PERIOD
615                 || key == SDLK_SLASH)
616         {
617                 //Need to handle shift key as well...
618                 text[caretPos++] = key;
619                 Draw();
620         }
621         else if (key == SDLK_BACKSPACE)
622         {
623
624         }
625         else if (key == SDLK_DELETE)
626         {
627         }
628 //left, right arrow
629 }
630
631 void TextEdit::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
632 {
633         if (text.length() > 0)
634         {
635                 FillScreenRectangle(screenBuffer, extents.x + offsetX, extents.y + offsetY, FONT_WIDTH * maxScreenSize, FONT_HEIGHT, bgColor);
636 //              DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY, false, "%s", text.c_str());
637                 DrawStringOpaque(screenBuffer, extents.x + offsetX, extents.y + offsetY, fgColor, bgColor, "%s", text.c_str());
638         }
639
640         // Draw the caret (underscore? or vertical line?)
641 }
642
643
644 //
645 // ListBox class
646 //
647
648 class ListBox: public Element
649 //class ListBox: public Window
650 {
651         public:
652 //              ListBox(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
653                 ListBox(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0);//: Window(x, y, w, h),
654 //              windowPtr(0), cursor(0), limit(0), charWidth((w / 8) - 1), charHeight(h / 8),
655 //              elementToTell(NULL), upArrow(w - 8, 0, upArrowBox),
656 //              downArrow(w - 8, h - 8, downArrowBox), upArrow2(w - 8, h - 16, upArrowBox) {}
657                 virtual void HandleKey(SDLKey key);
658                 virtual void HandleMouseMove(uint32 x, uint32 y);
659                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
660                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
661                 virtual void Notify(Element * e);
662                 void SetNotificationElement(Element * e) { elementToTell = e; }
663                 void AddItem(string s);
664                 string GetSelectedItem(void);
665
666         protected:
667                 bool thumbClicked;
668                 uint32 windowPtr, cursor, limit;
669                 uint32 charWidth, charHeight;                           // Box width/height in characters
670                 Element * elementToTell;
671                 Button upArrow, downArrow, upArrow2;
672                 vector<string> item;
673
674         private:
675                 uint32 yRelativePoint;
676 };
677
678 ListBox::ListBox(uint32 x, uint32 y, uint32 w, uint32 h): Element(x, y, w, h),
679         thumbClicked(false), windowPtr(0), cursor(0), limit(0), charWidth((w / FONT_WIDTH) - 1),
680         charHeight(h / FONT_HEIGHT), elementToTell(NULL), upArrow(w - 8, 0, upArrowBox),
681         downArrow(w - 8, h - 8, downArrowBox), upArrow2(w - 8, h - 16, upArrowBox)
682 {
683         upArrow.SetNotificationElement(this);
684         downArrow.SetNotificationElement(this);
685         upArrow2.SetNotificationElement(this);
686         extents.w -= 8;                                                                 // Make room for scrollbar...
687 }
688
689 void ListBox::HandleKey(SDLKey key)
690 {
691         if (key == SDLK_DOWN)
692         {
693                 if (cursor != limit - 1)        // Cursor is within its window
694                         cursor++;
695                 else                                            // Otherwise, scroll the window...
696                 {
697                         if (cursor + windowPtr != item.size() - 1)
698                                 windowPtr++;
699                 }
700         }
701         else if (key == SDLK_UP)
702         {
703                 if (cursor != 0)
704                         cursor--;
705                 else
706                 {
707                         if (windowPtr != 0)
708                                 windowPtr--;
709                 }
710         }
711         else if (key == SDLK_PAGEDOWN)
712         {
713                 if (cursor != limit - 1)
714                         cursor = limit - 1;
715                 else
716                 {
717                         windowPtr += limit;
718                         if (windowPtr > item.size() - limit)
719                                 windowPtr = item.size() - limit;
720                 }
721         }
722         else if (key == SDLK_PAGEUP)
723         {
724                 if (cursor != 0)
725                         cursor = 0;
726                 else
727                 {
728                         if (windowPtr < limit)
729                                 windowPtr = 0;
730                         else
731                                 windowPtr -= limit;
732                 }
733         }
734         else if (key >= SDLK_a && key <= SDLK_z)
735         {
736                 // Advance cursor to filename with first letter pressed...
737                 uint8 which = (key - SDLK_a) + 65;      // Convert key to A-Z char
738
739                 for(uint32 i=0; i<item.size(); i++)
740                 {
741                         if ((item[i][0] & 0xDF) == which)
742                         {
743                                 cursor = i - windowPtr;
744                                 if (i > windowPtr + limit - 1)
745                                         windowPtr = i - limit + 1, cursor = limit - 1;
746                                 if (i < windowPtr)
747                                         windowPtr = i, cursor = 0;
748                                 break;
749                         }
750                 }
751         }
752 }
753
754 void ListBox::HandleMouseMove(uint32 x, uint32 y)
755 {
756         upArrow.HandleMouseMove(x - extents.x, y - extents.y);
757         downArrow.HandleMouseMove(x - extents.x, y - extents.y);
758         upArrow2.HandleMouseMove(x - extents.x, y - extents.y);
759
760         if (thumbClicked)
761         {
762                 uint32 sbHeight = extents.h - 24,
763                         thumb = (uint32)(((float)limit / (float)item.size()) * (float)sbHeight);
764
765 //yRelativePoint is the spot on the thumb where we clicked...
766                 int32 newThumbStart = y - yRelativePoint;
767
768                 if (newThumbStart < 0)
769                         newThumbStart = 0;
770
771                 if ((uint32)newThumbStart > sbHeight - thumb)
772                         newThumbStart = sbHeight - thumb;
773
774                 windowPtr = (uint32)(((float)newThumbStart / (float)sbHeight) * (float)item.size());
775 //Check for cursor bounds as well... Or do we need to???
776 //Actually, we don't...!
777         }
778 }
779
780 void ListBox::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
781 {
782         if (Inside(x, y) && mouseDown)
783         {
784                 // Why do we have to do this??? (- extents.y?)
785                 // I guess it's because only the Window class has offsetting implemented... !!! FIX !!!
786 //              cursor = (y - extents.y) / 8;
787                 cursor = (y - extents.y) / FONT_HEIGHT;
788         }
789
790         // Check for a hit on the scrollbar...
791         if (x > (uint32)(extents.x + extents.w) && x <= (uint32)(extents.x + extents.w + 8)
792                 && y > (uint32)(extents.y + 8) && y <= (uint32)(extents.y + extents.h - 16))
793         {
794                 if (mouseDown)
795                 {
796 // This shiaut should be calculated in AddItem(), not here... (or in Draw() for that matter)
797                         uint32 sbHeight = extents.h - 24,
798                                 thumb = (uint32)(((float)limit / (float)item.size()) * (float)sbHeight),
799                                 thumbStart = (uint32)(((float)windowPtr / (float)item.size()) * (float)sbHeight);
800
801                         // Did we hit the thumb?
802                         if (y >= (extents.y + 8 + thumbStart) && y < (extents.y + 8 + thumbStart + thumb))
803                                 thumbClicked = true, yRelativePoint = y - thumbStart;
804                 }
805 //Seems that this is useless--never reached except in rare cases and that the code outside is
806 //more effective...
807 //              else
808 //                      thumbClicked = false;
809         }
810
811         if (!mouseDown)
812                 thumbClicked = false;
813
814         upArrow.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
815         downArrow.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
816         upArrow2.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
817 }
818
819 void ListBox::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
820 {
821         for(uint32 i=0; i<limit; i++)
822         {
823                 // Strip off the extension
824                 // (extension stripping should be an option, not default!)
825                 string s(item[windowPtr + i], 0, item[windowPtr + i].length() - 4);
826 //              DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY + i*8,
827                 DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY + i*FONT_HEIGHT,
828                         (cursor == i ? true : false), "%-*.*s", charWidth, charWidth, s.c_str());
829         }
830
831         upArrow.Draw(extents.x + offsetX, extents.y + offsetY);
832         downArrow.Draw(extents.x + offsetX, extents.y + offsetY);
833         upArrow2.Draw(extents.x + offsetX, extents.y + offsetY);
834
835         uint32 sbHeight = extents.h - 24,
836                 thumb = (uint32)(((float)limit / (float)item.size()) * (float)sbHeight),
837                 thumbStart = (uint32)(((float)windowPtr / (float)item.size()) * (float)sbHeight);
838
839         for(uint32 y=extents.y+offsetY+8; y<extents.y+offsetY+extents.h-16; y++)
840         {
841 //              for(uint32 x=extents.x+offsetX+extents.w-8; x<extents.x+offsetX+extents.w; x++)
842                 for(uint32 x=extents.x+offsetX+extents.w; x<extents.x+offsetX+extents.w+8; x++)
843                 {
844                         if (y >= thumbStart + (extents.y+offsetY+8) && y < thumbStart + thumb + (extents.y+offsetY+8))
845 //                              screenBuffer[x + (y * pitch)] = (thumbClicked ? 0x458E : 0xFFFF);
846 //458E -> 01 0001  0 1100  0 1110 -> 0100 0101  0110 0011  0111 0011 -> 45 63 73
847                                 screenBuffer[x + (y * pitch)] = (thumbClicked ? 0xFF736345 : 0xFFFFFFFF);
848                         else
849 //                              screenBuffer[x + (y * pitch)] = 0x0200;
850 //0200 -> 000000 10000 00000 -> 00 1000 0100 00
851                                 screenBuffer[x + (y * pitch)] = 0xFF008400;
852                 }
853         }
854 }
855
856 void ListBox::Notify(Element * e)
857 {
858         if (e == &upArrow || e == &upArrow2)
859         {
860                 if (windowPtr != 0)
861                 {
862                         windowPtr--;
863
864                         if (cursor < limit - 1)
865                                 cursor++;
866                 }
867         }
868         else if (e == &downArrow)
869         {
870                 if (windowPtr < item.size() - limit)
871                 {
872                         windowPtr++;
873
874                         if (cursor != 0)
875                                 cursor--;
876                 }
877         }
878 }
879
880 void ListBox::AddItem(string s)
881 {
882         // Do a simple insertion sort
883         bool inserted = false;
884
885         for(vector<string>::iterator i=item.begin(); i<item.end(); i++)
886         {
887                 if (stringCmpi(s, *i) == -1)
888                 {
889                         item.insert(i, s);
890                         inserted = true;
891                         break;
892                 }
893         }
894
895         if (!inserted)
896                 item.push_back(s);
897
898         limit = (item.size() > charHeight ? charHeight : item.size());
899 }
900
901 string ListBox::GetSelectedItem(void)
902 {
903         return item[windowPtr + cursor];
904 }
905
906
907 //
908 // FileList class
909 //
910
911 class FileList: public Window
912 {
913         public:
914                 FileList(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0);
915                 virtual ~FileList() {}
916                 virtual void HandleKey(SDLKey key);
917                 virtual void HandleMouseMove(uint32 x, uint32 y) { Window::HandleMouseMove(x, y); }
918                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) { Window::HandleMouseButton(x, y, mouseDown); }
919                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0) { Window::Draw(offsetX, offsetY); }
920                 virtual void Notify(Element * e);
921
922         protected:
923                 ListBox * files;
924                 Button * load;
925 };
926
927 //Need 4 buttons, one scrollbar...
928 FileList::FileList(uint32 x, uint32 y, uint32 w, uint32 h): Window(x, y, w, h)
929 {
930         files = new ListBox(8, 8, w - 16, h - 32);
931         AddElement(files);
932         load = new Button(8, h - 16, " Load ");
933         AddElement(load);
934         load->SetNotificationElement(this);
935
936 #warning !!! FIX !!! Directory might not exist--this shouldn't cause VJ to crash!
937         DIR * dp = opendir(vjs.ROMPath);
938         dirent * de;
939
940         if (dp != NULL)
941         {
942                 while ((de = readdir(dp)) != NULL)
943                 {
944                         char * ext = strrchr(de->d_name, '.');
945
946                         if (ext != NULL)
947                                 if (strcasecmp(ext, ".zip") == 0 || strcasecmp(ext, ".j64") == 0
948                                         || strcasecmp(ext, ".abs") == 0 || strcasecmp(ext, ".jag") == 0
949                                         || strcasecmp(ext, ".rom") == 0)
950                                         files->AddItem(string(de->d_name));
951                 }
952
953                 closedir(dp);
954         }
955         else
956         {
957 //Give a diagnostic message here so that the (l)user can figure out what went wrong. !!! FIX !!!
958         }
959 }
960
961 void FileList::HandleKey(SDLKey key)
962 {
963         if (key == SDLK_RETURN)
964                 Notify(load);
965         else
966                 Window::HandleKey(key);
967 }
968
969 void FileList::Notify(Element * e)
970 {
971         if (e == load)
972         {
973                 char filename[MAX_PATH];
974                 strcpy(filename, vjs.ROMPath);
975
976                 if (strlen(filename) > 0)
977                         if (filename[strlen(filename) - 1] != '/')
978                                 strcat(filename, "/");
979
980                 strcat(filename, files->GetSelectedItem().c_str());
981
982 //              uint32 romSize = JaguarLoadROM(jaguar_mainRom, filename);
983 //              JaguarLoadCart(jaguar_mainRom, filename);
984                 if (JaguarLoadFile(filename))
985                 {
986                         SDL_Event event;
987                         event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
988                         SDL_PushEvent(&event);
989
990                         event.type = SDL_USEREVENT, event.user.code = MENU_ITEM_CHOSEN;
991                         event.user.data1 = (void *)ResetJaguar;
992                 SDL_PushEvent(&event);
993                 }
994                 else
995                 {
996                         SDL_Event event;
997                         event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
998                         SDL_PushEvent(&event);
999
1000                         // Handle the error, but don't run...
1001                         // Tell the user that we couldn't run their file for some reason... !!! FIX !!!
1002 //how to kludge: Make a function like ResetJaguar which creates the dialog window
1003                 }
1004         }
1005         else
1006                 Window::Notify(e);
1007 }
1008
1009
1010 //
1011 // Menu class & supporting structs/classes
1012 //
1013
1014 struct NameAction
1015 {
1016         string name;
1017         Window * (* action)(void);
1018         SDLKey hotKey;
1019
1020         NameAction(string n, Window * (* a)(void) = NULL, SDLKey k = SDLK_UNKNOWN): name(n),
1021                 action(a), hotKey(k) {}
1022 };
1023
1024 class MenuItems
1025 {
1026         public:
1027                 MenuItems(): charLength(0) {}
1028                 bool Inside(uint32 x, uint32 y)
1029                 { return (x >= (uint32)extents.x && x < (uint32)(extents.x + extents.w)
1030                 && y >= (uint32)extents.y && y < (uint32)(extents.y + extents.h) ? true : false); }
1031
1032                 string title;
1033                 vector<NameAction> item;
1034                 uint32 charLength;
1035                 SDL_Rect extents;
1036 };
1037
1038 class Menu: public Element
1039 {
1040         public:
1041 // 1CFF -> 0 001 11 00  111 1 1111
1042 // 421F -> 0 100 00 10  000 1 1111
1043                 Menu(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = FONT_HEIGHT,
1044 /*                      uint16 fgc = 0x1CFF, uint16 bgc = 0x000F, uint16 fgch = 0x421F,
1045                         uint16 bgch = 0x1CFF): Element(x, y, w, h), activated(false), clicked(false),*/
1046 /*                      uint32 fgc = 0xFF3F3F00, uint32 bgc = 0x7F000000, uint32 fgch = 0xFF878700,
1047                         uint32 bgch = 0xFF3F3F00): Element(x, y, w, h), activated(false), clicked(false),*/
1048 /*                      uint32 fgc = 0xFFFF3F3F, uint32 bgc = 0xFF7F0000, uint32 fgch = 0xFFFF8787,
1049                         uint32 bgch = 0xFFFF3F3F): Element(x, y, w, h), activated(false), clicked(false),*/
1050                         uint32 fgc = 0xFF7F0000, uint32 bgc = 0xFFFF3F3F, uint32 fgch = 0xFFFF3F3F,
1051                         uint32 bgch = 0xFFFF8787): Element(x, y, w, h), activated(false), clicked(false),
1052                         inside(0), insidePopup(0), fgColor(fgc), bgColor(bgc), fgColorHL(fgch),
1053                         bgColorHL(bgch), menuChosen(-1), menuItemChosen(-1) {}
1054                 virtual void HandleKey(SDLKey key);
1055                 virtual void HandleMouseMove(uint32 x, uint32 y);
1056                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
1057                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
1058                 virtual void Notify(Element *) {}
1059                 void Add(MenuItems mi);
1060
1061         protected:
1062                 bool activated, clicked;
1063                 uint32 inside, insidePopup;
1064 //              uint16 fgColor, bgColor, fgColorHL, bgColorHL;
1065                 uint32 fgColor, bgColor, fgColorHL, bgColorHL;
1066                 int menuChosen, menuItemChosen;
1067
1068         private:
1069                 vector<MenuItems> itemList;
1070 };
1071
1072 void Menu::HandleKey(SDLKey key)
1073 {
1074         for(uint32 i=0; i<itemList.size(); i++)
1075         {
1076                 for(uint32 j=0; j<itemList[i].item.size(); j++)
1077                 {
1078                         if (itemList[i].item[j].hotKey == key)
1079                         {
1080                                 SDL_Event event;
1081                                 event.type = SDL_USEREVENT;
1082                                 event.user.code = MENU_ITEM_CHOSEN;
1083                                 event.user.data1 = (void *)itemList[i].item[j].action;
1084                         SDL_PushEvent(&event);
1085
1086                                 clicked = false, menuChosen = menuItemChosen = -1;
1087                                 break;
1088                         }
1089                 }
1090         }
1091 }
1092
1093 void Menu::HandleMouseMove(uint32 x, uint32 y)
1094 {
1095         inside = insidePopup = 0;
1096
1097         if (Inside(x, y))
1098         {
1099                 // Find out *where* we are inside the menu bar
1100                 uint32 xpos = extents.x;
1101
1102                 for(uint32 i=0; i<itemList.size(); i++)
1103                 {
1104                         uint32 width = (itemList[i].title.length() + 2) * FONT_WIDTH;
1105
1106                         if (x >= xpos && x < xpos + width)
1107                         {
1108                                 inside = i + 1;
1109                                 menuChosen = i;
1110                                 break;
1111                         }
1112
1113                         xpos += width;
1114                 }
1115         }
1116
1117         if (!Inside(x, y) && !clicked)
1118         {
1119                 menuChosen = -1;
1120         }
1121
1122         if (itemList[menuChosen].Inside(x, y) && clicked)
1123         {
1124                 insidePopup = ((y - itemList[menuChosen].extents.y) / FONT_HEIGHT) + 1;
1125                 menuItemChosen = insidePopup - 1;
1126         }
1127 }
1128
1129 void Menu::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
1130 {
1131         if (!clicked)
1132         {
1133                 if (mouseDown)
1134                 {
1135                         if (inside)
1136                                 clicked = true;
1137                         else
1138                                 menuChosen = -1;                                        // clicked is already false...!
1139                 }
1140         }
1141         else                                                                                    // clicked == true
1142         {
1143                 if (insidePopup && !mouseDown)                          // I.e., mouse-button-up
1144                 {
1145                         activated = true;
1146                         if (itemList[menuChosen].item[menuItemChosen].action != NULL)
1147                         {
1148 //                              itemList[menuChosen].item[menuItemChosen].action();
1149                                 SDL_Event event;
1150                                 event.type = SDL_USEREVENT;
1151                                 event.user.code = MENU_ITEM_CHOSEN;
1152                                 event.user.data1 = (void *)itemList[menuChosen].item[menuItemChosen].action;
1153                             SDL_PushEvent(&event);
1154
1155                                 clicked = false, menuChosen = menuItemChosen = -1;
1156
1157 /*                              SDL_Event event;
1158                                 while (SDL_PollEvent(&event));          // Flush the event queue...
1159                                 event.type = SDL_MOUSEMOTION;
1160                                 int mx, my;
1161                                 SDL_GetMouseState(&mx, &my);
1162                                 event.motion.x = mx, event.motion.y = my;
1163                             SDL_PushEvent(&event);                              // & update mouse position...!
1164 */                      }
1165                 }
1166
1167                 if (!inside && !insidePopup && mouseDown)
1168                         clicked = false, menuChosen = menuItemChosen = -1;
1169         }
1170 }
1171
1172 void Menu::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
1173 {
1174         uint32 xpos = extents.x + offsetX;
1175
1176         for(uint32 i=0; i<itemList.size(); i++)
1177         {
1178 //              uint16 color1 = fgColor, color2 = bgColor;
1179                 uint32 color1 = fgColor, color2 = bgColor;
1180                 if (inside == (i + 1) || (menuChosen != -1 && (uint32)menuChosen == i))
1181                         color1 = fgColorHL, color2 = bgColorHL;
1182
1183                 DrawStringOpaque(screenBuffer, xpos, extents.y + offsetY, color1, color2,
1184                         " %s ", itemList[i].title.c_str());
1185                 xpos += (itemList[i].title.length() + 2) * FONT_WIDTH;
1186         }
1187
1188         // Draw sub menu (but only if active)
1189         if (clicked)
1190         {
1191                 uint32 ypos = extents.y + FONT_HEIGHT + 1;
1192
1193                 for(uint32 i=0; i<itemList[menuChosen].item.size(); i++)
1194                 {
1195 //                      uint16 color1 = fgColor, color2 = bgColor;
1196                         uint32 color1 = fgColor, color2 = bgColor;
1197
1198                         if (insidePopup == i + 1)
1199                                 color1 = fgColorHL, color2 = bgColorHL, menuItemChosen = i;
1200
1201                         if (itemList[menuChosen].item[i].name.length() > 0)
1202                                 DrawStringOpaque(screenBuffer, itemList[menuChosen].extents.x, ypos,
1203                                         color1, color2, " %-*.*s ", itemList[menuChosen].charLength,
1204                                         itemList[menuChosen].charLength, itemList[menuChosen].item[i].name.c_str());
1205                         else
1206                                 DrawStringOpaque(screenBuffer, itemList[menuChosen].extents.x, ypos,
1207                                         fgColor, bgColor, "%.*s", itemList[menuChosen].charLength + 2, separator);
1208
1209                         ypos += FONT_HEIGHT;
1210                 }
1211         }
1212 }
1213
1214 void Menu::Add(MenuItems mi)
1215 {
1216         for(uint32 i=0; i<mi.item.size(); i++)
1217                 if (mi.item[i].name.length() > mi.charLength)
1218                         mi.charLength = mi.item[i].name.length();
1219
1220         // Set extents here as well...
1221         mi.extents.x = extents.x + extents.w, mi.extents.y = extents.y + FONT_HEIGHT + 1;
1222         mi.extents.w = (mi.charLength + 2) * FONT_WIDTH, mi.extents.h = mi.item.size() * FONT_HEIGHT;
1223
1224         itemList.push_back(mi);
1225         extents.w += (mi.title.length() + 2) * FONT_WIDTH;
1226 }
1227
1228
1229 //Do we even *need* this?
1230 //Doesn't seem like it...
1231 /*class RootWindow: public Window
1232 {
1233         public:
1234                 RootWindow(Menu * m, Window * w = NULL): menu(m), window(w) {}
1235 //Do we even need to care about this crap?
1236 //                      { extents.x = extents.y = 0, extents.w = 320, extents.h = 240; }
1237                 virtual void HandleKey(SDLKey key) {}
1238                 virtual void HandleMouseMove(uint32 x, uint32 y) {}
1239                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
1240                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0) {}
1241                 virtual void Notify(Element *) {}
1242
1243         private:
1244                 Menu * menu;
1245                 Window * window;
1246                 int16 * rootImage[1280 * 240 * 2];
1247 };//*/
1248
1249
1250 //
1251 // Draw text at the given x/y coordinates. Can invert text as well.
1252 //
1253 void DrawString(uint32 * screen, uint32 x, uint32 y, bool invert, const char * text, ...)
1254 {
1255         char string[4096];
1256         va_list arg;
1257
1258         va_start(arg, text);
1259         vsprintf(string, text, arg);
1260         va_end(arg);
1261
1262         uint32 pitch = sdlemuGetOverlayWidthInPixels();//GetSDLScreenWidthInPixels();
1263         uint32 length = strlen(string), address = x + (y * pitch);
1264
1265         uint32 color1 = 0x0080FF;
1266         uint8 nBlue = (color1 >> 16) & 0xFF, nGreen = (color1 >> 8) & 0xFF, nRed = color1 & 0xFF;
1267         uint8 xorMask = (invert ? 0xFF : 0x00);
1268
1269         for(uint32 i=0; i<length; i++)
1270         {
1271                 uint8 c = string[i];
1272                 uint32 fontAddr = (uint32)(c < 32 ? 0 : c - 32) * FONT_WIDTH * FONT_HEIGHT;
1273
1274                 for(uint32 yy=0; yy<FONT_HEIGHT; yy++)
1275                 {
1276                         for(uint32 xx=0; xx<FONT_WIDTH; xx++)
1277                         {
1278                                 uint32 existingColor = *(screen + address + xx + (yy * pitch));
1279
1280                                 uint8 eBlue = (existingColor >> 16) & 0xFF,
1281                                         eGreen = (existingColor >> 8) & 0xFF,
1282                                         eRed = existingColor & 0xFF;
1283
1284                                 uint8 trans = font2[fontAddr] ^ xorMask;
1285                                 uint8 invTrans = trans ^ 0xFF;
1286
1287                                 uint32 bRed = (eRed * invTrans + nRed * trans) / 255,
1288                                         bGreen = (eGreen * invTrans + nGreen * trans) / 255,
1289                                         bBlue = (eBlue * invTrans + nBlue * trans) / 255;
1290
1291                                 *(screen + address + xx + (yy * pitch)) = 0xFF000000 | (bBlue << 16) | (bGreen << 8) | bRed;
1292                                 fontAddr++;
1293                         }
1294                 }
1295
1296                 address += FONT_WIDTH;
1297         }
1298 }
1299
1300 //
1301 // Draw text at the given x/y coordinates, using FG/BG colors.
1302 //
1303 void DrawStringOpaque(uint32 * screen, uint32 x, uint32 y, uint32 color1, uint32 color2, const char * text, ...)
1304 {
1305         char string[4096];
1306         va_list arg;
1307
1308         va_start(arg, text);
1309         vsprintf(string, text, arg);
1310         va_end(arg);
1311
1312         uint32 pitch = sdlemuGetOverlayWidthInPixels();
1313         uint32 length = strlen(string), address = x + (y * pitch);
1314
1315         uint8 eBlue = (color2 >> 16) & 0xFF, eGreen = (color2 >> 8) & 0xFF, eRed = color2 & 0xFF,
1316                 nBlue = (color1 >> 16) & 0xFF, nGreen = (color1 >> 8) & 0xFF, nRed = color1 & 0xFF;
1317
1318         for(uint32 i=0; i<length; i++)
1319         {
1320                 uint8 c = string[i];
1321                 c = (c < 32 ? 0 : c - 32);
1322                 uint32 fontAddr = (uint32)c * FONT_WIDTH * FONT_HEIGHT;
1323
1324                 for(uint32 yy=0; yy<FONT_HEIGHT; yy++)
1325                 {
1326                         for(uint32 xx=0; xx<FONT_WIDTH; xx++)
1327                         {
1328                                 uint8 trans = font2[fontAddr++];
1329                                 uint8 invTrans = trans ^ 0xFF;
1330
1331                                 uint32 bRed   = (eRed   * invTrans + nRed   * trans) / 255;
1332                                 uint32 bGreen = (eGreen * invTrans + nGreen * trans) / 255;
1333                                 uint32 bBlue  = (eBlue  * invTrans + nBlue  * trans) / 255;
1334
1335                                 *(screen + address + xx + (yy * pitch)) = 0xFF000000 | (bBlue << 16) | (bGreen << 8) | bRed;
1336                         }
1337                 }
1338
1339                 address += FONT_WIDTH;
1340         }
1341 }
1342
1343 //
1344 // Draw text at the given x/y coordinates with transparency (0 is fully opaque, 32 is fully transparent).
1345 //
1346 void DrawStringTrans(uint32 * screen, uint32 x, uint32 y, uint32 color, uint8 trans, const char * text, ...)
1347 {
1348         char string[4096];
1349         va_list arg;
1350
1351         va_start(arg, text);
1352         vsprintf(string, text, arg);
1353         va_end(arg);
1354
1355         uint32 pitch = sdlemuGetOverlayWidthInPixels();//GetSDLScreenWidthInPixels();
1356         uint32 length = strlen(string), address = x + (y * pitch);
1357
1358         for(uint32 i=0; i<length; i++)
1359         {
1360                 uint32 fontAddr = (uint32)string[i] * 64;
1361
1362                 for(uint32 yy=0; yy<8; yy++)
1363                 {
1364                         for(uint32 xx=0; xx<8; xx++)
1365                         {
1366                                 if (font1[fontAddr])
1367                                 {
1368                                         uint32 existingColor = *(screen + address + xx + (yy * pitch));
1369
1370                                         uint8 eBlue = (existingColor >> 16) & 0xFF,
1371                                                 eGreen = (existingColor >> 8) & 0xFF,
1372                                                 eRed = existingColor & 0xFF,
1373 //This could be done ahead of time, instead of on each pixel...
1374                                                 nBlue = (color >> 16) & 0xFF,
1375                                                 nGreen = (color >> 8) & 0xFF,
1376                                                 nRed = color & 0xFF;
1377
1378 //This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
1379 //Here we've modified it to have 33 levels of transparency (could have any # we want!)
1380 //because dividing by 32 is faster than dividing by 31...!
1381                                         uint8 invTrans = 32 - trans;
1382
1383                                         uint32 bRed = (eRed * trans + nRed * invTrans) / 32;
1384                                         uint32 bGreen = (eGreen * trans + nGreen * invTrans) / 32;
1385                                         uint32 bBlue = (eBlue * trans + nBlue * invTrans) / 32;
1386
1387                                         *(screen + address + xx + (yy * pitch)) = 0xFF000000 | (bBlue << 16) | (bGreen << 8) | bRed;
1388                                 }
1389
1390                                 fontAddr++;
1391                         }
1392                 }
1393
1394                 address += 8;
1395         }
1396 }
1397
1398 //
1399 // Draw text at the given x/y coordinates, using FG color and overlay alpha blending.
1400 //
1401 void DrawString2(uint32 * screen, uint32 x, uint32 y, uint32 color, uint8 transparency, const char * text, ...)
1402 {
1403         char string[4096];
1404         va_list arg;
1405
1406         va_start(arg, text);
1407         vsprintf(string, text, arg);
1408         va_end(arg);
1409
1410         uint32 pitch = sdlemuGetOverlayWidthInPixels();
1411         uint32 length = strlen(string), address = x + (y * pitch);
1412
1413         color &= 0x00FFFFFF;                                            // Just in case alpha was passed in...
1414
1415         for(uint32 i=0; i<length; i++)
1416         {
1417                 uint8 c = string[i];
1418                 c = (c < 32 ? 0 : c - 32);
1419                 uint32 fontAddr = (uint32)c * FONT_WIDTH * FONT_HEIGHT;
1420
1421                 for(uint32 yy=0; yy<FONT_HEIGHT; yy++)
1422                 {
1423                         for(uint32 xx=0; xx<FONT_WIDTH; xx++)
1424                         {
1425                                 uint8 fontTrans = font2[fontAddr++];
1426                                 uint32 newTrans = (fontTrans * transparency / 255) << 24;
1427                                 uint32 pixel = newTrans | color;
1428
1429                                 *(screen + address + xx + (yy * pitch)) = pixel;
1430                         }
1431                 }
1432
1433                 address += FONT_WIDTH;
1434         }
1435 }
1436
1437 //
1438 // Draw "picture"
1439 // Uses zero as transparent color
1440 // Can also use an optional alpha channel
1441 // Alpha channel is now mandatory! ;-)
1442 //
1443 //void DrawTransparentBitmap(int16 * screen, uint32 x, uint32 y, uint16 * bitmap, uint8 * alpha/*=NULL*/)
1444 /*void DrawTransparentBitmap(uint32 * screen, uint32 x, uint32 y, uint32 * bitmap, uint8 * alpha)
1445 {
1446         uint32 width = bitmap[0], height = bitmap[1];
1447         bitmap += 2;
1448
1449 //      uint32 pitch = GetSDLScreenPitch() / 2;                 // Returns pitch in bytes but we need words...
1450         uint32 pitch = sdlemuGetOverlayWidthInPixels();//GetSDLScreenWidthInPixels();
1451         uint32 address = x + (y * pitch);
1452
1453         for(uint32 yy=0; yy<height; yy++)
1454         {
1455                 for(uint32 xx=0; xx<width; xx++)
1456                 {
1457                         if (alpha == NULL)
1458                         {
1459                                 if (*bitmap && x + xx < pitch)                  // NOTE: Still doesn't clip the Y val...
1460                                         *(screen + address + xx + (yy * pitch)) = *bitmap;
1461                         }
1462                         else
1463                         {
1464                                 uint8 trans = *alpha;
1465                                 uint32 color = *bitmap;
1466                                 uint32 existingColor = *(screen + address + xx + (yy * pitch));
1467
1468                                 uint8 eRed = existingColor & 0xFF,
1469                                         eGreen = (existingColor >> 8) & 0xFF,
1470                                         eBlue = (existingColor >> 16) & 0xFF,
1471
1472                                         nRed = color & 0xFF,
1473                                         nGreen = (color >> 8) & 0xFF,
1474                                         nBlue = (color >> 16) & 0xFF;
1475
1476                                 uint8 invTrans = 255 - trans;
1477                                 uint32 bRed = (eRed * trans + nRed * invTrans) / 255;
1478                                 uint32 bGreen = (eGreen * trans + nGreen * invTrans) / 255;
1479                                 uint32 bBlue = (eBlue * trans + nBlue * invTrans) / 255;
1480
1481                                 uint32 blendedColor = 0xFF000000 | bRed | (bGreen << 8) | (bBlue << 16);
1482
1483                                 *(screen + address + xx + (yy * pitch)) = blendedColor;
1484
1485                                 alpha++;
1486                         }
1487
1488                         bitmap++;
1489                 }
1490         }
1491 }*/
1492 void DrawTransparentBitmapDeprecated(uint32 * screen, uint32 x, uint32 y, uint32 * bitmap)
1493 {
1494         uint32 width = bitmap[0], height = bitmap[1];
1495         bitmap += 2;
1496
1497         uint32 pitch = sdlemuGetOverlayWidthInPixels();//GetSDLScreenWidthInPixels();
1498         uint32 address = x + (y * pitch);
1499
1500         for(uint32 yy=0; yy<height; yy++)
1501         {
1502                 for(uint32 xx=0; xx<width; xx++)
1503                 {
1504                         uint32 color = *bitmap;
1505                         uint32 blendedColor = color;
1506                         uint32 existingColor = *(screen + address + xx + (yy * pitch));
1507
1508                         if (existingColor >> 24 != 0x00)                // Pixel needs blending
1509                         {
1510                                 uint8 trans = color >> 24;
1511                                 uint8 invTrans = trans ^ 0xFF;//255 - trans;
1512
1513                                 uint8 eRed = existingColor & 0xFF,
1514                                         eGreen = (existingColor >> 8) & 0xFF,
1515                                         eBlue = (existingColor >> 16) & 0xFF,
1516
1517                                         nRed = color & 0xFF,
1518                                         nGreen = (color >> 8) & 0xFF,
1519                                         nBlue = (color >> 16) & 0xFF;
1520
1521                                 uint32 bRed = (eRed * invTrans + nRed * trans) / 255;
1522                                 uint32 bGreen = (eGreen * invTrans + nGreen * trans) / 255;
1523                                 uint32 bBlue = (eBlue * invTrans + nBlue * trans) / 255;
1524
1525                                 blendedColor = 0xFF000000 | bRed | (bGreen << 8) | (bBlue << 16);
1526                         }
1527
1528                         *(screen + address + xx + (yy * pitch)) = blendedColor;
1529                         bitmap++;
1530                 }
1531         }
1532 }
1533
1534 void DrawTransparentBitmap(uint32 * screen, uint32 x, uint32 y, const void * bitmap)
1535 {
1536         uint32 pitch = sdlemuGetOverlayWidthInPixels();
1537         uint32 address = x + (y * pitch);
1538         uint32 count = 0;
1539
1540         for(uint32 yy=0; yy<((Bitmap *)bitmap)->height; yy++)
1541         {
1542                 for(uint32 xx=0; xx<((Bitmap *)bitmap)->width; xx++)
1543                 {
1544                         uint32 color = ((uint32 *)((Bitmap *)bitmap)->pixelData)[count];
1545                         uint32 blendedColor = color;
1546                         uint32 existingColor = *(screen + address + xx + (yy * pitch));
1547
1548                         if (existingColor >> 24 != 0x00)        // Pixel needs blending
1549                         {
1550                                 uint8 trans = color >> 24;
1551                                 uint8 invTrans = trans ^ 0xFF;
1552
1553                                 uint8 eRed = existingColor & 0xFF,
1554                                         eGreen = (existingColor >> 8) & 0xFF,
1555                                         eBlue = (existingColor >> 16) & 0xFF,
1556
1557                                         nRed = color & 0xFF,
1558                                         nGreen = (color >> 8) & 0xFF,
1559                                         nBlue = (color >> 16) & 0xFF;
1560
1561                                 uint32 bRed = (eRed * invTrans + nRed * trans) / 255;
1562                                 uint32 bGreen = (eGreen * invTrans + nGreen * trans) / 255;
1563                                 uint32 bBlue = (eBlue * invTrans + nBlue * trans) / 255;
1564
1565 // Instead of $FF, should use the alpha from the destination pixel as the final alpha value...
1566                                 blendedColor = 0xFF000000 | bRed | (bGreen << 8) | (bBlue << 16);
1567                         }
1568
1569                         *(screen + address + xx + (yy * pitch)) = blendedColor;
1570                         count++;
1571                 }
1572         }
1573 }
1574
1575 //
1576 // Draw a bitmap without using blending
1577 //
1578 void DrawBitmap(uint32 * screen, uint32 x, uint32 y, const void * bitmap)
1579 {
1580         uint32 pitch = sdlemuGetOverlayWidthInPixels();
1581         uint32 address = x + (y * pitch);
1582         uint32 count = 0;
1583
1584         for(uint32 yy=0; yy<((Bitmap *)bitmap)->height; yy++)
1585         {
1586                 for(uint32 xx=0; xx<((Bitmap *)bitmap)->width; xx++)
1587                 {
1588                         *(screen + address + xx + (yy * pitch)) = ((uint32 *)((Bitmap *)bitmap)->pixelData)[count];
1589                         count++;
1590                 }
1591         }
1592 }
1593
1594 //
1595 // Fill a portion of the screen with the passed in color
1596 //
1597 void FillScreenRectangle(uint32 * screen, uint32 x, uint32 y, uint32 w, uint32 h, uint32 color)
1598 //void ClearScreenRectangle(uint32 * screen, uint32 x, uint32 y, uint32 w, uint32 h)
1599 {
1600         uint32 pitch = sdlemuGetOverlayWidthInPixels();
1601         uint32 address = x + (y * pitch);
1602
1603         for(uint32 yy=0; yy<h; yy++)
1604                 for(uint32 xx=0; xx<w; xx++)
1605                         *(screen + address + xx + (yy * pitch)) = color;
1606 }
1607
1608
1609 //
1610 // GUI stuff--it's not crunchy, it's GUI! ;-)
1611 //
1612
1613 void GUIInit(void)
1614 {
1615         SDL_ShowCursor(SDL_DISABLE);
1616         SDL_GetMouseState(&mouseX, &mouseY);
1617 }
1618
1619 void GUIDone(void)
1620 {
1621 }
1622
1623 //
1624 // GUI main loop
1625 //
1626 //bool GUIMain(void)
1627 bool GUIMain(char * filename)
1628 {
1629 WriteLog("GUI: Inside GUIMain...\n");
1630
1631         uint32 pointerBGSave[6 * 8 + 2];
1632         pointerBGSave[0] = 6;
1633         pointerBGSave[1] = 8;
1634
1635 // Need to set things up so that it loads and runs a file if given on the command line. !!! FIX !!! [DONE]
1636 //      extern uint32 * backbuffer;
1637 //      bool done = false;
1638         SDL_Event event;
1639         Window * mainWindow = NULL;
1640
1641         // Set up the GUI classes...
1642 //      Element::SetScreenAndPitch(backbuffer, GetSDLScreenWidthInPixels());
1643         Element::SetScreenAndPitch((uint32 *)sdlemuGetOverlayPixels(), sdlemuGetOverlayWidthInPixels());
1644         sdlemuEnableOverlay();
1645
1646         Menu mainMenu;
1647         MenuItems mi;
1648         mi.title = "Jaguar";
1649         mi.item.push_back(NameAction("Load...", LoadROM, SDLK_l));
1650         mi.item.push_back(NameAction("Reset", ResetJaguar, SDLK_r));
1651         if (CDBIOSLoaded)
1652                 mi.item.push_back(NameAction("Reset CD", ResetJaguarCD, SDLK_c));
1653         mi.item.push_back(NameAction("Run", RunEmu, SDLK_ESCAPE));
1654         mi.item.push_back(NameAction(""));
1655         mi.item.push_back(NameAction("Quit", Quit, SDLK_q));
1656         mainMenu.Add(mi);
1657         mi.title = "Settings";
1658         mi.item.clear();
1659         mi.item.push_back(NameAction("Video..."));
1660         mi.item.push_back(NameAction("Audio..."));
1661         mi.item.push_back(NameAction("Misc...", MiscOptions, SDLK_m));
1662         mainMenu.Add(mi);
1663         mi.title = "Info";
1664         mi.item.clear();
1665         mi.item.push_back(NameAction("About...", About));
1666         mainMenu.Add(mi);
1667
1668         bool showMouse = true;
1669
1670         // Grab the BG where the mouse will be painted (prime the backstore)
1671
1672 /*
1673 DISNOWOK
1674 Bitmap ptr = { 6, 8, 4,
1675 ""//"000011112222333344445555"
1676 //"000011112222333344445555"
1677 //"000011112222333344445555"
1678 //"000011112222333344445555"
1679 //"000011112222333344445555"
1680 //"000011112222333344445555"
1681 //"000011112222333344445555"
1682 //"000011112222333344445555"
1683 };//*/
1684         uint32 * overlayPixels = (uint32 *)sdlemuGetOverlayPixels();
1685         uint32 count = 2;
1686
1687         for(uint32 y=0; y<pointerBGSave[1]; y++)
1688                 for(uint32 x=0; x<pointerBGSave[0]; x++)
1689                         pointerBGSave[count++] = overlayPixels[((mouseY + y) * sdlemuGetOverlayWidthInPixels()) + (mouseX + x)];
1690
1691         uint32 oldMouseX = mouseX, oldMouseY = mouseY;
1692
1693 //This is crappy!!! !!! FIX !!!
1694 //Is this even needed any more? Hmm. Maybe. Dunno.
1695 WriteLog("GUI: Resetting Jaguar...\n");
1696         JaguarReset();
1697
1698 WriteLog("GUI: Clearing BG save...\n");
1699         // Set up our background save...
1700 //      memset(background, 0x11, tom_getVideoModeWidth() * 240 * 2);
1701 //1111 -> 000100 01000 10001 -> 0001 0000 0100 0010 1000 1100 -> 10 42 8C
1702         for(uint32 i=0; i<TOMGetVideoModeWidth()*240; i++)
1703 //              background[i] = 0xFF8C4210;
1704                 backbuffer[i] = 0xFF8C4210;
1705
1706 /*      uint32 * overlayPix = (uint32 *)sdlemuGetOverlayPixels();
1707         for(uint32 i=0; i<sdlemuGetOverlayWidthInPixels()*480; i++)
1708                 overlayPix[i] = 0x00000000;*/
1709
1710         // Handle loading file passed in on the command line...! [DONE]
1711
1712         if (filename)
1713         {
1714                 if (JaguarLoadFile(filename))
1715                 {
1716 //                      event.type = SDL_USEREVENT, event.user.code = MENU_ITEM_CHOSEN;
1717 //                      event.user.data1 = (void *)ResetJaguar;
1718 //              SDL_PushEvent(&event);
1719                         // Make it so that if passed in on the command line, we quit right
1720                         // away when pressing ESC
1721 WriteLog("GUI: Bypassing GUI since ROM passed in on command line...\n");
1722                         ResetJaguar();
1723                         return true;
1724                 }
1725                 else
1726                 {
1727                         // Create error dialog...
1728                         char errText[1024];
1729                         sprintf(errText, "The file %40s could not be loaded.", filename);
1730
1731                         mainWindow = new Window(8, 16, 304, 160);
1732                         mainWindow->AddElement(new Text(8, 8, "Error!"));
1733                         mainWindow->AddElement(new Text(8, 24, errText));
1734                 }
1735         }
1736
1737 WriteLog("GUI: Entering main loop...\n");
1738         while (!exitGUI)
1739         {
1740                 if (SDL_PollEvent(&event))
1741                 {
1742                         if (event.type == SDL_USEREVENT)
1743                         {
1744                                 if (event.user.code == WINDOW_CLOSE)
1745                                 {
1746                                         delete mainWindow;
1747                                         mainWindow = NULL;
1748                                 }
1749                                 else if (event.user.code == MENU_ITEM_CHOSEN)
1750                                 {
1751                                         // Confused? Let me enlighten... What we're doing here is casting
1752                                         // data1 as a pointer to a function which returns a Window pointer and
1753                                         // which takes no parameters (the "(Window *(*)(void))" part), then
1754                                         // derefencing it (the "*" in front of that) in order to call the
1755                                         // function that it points to. Clear as mud? Yeah, I hate function
1756                                         // pointers too, but what else are you gonna do?
1757                                         mainWindow = (*(Window *(*)(void))event.user.data1)();
1758
1759                                         while (SDL_PollEvent(&event));  // Flush the event queue...
1760                                         event.type = SDL_MOUSEMOTION;
1761                                         int mx, my;
1762                                         SDL_GetMouseState(&mx, &my);
1763                                         event.motion.x = mx, event.motion.y = my;
1764                                     SDL_PushEvent(&event);                      // & update mouse position...!
1765
1766                                         oldMouseX = mouseX, oldMouseY = mouseY;
1767                                         mouseX = mx, mouseY = my;               // This prevents "mouse flash"...
1768                                 }
1769                         }
1770                         else if (event.type == SDL_ACTIVEEVENT)
1771                         {
1772                                 if (event.active.state == SDL_APPMOUSEFOCUS)
1773                                         showMouse = (event.active.gain ? true : false);
1774                         }
1775                         else if (event.type == SDL_KEYDOWN)
1776                         {
1777 // Ugly kludge for windowed<-->fullscreen switching...
1778 uint8 * keystate = SDL_GetKeyState(NULL);
1779
1780 if ((keystate[SDLK_LALT] || keystate[SDLK_RALT]) & keystate[SDLK_RETURN])
1781         ToggleFullscreen();
1782
1783                                 if (mainWindow)
1784                                         mainWindow->HandleKey(event.key.keysym.sym);
1785                                 else
1786                                         mainMenu.HandleKey(event.key.keysym.sym);
1787                         }
1788                         else if (event.type == SDL_MOUSEMOTION)
1789                         {
1790                                 oldMouseX = mouseX, oldMouseY = mouseY;
1791                                 mouseX = event.motion.x, mouseY = event.motion.y;
1792
1793                                 if (mainWindow)
1794                                         mainWindow->HandleMouseMove(mouseX, mouseY);
1795                                 else
1796                                         mainMenu.HandleMouseMove(mouseX, mouseY);
1797                         }
1798                         else if (event.type == SDL_MOUSEBUTTONDOWN)
1799                         {
1800                                 uint32 mx = event.button.x, my = event.button.y;
1801
1802                                 if (mainWindow)
1803                                         mainWindow->HandleMouseButton(mx, my, true);
1804                                 else
1805                                         mainMenu.HandleMouseButton(mx, my, true);
1806                         }
1807                         else if (event.type == SDL_MOUSEBUTTONUP)
1808                         {
1809                                 uint32 mx = event.button.x, my = event.button.y;
1810
1811                                 if (mainWindow)
1812                                         mainWindow->HandleMouseButton(mx, my, false);
1813                                 else
1814                                         mainMenu.HandleMouseButton(mx, my, false);
1815                         }
1816
1817 //PROBLEM: In order to use the dirty rectangle approach here, we need some way of
1818 //         handling it in mainMenu.Draw() and mainWindow->Draw(). !!! FIX !!!
1819 //POSSIBLE SOLUTION:
1820 // When mouse is moving and not on menu or window, can do straight dirty rect.
1821 // When mouse is on menu, need to update screen. Same for buttons on windows...
1822 // What the menu & windows should do is only redraw on a state change. IOW, they
1823 // should call their own/child window's Draw() function instead of doing it top
1824 // level.
1825 //#define NEW_BACKSTORE_METHOD
1826
1827                         // Draw the GUI...
1828 // The way we do things here is kinda stupid (redrawing the screen every frame), but
1829 // it's simple. Perhaps there may be a reason down the road to be more selective with
1830 // our clearing, but for now, this will suffice.
1831 //                      memset(backbuffer, 0x11, tom_getVideoModeWidth() * 240 * 2);
1832 //                      memcpy(backbuffer, background, tom_getVideoModeWidth() * 256 * 2);
1833 //                      memcpy(backbuffer, background, tom_getVideoModeWidth() * 256 * 4);
1834 #ifndef NEW_BACKSTORE_METHOD
1835                         memset(sdlemuGetOverlayPixels(), 0, sdlemuGetOverlayWidthInPixels() * 480 * 4);
1836
1837                         mainMenu.Draw();
1838 //Could do multiple windows here by using a vector + priority info...
1839 //Though the way ZSNES does it seems to be by a bool (i.e., they're always active, just not shown)
1840                         if (mainWindow)
1841                                 mainWindow->Draw();
1842 #endif
1843
1844 /*uint32 pBGS[6 * 8 + 3] = { 6, 8, 4,
1845         0, 0, 0, 0, 0, 0,
1846         0, 0, 0, 0, 0, 0,
1847         0, 0, 0, 0, 0, 0,
1848         0, 0, 0, 0, 0, 0,
1849         0, 0, 0, 0, 0, 0,
1850         0, 0, 0, 0, 0, 0,
1851         0, 0, 0, 0, 0, 0,
1852         0, 0, 0, 0, 0, 0
1853 };*/
1854 //This isn't working... Why????
1855 //It's because DrawTransparentBitmap does alpha blending if it detects zero in the alpha channel.
1856 //So why do it that way? Hm.
1857                         overlayPixels = (uint32 *)sdlemuGetOverlayPixels();
1858
1859 #ifdef NEW_BACKSTORE_METHOD
1860 //                      DrawTransparentBitmapDeprecated(overlayPixels, oldMouseX, oldMouseY, pointerBGSave);
1861 //                      DrawTransparentBitmap(overlayPixels, oldMouseX, oldMouseY, pBGS);
1862                         for(uint32 y=0; y<pointerBGSave[1]; y++)
1863                                 for(uint32 x=0; x<pointerBGSave[0]; x++)
1864                                         overlayPixels[((oldMouseY + y) * sdlemuGetOverlayWidthInPixels()) + (oldMouseX + x)] = 0x00000000;
1865
1866                         count = 2;
1867
1868                         for(uint32 y=0; y<pointerBGSave[1]; y++)
1869                                 for(uint32 x=0; x<pointerBGSave[0]; x++)
1870                                         pointerBGSave[count++] = overlayPixels[((mouseY + y) * sdlemuGetOverlayWidthInPixels()) + (mouseX + x)];
1871 #endif
1872
1873                         if (showMouse)
1874 //                              DrawTransparentBitmapDeprecated(backbuffer, mouseX, mouseY, mousePic);
1875                                 DrawTransparentBitmapDeprecated(overlayPixels, mouseX, mouseY, mousePic);
1876
1877                         RenderBackbuffer();
1878                 }
1879         }
1880
1881         return true;
1882 }
1883
1884 //
1885 // GUI "action" functions
1886 //
1887
1888 Window * LoadROM(void)
1889 {
1890         FileList * fileList = new FileList(20, 20, 600, 440);
1891
1892         return (Window *)fileList;
1893 }
1894
1895 Window * ResetJaguar(void)
1896 {
1897         JaguarReset();
1898
1899         return RunEmu();
1900 }
1901
1902 Window * ResetJaguarCD(void)
1903 {
1904         memcpy(jaguarMainRom, jaguarCDBootROM, 0x40000);
1905         jaguarRunAddress = 0x802000;
1906         jaguarMainRomCRC32 = crc32_calcCheckSum(jaguarMainRom, 0x40000);
1907         JaguarReset();
1908 //This is a quick kludge to get the CDBIOS to boot properly...
1909 //Wild speculation: It could be that this memory location is wired into the CD unit
1910 //somehow, which lets it know whether or not a cart is present in the unit...
1911         jaguarMainRom[0x0040B] = 0x03;
1912
1913         return RunEmu();
1914 }
1915
1916
1917 #if 0
1918
1919 bool debounceRunKey = true;
1920 Window * RunEmu(void)
1921 {
1922         extern uint32 * backbuffer;
1923 //Temporary, to test the new timer based code...
1924 sdlemuDisableOverlay();
1925 JaguarExecuteNew();
1926 sdlemuEnableOverlay();
1927         // Save the background for the GUI...
1928         // In this case, we squash the color to monochrome, then force it to blue + green...
1929         for(uint32 i=0; i<tom_getVideoModeWidth() * 256; i++)
1930         {
1931                 uint32 pixel = backbuffer[i];
1932                 uint8 b = (pixel >> 16) & 0xFF, g = (pixel >> 8) & 0xFF, r = pixel & 0xFF;
1933                 pixel = ((r + g + b) / 3) & 0x00FF;
1934                 backbuffer[i] = 0xFF000000 | (pixel << 16) | (pixel << 8);
1935         }
1936 return NULL;//*/
1937
1938 //This is crappy... !!! FIX !!!
1939         extern bool finished, showGUI;
1940
1941 //      uint32 nFrame = 0, nFrameskip = 0;
1942         uint32 totalFrames = 0;
1943         finished = false;
1944         bool showMessage = true;
1945         uint32 showMsgFrames = 120;
1946         uint8 transparency = 0;
1947         // Pass a message to the "joystick" code to debounce the ESC key...
1948         debounceRunKey = true;
1949
1950         uint32 cartType = 4;
1951         if (jaguarRomSize == 0x200000)
1952                 cartType = 0;
1953         else if (jaguarRomSize == 0x400000)
1954                 cartType = 1;
1955         else if (jaguar_mainRom_crc32 == 0x687068D5)
1956                 cartType = 2;
1957         else if (jaguar_mainRom_crc32 == 0x55A0669C)
1958                 cartType = 3;
1959
1960         char * cartTypeName[5] = { "2M Cartridge", "4M Cartridge", "CD BIOS", "CD Dev BIOS", "Homebrew" };
1961         uint32 elapsedTicks = SDL_GetTicks(), frameCount = 0, framesPerSecond = 0;
1962
1963         while (true)
1964         {
1965                 // Set up new backbuffer with new pixels and data
1966                 JaguarExecute(backbuffer, true);
1967 //              JaguarExecuteNew();
1968                 totalFrames++;
1969 //WriteLog("Frame #%u...\n", totalFrames);
1970 //extern bool doDSPDis;
1971 //if (totalFrames == 373)
1972 //      doDSPDis = true;
1973
1974 //This sucks... !!! FIX !!!
1975                 joystick_exec();
1976 //This is done here so that the crud below doesn't get on our GUI background...
1977                 if (finished)
1978                         break;
1979
1980                 // Some QnD GUI stuff here...
1981                 if (showGUI)
1982                 {
1983                         extern uint32 gpu_pc, dsp_pc;
1984                         DrawString(backbuffer, 8, 8, false, "GPU PC: %08X", gpu_pc);
1985                         DrawString(backbuffer, 8, 16, false, "DSP PC: %08X", dsp_pc);
1986                         DrawString(backbuffer, 8, 32, false, "%u FPS", framesPerSecond);
1987                 }
1988
1989                 if (showMessage)
1990                 {
1991 // FF0F -> 1111 11 11  000 0 1111 -> 3F 18 0F
1992 // 3FE3 -> 0011 11 11  111 0 0011 -> 0F 3F 03
1993 /*                      DrawStringTrans((uint32 *)backbuffer, 8, 24*8, 0xFF0F, transparency, "Running...");
1994                         DrawStringTrans((uint32 *)backbuffer, 8, 26*8, 0x3FE3, transparency, "%s, run address: %06X", cartTypeName[cartType], jaguarRunAddress);
1995                         DrawStringTrans((uint32 *)backbuffer, 8, 27*8, 0x3FE3, transparency, "CRC: %08X", jaguar_mainRom_crc32);//*/
1996 //first has wrong color. !!! FIX !!!
1997                         DrawStringTrans(backbuffer, 8, 24*8, 0xFF7F63FF, transparency, "Running...");
1998                         DrawStringTrans(backbuffer, 8, 26*8, 0xFF1FFF3F, transparency, "%s, run address: %06X", cartTypeName[cartType], jaguarRunAddress);
1999                         DrawStringTrans(backbuffer, 8, 27*8, 0xFF1FFF3F, transparency, "CRC: %08X", jaguar_mainRom_crc32);
2000
2001                         if (showMsgFrames == 0)
2002                         {
2003                                 transparency++;
2004
2005                                 if (transparency == 33)
2006 {
2007                                         showMessage = false;
2008 /*extern bool doGPUDis;
2009 doGPUDis = true;//*/
2010 }
2011
2012                         }
2013                         else
2014                                 showMsgFrames--;
2015                 }
2016
2017                 RenderBackbuffer();
2018                 frameCount++;
2019
2020                 if (SDL_GetTicks() - elapsedTicks > 250)
2021                         elapsedTicks += 250, framesPerSecond = frameCount * 4, frameCount = 0;
2022         }
2023
2024         // Reset the pitch, since it may have been changed in-game...
2025         Element::SetScreenAndPitch((uint32 *)backbuffer, GetSDLScreenWidthInPixels());
2026
2027         // Save the background for the GUI...
2028 //      memcpy(background, backbuffer, tom_getVideoModeWidth() * 240 * 2);
2029         // In this case, we squash the color to monochrome, then force it to blue + green...
2030         for(uint32 i=0; i<tom_getVideoModeWidth() * 256; i++)
2031         {
2032                 uint32 pixel = backbuffer[i];
2033                 uint8 b = (pixel >> 16) & 0xFF, g = (pixel >> 8) & 0xFF, r = pixel & 0xFF;
2034                 pixel = ((r + g + b) / 3) & 0x00FF;
2035                 background[i] = 0xFF000000 | (pixel << 16) | (pixel << 8);
2036         }
2037
2038         return NULL;
2039 }
2040
2041 #else
2042
2043 bool debounceRunKey = true;
2044 Window * RunEmu(void)
2045 {
2046 //      extern uint32 * backbuffer;
2047         uint32 * overlayPixels = (uint32 *)sdlemuGetOverlayPixels();
2048         memset(overlayPixels, 0x00, 640 * 480 * 4);                     // Clear out overlay...
2049
2050 //This is crappy... !!! FIX !!!
2051 //      extern bool finished, showGUI;
2052
2053         sdlemuDisableOverlay();
2054
2055 //      uint32 nFrame = 0, nFrameskip = 0;
2056         uint32 totalFrames = 0;
2057         finished = false;
2058         bool showMessage = true;
2059         uint32 showMsgFrames = 120;
2060         uint8 transparency = 0xFF;
2061         // Pass a message to the "joystick" code to debounce the ESC key...
2062         debounceRunKey = true;
2063
2064         uint32 cartType = 4;
2065         if (jaguarRomSize == 0x200000)
2066                 cartType = 0;
2067         else if (jaguarRomSize == 0x400000)
2068                 cartType = 1;
2069         else if (jaguarMainRomCRC32 == 0x687068D5)
2070                 cartType = 2;
2071         else if (jaguarMainRomCRC32 == 0x55A0669C)
2072                 cartType = 3;
2073
2074         const char * cartTypeName[5] = { "2M Cartridge", "4M Cartridge", "CD BIOS", "CD Dev BIOS", "Homebrew" };
2075         uint32 elapsedTicks = SDL_GetTicks(), frameCount = 0, framesPerSecond = 0;
2076
2077         while (!finished)
2078         {
2079                 // Set up new backbuffer with new pixels and data
2080                 JaguarExecuteNew();
2081                 totalFrames++;
2082 //WriteLog("Frame #%u...\n", totalFrames);
2083 //extern bool doDSPDis;
2084 //if (totalFrames == 373)
2085 //      doDSPDis = true;
2086
2087 //Problem: Need to do this *only* when the state changes from visible to not...
2088 //Also, need to clear out the GUI when not on (when showMessage is active...)
2089 if (showGUI || showMessage)
2090         sdlemuEnableOverlay();
2091 else
2092         sdlemuDisableOverlay();
2093
2094 //Add in a new function for clearing patches of screen (ClearOverlayRect)
2095
2096 // Also: Take frame rate into account when calculating fade time...
2097
2098                 // Some QnD GUI stuff here...
2099                 if (showGUI)
2100                 {
2101                         FillScreenRectangle(overlayPixels, 8, 1*FONT_HEIGHT, 128, 4*FONT_HEIGHT, 0x00000000);
2102                         extern uint32 gpu_pc, dsp_pc;
2103                         DrawString(overlayPixels, 8, 1*FONT_HEIGHT, false, "GPU PC: %08X", gpu_pc);
2104                         DrawString(overlayPixels, 8, 2*FONT_HEIGHT, false, "DSP PC: %08X", dsp_pc);
2105                         DrawString(overlayPixels, 8, 4*FONT_HEIGHT, false, "%u FPS", framesPerSecond);
2106                 }
2107
2108                 if (showMessage)
2109                 {
2110                         DrawString2(overlayPixels, 8, 24*FONT_HEIGHT, 0x007F63FF, transparency, "Running...");
2111                         DrawString2(overlayPixels, 8, 26*FONT_HEIGHT, 0x001FFF3F, transparency, "%s, run address: %06X", cartTypeName[cartType], jaguarRunAddress);
2112                         DrawString2(overlayPixels, 8, 27*FONT_HEIGHT, 0x001FFF3F, transparency, "CRC: %08X", jaguarMainRomCRC32);
2113
2114                         if (showMsgFrames == 0)
2115                         {
2116                                 transparency--;
2117
2118                                 if (transparency == 0)
2119 {
2120                                         showMessage = false;
2121 /*extern bool doGPUDis;
2122 doGPUDis = true;//*/
2123 }
2124
2125                         }
2126                         else
2127                                 showMsgFrames--;
2128                 }
2129
2130                 frameCount++;
2131
2132                 if (SDL_GetTicks() - elapsedTicks > 250)
2133                         elapsedTicks += 250, framesPerSecond = frameCount * 4, frameCount = 0;
2134         }
2135
2136         // Save the background for the GUI...
2137         // In this case, we squash the color to monochrome, then force it to blue + green...
2138         for(uint32 i=0; i<TOMGetVideoModeWidth() * 256; i++)
2139         {
2140                 uint32 pixel = backbuffer[i];
2141                 uint8 b = (pixel >> 16) & 0xFF, g = (pixel >> 8) & 0xFF, r = pixel & 0xFF;
2142                 pixel = ((r + g + b) / 3) & 0x00FF;
2143                 backbuffer[i] = 0xFF000000 | (pixel << 16) | (pixel << 8);
2144         }
2145
2146         sdlemuEnableOverlay();
2147
2148         return NULL;
2149 }
2150
2151 #endif
2152
2153
2154 Window * Quit(void)
2155 {
2156         WriteLog("GUI: Quitting due to user request.\n");
2157         exitGUI = true;
2158
2159         return NULL;
2160 }
2161
2162 Window * About(void)
2163 {
2164         char buf[512];
2165         sprintf(buf, "SVN %s", __DATE__);
2166 //fprintf(fp, "VirtualJaguar v1.0.8 (Last full build was on %s %s)\n", __DATE__, __TIME__);
2167 //VirtualJaguar v1.0.8 (Last full build was on Dec 30 2004 20:01:31)
2168 //Hardwired, bleh... !!! FIX !!!
2169 uint32 width = 55 * FONT_WIDTH, height = 18 * FONT_HEIGHT;
2170 uint32 xpos = (640 - width) / 2, ypos = (480 - height) / 2;
2171 //      Window * window = new Window(8, 16, 50 * FONT_WIDTH, 21 * FONT_HEIGHT);
2172         Window * window = new Window(xpos, ypos, width, height);
2173 //      window->AddElement(new Text(8, 8, "Virtual Jaguar 1.0.8"));
2174 //      window->AddElement(new Text(8, 8, "Virtual Jaguar CVS 20050110", 0xFF3030FF, 0xFF000000));
2175 //      window->AddElement(new Text(208, 8+0*FONT_HEIGHT, buf, 0xFF3030FF, 0xFF000000));
2176         window->AddElement(new Text(248, 8+4*FONT_HEIGHT+5, buf, 0xFF3030FF, 0xFF000000));
2177         window->AddElement(new Text(8, 8+0*FONT_HEIGHT, "Coders:"));
2178         window->AddElement(new Text(16, 8+1*FONT_HEIGHT, "James L. Hammons (shamus)"));
2179         window->AddElement(new Text(16, 8+2*FONT_HEIGHT, "Niels Wagenaar (nwagenaar)"));
2180         window->AddElement(new Text(16, 8+3*FONT_HEIGHT, "Carwin Jones (Caz)"));
2181         window->AddElement(new Text(16, 8+4*FONT_HEIGHT, "Adam Green"));
2182         window->AddElement(new Text(8, 8+6*FONT_HEIGHT, "Testers:"));
2183         window->AddElement(new Text(16, 8+7*FONT_HEIGHT, "Guruma"));
2184         window->AddElement(new Text(8, 8+9*FONT_HEIGHT, "Thanks go out to:"));
2185         window->AddElement(new Text(16, 8+10*FONT_HEIGHT, "Aaron Giles for the original CoJag"));
2186         window->AddElement(new Text(16, 8+11*FONT_HEIGHT, "David Raingeard for the original VJ"));
2187         window->AddElement(new Text(16, 8+12*FONT_HEIGHT, "Karl Stenerud for his Musashi 68K emu"));
2188         window->AddElement(new Text(16, 8+13*FONT_HEIGHT, "Sam Lantinga for his amazing SDL libs"));
2189         window->AddElement(new Text(16, 8+14*FONT_HEIGHT, "Ryan C. Gordon for VJ's web presence"));
2190         window->AddElement(new Text(16, 8+15*FONT_HEIGHT, "Curt Vendel for various Jaguar goodies"));
2191         window->AddElement(new Text(16, 8+16*FONT_HEIGHT, "The guys over at Atari Age ;-)"));
2192 //      window->AddElement(new Image(8, 8, &vj_title_small));
2193         window->AddElement(new Image(width - (vj_title_small.width + 8), 8, &vj_title_small));
2194
2195         return window;
2196 }
2197
2198 Window * MiscOptions(void)
2199 {
2200         Window * window = new Window(8, 16, 304, 192);
2201         window->AddElement(new PushButton(8, 8, &vjs.useJaguarBIOS, "BIOS"));
2202         window->AddElement(new SlideSwitch(8, 32, &vjs.hardwareTypeNTSC, "PAL", "NTSC"));
2203         window->AddElement(new PushButton(8, 64, &vjs.DSPEnabled, "DSP"));
2204         window->AddElement(new SlideSwitch(24, 88, &vjs.usePipelinedDSP, "Original", "Pipelined"));
2205         window->AddElement(new SlideSwitch(8, 120, (bool *)&vjs.glFilter, "Sharp", "Blurry"));
2206         window->AddElement(new SlideSwitch(8, 152, (bool *)&vjs.renderType, "Normal render", "TV style"));
2207
2208         window->AddElement(new TextEdit(88, 8, vjs.ROMPath, 20, 0xFF8484FF, 0xFF000000));
2209
2210 /*TextEdit(uint32 x, uint32 y, string s, uint32 mss = 10, uint32 fg = 0xFF8484FF,
2211         uint32 bg = 0xFF84FF4D): Element(x, y, 0, 0), fgColor(fg), bgColor(bg), text(s),
2212         caretPos(0), maxScreenSize(mss) {}*/
2213 // Missing:
2214 // * BIOS path
2215 // * ROM path
2216 // * EEPROM path
2217 // * joystick
2218 // * joystick port
2219 // * OpenGL?
2220 // * GL Filter type
2221 // * Window/fullscreen
2222 // * Key definitions
2223
2224         return window;
2225 }
2226
2227 // Function prototype
2228 Window * CrashGracefullyCallback(void);
2229
2230 //NOTE: Probably should set a flag as well telling it to do a full reset
2231 //      of the Jaguar hardware if this happens...
2232 void GUICrashGracefully(const char * reason)
2233 {
2234         finished = true;                                                        // We're finished for now!
2235
2236         // Since this is used in the menu code as well, we could create another
2237         // internal function called "PushWindowOnQueue" or somesuch
2238         SDL_Event event;
2239         event.type = SDL_USEREVENT;
2240         event.user.code = MENU_ITEM_CHOSEN;
2241         event.user.data1 = (void *)CrashGracefullyCallback;
2242         SDL_PushEvent(&event);
2243 }
2244
2245 Window * CrashGracefullyCallback(void)
2246 {
2247         Window * window = new Window(8, 16, 304, 192);
2248
2249         window->AddElement(new Text(8, 8+0*FONT_HEIGHT, "We CRASHED!!!"));
2250
2251         return window;
2252 }