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