]> Shamusworld >> Repos - apple2/blob - src/gui/gui.cpp
Miscellaneous bugfixes.
[apple2] / src / gui / gui.cpp
1 //
2 // gui.cpp
3 //
4 // Graphical User Interface support
5 // by James Hammons
6 // © 2014-2019 Underground Software
7 //
8 // JLH = James Hammons <jlhamm@acm.org>
9 //
10 // WHO  WHEN        WHAT
11 // ---  ----------  -----------------------------------------------------------
12 // JLH  02/03/2006  Created this file
13 // JLH  03/13/2006  Added functions to allow shutting down GUI externally
14 // JLH  03/22/2006  Finalized basic multiple window support
15 // JLH  03/03/2014  Refactored GUI to use SDL 2, more modern approach as well
16 //
17 // STILL TO DO:
18 //
19 // - Memory leak on quitting with a window active [DONE]
20 // - Multiple window handling [DONE]
21 //
22
23 #include "gui.h"
24 #include <vector>
25 #include "apple2.h"
26 #include "config.h"
27 #include "diskselector.h"
28 #include "elements.h"
29 #include "floppydrive.h"
30 #include "font10pt.h"
31 #include "log.h"
32 #include "video.h"
33
34 // Icons, in GIMP "C" format
35 #include "gfx/icon-selection.c"
36 #include "gfx/disk-icon.c"
37 #include "gfx/power-off-icon.c"
38 #include "gfx/power-on-icon.c"
39 #include "gfx/disk-swap-icon.c"
40 #include "gfx/disk-door-open.c"
41 #include "gfx/disk-door-closed.c"
42 #include "gfx/save-state-icon.c"
43 #include "gfx/load-state-icon.c"
44 #include "gfx/config-icon.c"
45
46
47 // Okay, this is ugly but works and I can't think of any better way to handle
48 // this. So what we do when we pass the GIMP bitmaps into a function is pass
49 // them as a (void *) and then cast them as type (Bitmap *) in order to use
50 // them. Yes, it's ugly. Come up with something better!
51
52 struct Bitmap {
53         unsigned int width;
54         unsigned int height;
55         unsigned int bytesPerPixel;                                     // 3:RGB, 4:RGBA
56         unsigned char pixelData[];
57 };
58
59
60 const char numeralOne[(7 * 7) + 1] =
61         "  @@   "
62         " @@@   "
63         "@@@@   "
64         "  @@   "
65         "  @@   "
66         "  @@   "
67         "@@@@@@ ";
68
69 const char numeralTwo[(7 * 7) + 1] =
70         " @@@@@ "
71         "@@   @@"
72         "    @@@"
73         "  @@@@ "
74         " @@@   "
75         "@@     "
76         "@@@@@@@";
77
78 const char ejectIcon[(8 * 7) + 1] =
79         "   @@   "
80         "  @@@@  "
81         " @@@@@@ "
82         "@@@@@@@@"
83         "        "
84         "@@@@@@@@"
85         "@@@@@@@@";
86
87 const char newDiskIcon[(30 * 6) + 1] =
88         "@@   @@ @@@@@ @@   @@     @@  "
89         "@@@  @@ @@    @@   @@    @@@@ "
90         "@@@@ @@ @@@@  @@ @ @@   @@@@@@"
91         "@@ @@@@ @@    @@@@@@@     @@  "
92         "@@  @@@ @@    @@@@@@@  @@@@@  "
93         "@@   @@ @@@@@ @@   @@  @@@@   ";
94
95 const char driveLight[(5 * 5) + 1] =
96         " @@@ "
97         "@@@@@"
98         "@@@@@"
99         "@@@@@"
100         " @@@ ";
101
102
103 SDL_Texture * GUI::overlay = NULL;
104 SDL_Rect GUI::olDst;
105 int GUI::sidebarState = SBS_HIDDEN;
106 int32_t GUI::dx = 0;
107 int32_t GUI::iconSelected = -1;
108 bool GUI::hasKeyboardFocus = false;
109 bool GUI::powerOnState = true;
110
111 int32_t lastIconSelected = -1;
112 SDL_Texture * iconSelection = NULL;
113 SDL_Texture * diskIcon = NULL;
114 SDL_Texture * disk1Icon = NULL;
115 SDL_Texture * disk2Icon = NULL;
116 SDL_Texture * powerOnIcon = NULL;
117 SDL_Texture * powerOffIcon = NULL;
118 SDL_Texture * diskSwapIcon = NULL;
119 SDL_Texture * stateSaveIcon = NULL;
120 SDL_Texture * stateLoadIcon = NULL;
121 SDL_Texture * configIcon = NULL;
122 SDL_Texture * doorOpen = NULL;
123 SDL_Texture * doorClosed = NULL;
124 uint32_t texturePointer[128 * 380];
125 const char iconHelp[7][80] = { "Turn emulated Apple off/on",
126         "Insert floppy image into drive #1", "Insert floppy image into drive #2",
127         "Swap disks", "Save emulator state", "Load emulator state",
128         "Configure Apple2" };
129 bool disk1EjectHovered = false;
130 bool disk2EjectHovered = false;
131 bool disk1NewDiskHovered = false;
132 bool disk2NewDiskHovered = false;
133
134 SDL_Texture * GUI::charStamp = NULL;
135 uint32_t GUI::stamp[FONT_WIDTH * FONT_HEIGHT];
136
137 #define SIDEBAR_X_POS  (VIRTUAL_SCREEN_WIDTH - 80)
138
139 //std::vector<void *> objList;
140
141
142 GUI::GUI(void)
143 {
144 }
145
146
147 GUI::~GUI(void)
148 {
149 }
150
151
152 void GUI::Init(SDL_Renderer * renderer)
153 {
154         overlay = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888,
155                 SDL_TEXTUREACCESS_TARGET, 128, 380);
156         charStamp = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888,
157                 SDL_TEXTUREACCESS_TARGET, FONT_WIDTH, FONT_HEIGHT);
158
159         if (!overlay)
160         {
161                 WriteLog("GUI: Could not create overlay!\n");
162                 return;
163         }
164
165         if (SDL_SetTextureBlendMode(overlay, SDL_BLENDMODE_BLEND) == -1)
166                 WriteLog("GUI: Could not set blend mode for overlay.\n");
167
168         if (SDL_SetTextureBlendMode(charStamp, SDL_BLENDMODE_BLEND) == -1)
169                 WriteLog("GUI: Could not set blend mode for charStamp.\n");
170
171         for(uint32_t i=0; i<128*380; i++)
172                 texturePointer[i] = 0xB0A000A0;
173
174         SDL_UpdateTexture(overlay, NULL, texturePointer, 128 * sizeof(Uint32));
175
176         olDst.x = VIRTUAL_SCREEN_WIDTH;
177         olDst.y = 2;
178         olDst.w = 128;
179         olDst.h = 380;
180
181         iconSelection  = CreateTexture(renderer, &icon_selection);
182         diskIcon       = CreateTexture(renderer, &disk_icon);
183         doorOpen       = CreateTexture(renderer, &door_open);
184         doorClosed     = CreateTexture(renderer, &door_closed);
185         disk1Icon      = CreateTexture(renderer, &disk_icon);
186         disk2Icon      = CreateTexture(renderer, &disk_icon);
187         powerOffIcon   = CreateTexture(renderer, &power_off);
188         powerOnIcon    = CreateTexture(renderer, &power_on);
189         diskSwapIcon   = CreateTexture(renderer, &disk_swap);
190         stateSaveIcon  = CreateTexture(renderer, &save_state);
191         stateLoadIcon  = CreateTexture(renderer, &load_state);
192         configIcon     = CreateTexture(renderer, &config);
193
194         // Set up drive icons in their current states
195 //      AssembleDriveIcon(renderer, 0);
196 //      AssembleDriveIcon(renderer, 1);
197
198         if (SDL_SetRenderTarget(renderer, overlay) < 0)
199         {
200                 WriteLog("GUI: Could not set Render Target to overlay... (%s)\n", SDL_GetError());
201         }
202         else
203         {
204                 DrawSidebarIcons(renderer);
205                 // Set render target back to default
206                 SDL_SetRenderTarget(renderer, NULL);
207         }
208
209         DiskSelector::Init(renderer);
210         Config::Init(renderer);
211         WriteLog("GUI: Successfully initialized.\n");
212 }
213
214
215 SDL_Texture * GUI::CreateTexture(SDL_Renderer * renderer, const void * source)
216 {
217         Bitmap * bitmap = (Bitmap *)source;
218         SDL_Texture * texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888,
219 //              SDL_TEXTUREACCESS_STATIC, bitmap->width, bitmap->height);
220                 SDL_TEXTUREACCESS_TARGET, bitmap->width, bitmap->height);
221         SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
222         SDL_UpdateTexture(texture, NULL, (Uint32 *)bitmap->pixelData,
223                 bitmap->width * sizeof(Uint32));
224
225         return texture;
226 }
227
228
229 void GUI::MouseDown(int32_t x, int32_t y, uint32_t buttons)
230 {
231         DiskSelector::MouseDown(x, y, buttons);
232         Config::MouseDown(x, y, buttons);
233
234         if (sidebarState != SBS_SHOWN)
235                 return;
236
237         switch (iconSelected)
238         {
239         // Power
240         case 0:
241                 powerOnState = !powerOnState;
242                 SetPowerState();
243
244                 if (!powerOnState)
245                         SpawnMessage("*** POWER OFF ***");
246
247                 break;
248         // Disk #1
249         case 1:
250                 SpawnMessage("*** DISK #1 ***");
251
252 #if 0
253                 if (disk1EjectHovered && !floppyDrive[0].IsEmpty(0))
254                 {
255                         floppyDrive[0].EjectImage(0);
256                         SpawnMessage("*** DISK #1 EJECTED ***");
257                 }
258
259                 if (!disk1EjectHovered && !disk1NewDiskHovered)
260                 {
261                         // Load the disk selector
262                         Config::HideWindow();
263                         DiskSelector::ShowWindow(0);
264                 }
265 #else
266                 if (disk1EjectHovered)
267                 {
268                         if (!floppyDrive[0].IsEmpty(0))
269                         {
270                                 floppyDrive[0].EjectImage(0);
271                                 SpawnMessage("*** DISK #1 EJECTED ***");
272                         }
273                 }
274                 else
275                 {
276                         if (disk1NewDiskHovered)
277                         {
278                                 if (!floppyDrive[0].IsEmpty(0))
279                                         floppyDrive[0].EjectImage(0);
280
281                                 floppyDrive[0].CreateBlankImage(0);
282                         }
283                         else
284                         {
285                                 // Load the disk selector
286                                 Config::HideWindow();
287                                 DiskSelector::ShowWindow(0);
288                         }
289                 }
290 #endif
291
292                 break;
293         // Disk #2
294         case 2:
295                 SpawnMessage("*** DISK #2 ***");
296
297                 if (disk2EjectHovered && !floppyDrive[0].IsEmpty(1))
298                 {
299                         floppyDrive[0].EjectImage(1);
300                         SpawnMessage("*** DISK #2 EJECTED ***");
301                 }
302
303                 if (!disk2EjectHovered && !disk2NewDiskHovered)
304                 {
305                         // Load the disk selector
306                         Config::HideWindow();
307                         DiskSelector::ShowWindow(1);
308                 }
309
310                 break;
311         // Swap disks
312         case 3:
313                 floppyDrive[0].SwapImages();
314                 SpawnMessage("*** DISKS SWAPPED ***");
315                 break;
316         // Save state
317         case 4:
318                 SpawnMessage("*** SAVE STATE ***");
319                 break;
320         // Load state
321         case 5:
322                 SpawnMessage("*** LOAD STATE ***");
323                 break;
324         // Configuration
325         case 6:
326                 SpawnMessage("*** CONFIGURATION ***");
327                 // Load the configuration window
328                 DiskSelector::HideWindow();
329                 Config::ShowWindow();
330                 break;
331         }
332 }
333
334
335 void GUI::MouseUp(int32_t x, int32_t y, uint32_t buttons)
336 {
337         DiskSelector::MouseUp(x, y, buttons);
338         Config::MouseUp(x, y, buttons);
339 }
340
341
342 void GUI::MouseMove(int32_t x, int32_t y, uint32_t buttons)
343 {
344         DiskSelector::MouseMove(x, y, buttons);
345         Config::MouseMove(x, y, buttons);
346
347         if (sidebarState != SBS_SHOWN)
348         {
349                 iconSelected = -1;
350
351                 if (x > SIDEBAR_X_POS)
352                 {
353 //printf("GUI: sidebar showing (x = %i)...\n", x);
354                         sidebarState = SBS_SHOWING;
355                         dx = -8;
356                 }
357                 else
358                 {
359 //printf("GUI: sidebar hiding[1] (x = %i)...\n", x);
360                         sidebarState = SBS_HIDING;
361                         dx = 8;
362                 }
363         }
364         else
365         {
366                 if (x < SIDEBAR_X_POS)
367                 {
368                         iconSelected = lastIconSelected = -1;
369                         HandleIconSelection(sdlRenderer);
370 //printf("GUI: sidebar hiding[2] (x = %i)...\n", x);
371                         sidebarState = SBS_HIDING;
372                         dx = 8;
373                 }
374                 // We're in the right zone, and the sidebar is shown, so let's select
375                 // something!
376                 else
377                 {
378                         if (y < 4 || y > 383)
379                         {
380                                 iconSelected = -1;
381                         }
382                         else
383                                 iconSelected = (y - 4) / 54;
384
385                         // It's y+2 because the sidebar sits at (SIDEBAR_X_POS, 2)
386                         disk1EjectHovered = ((x >= (SIDEBAR_X_POS + 24 + 29))
387                                 && (x < (SIDEBAR_X_POS + 24 + 29 + 8))
388                                 && (y >= (63 + 31 + 2))
389                                 && (y < (63 + 31 + 2 + 7)) ? true : false);
390
391                         disk2EjectHovered = ((x >= (SIDEBAR_X_POS + 24 + 29))
392                                 && (x < (SIDEBAR_X_POS + 24 + 29 + 8))
393                                 && (y >= (117 + 31 + 2))
394                                 && (y < (117 + 31 + 2 + 7)) ? true : false);
395
396                         disk1NewDiskHovered = ((x >= (SIDEBAR_X_POS + 24 + 6))
397                                 && (x < (SIDEBAR_X_POS + 24 + 6 + 30))
398                                 && (y >= (63 + 31 + 2))
399                                 && (y < (63 + 31 + 2 + 6)) ? true : false);
400
401                         disk2NewDiskHovered = ((x >= (SIDEBAR_X_POS + 24 + 6))
402                                 && (x < (SIDEBAR_X_POS + 24 + 6 + 30))
403                                 && (y >= (117 + 31 + 2))
404                                 && (y < (117 + 31 + 2 + 6)) ? true : false);
405
406                         if (iconSelected != lastIconSelected)
407                         {
408                                 HandleIconSelection(sdlRenderer);
409                                 lastIconSelected = iconSelected;
410
411                                 if ((iconSelected >= 0) && (iconSelected <= 6))
412                                         SpawnMessage("%s", iconHelp[iconSelected]);
413
414                                 // Show what's in the selected drive
415                                 if (iconSelected >= 1 && iconSelected <= 2)
416                                 {
417                                         if (!floppyDrive[0].IsEmpty(iconSelected - 1))
418                                                 SpawnMessage("\"%s\"", floppyDrive[0].ImageName(iconSelected - 1));
419                                 }
420                         }
421                 }
422         }
423 }
424
425
426 bool GUI::KeyDown(uint32_t key)
427 {
428         return Config::KeyDown(key);
429 }
430
431
432 void GUI::HandleIconSelection(SDL_Renderer * renderer)
433 {
434         // Set up drive icons in their current states
435         AssembleDriveIcon(renderer, 0);
436         AssembleDriveIcon(renderer, 1);
437
438         // Reload the background...
439         SDL_UpdateTexture(overlay, NULL, texturePointer, 128 * sizeof(Uint32));
440
441         if (SDL_SetRenderTarget(renderer, overlay) < 0)
442         {
443                 WriteLog("GUI: Could not set Render Target to overlay... (%s)\n", SDL_GetError());
444                 return;
445         }
446
447         // Draw the icon selector, if an icon is selected
448         if (iconSelected >= 0)
449         {
450                 SDL_Rect dst;// = { 54, 54, 24 - 7, 2 };
451                 dst.w = dst.h = 54, dst.x = 24 - 7, dst.y = 2 + (iconSelected * 54);
452                 SDL_RenderCopy(renderer, iconSelection, NULL, &dst);
453         }
454
455         DrawSidebarIcons(renderer);
456
457         // Set render target back to default
458         SDL_SetRenderTarget(renderer, NULL);
459 }
460
461
462 void GUI::AssembleDriveIcon(SDL_Renderer * renderer, int driveNumber)
463 {
464         SDL_Texture * drive[2] = { disk1Icon, disk2Icon };
465         const char * number[2] = { numeralOne, numeralTwo };
466
467         if (SDL_SetRenderTarget(renderer, drive[driveNumber]) < 0)
468         {
469                 WriteLog("GUI: Could not set Render Target to overlay... (%s)\n", SDL_GetError());
470                 return;
471         }
472
473         SDL_RenderClear(renderer);
474         SDL_RenderCopy(renderer, diskIcon, NULL, NULL);
475
476         // Drive door @ (16, 7)
477         SDL_Rect dst;
478         dst.w = 8, dst.h = 10, dst.x = 16, dst.y = 7;
479         SDL_RenderCopy(renderer, (floppyDrive[0].IsEmpty(driveNumber) ?
480                 doorOpen : doorClosed), NULL, &dst);
481
482         // Numeral @ (30, 20)
483         DrawCharArray(renderer, number[driveNumber], 30, 20, 7, 7, 0xD0, 0xE0, 0xF0);
484         DrawDriveLight(renderer, driveNumber);
485         DrawEjectButton(renderer, driveNumber);
486         DrawNewDiskButton(renderer, driveNumber);
487
488         // Set render target back to default
489         SDL_SetRenderTarget(renderer, NULL);
490 }
491
492
493 void GUI::DrawEjectButton(SDL_Renderer * renderer, int driveNumber)
494 {
495         if (floppyDrive[0].IsEmpty(driveNumber))
496                 return;
497
498         uint8_t r = 0x00, g = 0xAA, b = 0x00;
499
500         if ((driveNumber == 0 && disk1EjectHovered)
501                 || (driveNumber == 1 && disk2EjectHovered))
502                 r = 0x20, g = 0xFF, b = 0x20;
503
504         DrawCharArray(renderer, ejectIcon, 29, 31, 8, 7, r, g, b);
505 }
506
507
508 void GUI::DrawNewDiskButton(SDL_Renderer * renderer, int driveNumber)
509 {
510         if (!floppyDrive[0].IsEmpty(driveNumber))
511                 return;
512
513         uint8_t r = 0x00, g = 0xAA, b = 0x00;
514
515         if ((driveNumber == 0 && disk1NewDiskHovered)
516                 || (driveNumber == 1 && disk2NewDiskHovered))
517                 r = 0x20, g = 0xFF, b = 0x20;
518
519         DrawCharArray(renderer, newDiskIcon, 6, 31, 30, 6, r, g, b);
520 }
521
522
523 void GUI::DrawDriveLight(SDL_Renderer * renderer, int driveNumber)
524 {
525         int lightState = floppyDrive[0].DriveLightStatus(driveNumber);
526         int r = 0x77, g = 0x00, b = 0x00;
527
528         if (lightState == DLS_READ)
529                 r = 0x20, g = 0xFF, b = 0x20;
530         else if (lightState == DLS_WRITE)
531                 r = 0xFF, g = 0x30, b = 0x30;
532
533         // Drive light @ (8, 21)
534         DrawCharArray(renderer, driveLight, 8, 21, 5, 5, r, g, b);
535 }
536
537
538 void GUI::DrawCharArray(SDL_Renderer * renderer, const char * array, int x,
539         int y, int w, int h, int r, int g, int b)
540 {
541         SDL_SetRenderDrawColor(renderer, r, g, b, 0xFF);
542
543         for(int j=0; j<h; j++)
544         {
545                 for(int i=0; i<w; i++)
546                 {
547                         if (array[(j * w) + i] != ' ')
548                                 SDL_RenderDrawPoint(renderer, x + i, y + j);
549                 }
550         }
551
552         SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00);
553 }
554
555
556 void GUI::DrawCharacter(SDL_Renderer * renderer, int x, int y, uint8_t c, bool invert/*= false*/)
557 {
558         uint32_t inv = (invert ? 0x000000FF : 0x00000000);
559         uint32_t pixel = 0xFFFFC000;    // RRGGBBAA
560         uint8_t * ptr = (uint8_t *)&font10pt[(c - 0x20) * FONT_WIDTH * FONT_HEIGHT];
561         SDL_Rect dst;
562         dst.x = x * FONT_WIDTH, dst.y = y * FONT_HEIGHT, dst.w = FONT_WIDTH, dst.h = FONT_HEIGHT;
563
564         for(int i=0; i<FONT_WIDTH*FONT_HEIGHT; i++)
565                 stamp[i] = (pixel | ptr[i]) ^ inv;
566
567         SDL_UpdateTexture(charStamp, NULL, stamp, FONT_WIDTH * sizeof(Uint32));
568         SDL_RenderCopy(renderer, charStamp, NULL, &dst);
569 }
570
571
572 //
573 // N.B.: This draws a char at an abosulte X/Y position, not on a grid
574 //
575 void GUI::DrawCharacterVert(SDL_Renderer * renderer, int x, int y, uint8_t c, bool invert/*= false*/)
576 {
577         uint32_t inv = (invert ? 0x000000FF : 0x00000000);
578         uint32_t pixel = 0xFFFFC000;    // RRGGBBAA
579         uint8_t * ptr = (uint8_t *)&font10pt[(c - 0x20) * FONT_WIDTH * FONT_HEIGHT];
580         SDL_Rect dst;
581         dst.x = x, dst.y = y, dst.w = FONT_WIDTH, dst.h = FONT_HEIGHT;
582         SDL_Point pt = { FONT_WIDTH - 1, FONT_HEIGHT - 1 };
583
584         for(int i=0; i<FONT_WIDTH*FONT_HEIGHT; i++)
585                 stamp[i] = (pixel | ptr[i]) ^ inv;
586
587         SDL_SetTextureBlendMode(charStamp, SDL_BLENDMODE_NONE);
588         SDL_UpdateTexture(charStamp, NULL, stamp, FONT_WIDTH * sizeof(Uint32));
589         SDL_RenderCopyEx(renderer, charStamp, NULL, &dst, 270.0, &pt, SDL_FLIP_NONE);
590         SDL_SetTextureBlendMode(charStamp, SDL_BLENDMODE_BLEND);
591 }
592
593
594 void GUI::DrawString(SDL_Renderer * renderer, int x, int y, const char * s, bool invert/*= false*/)
595 {
596         int len = strlen(s);
597
598         for(int i=0; i<len; i++)
599                 DrawCharacter(renderer, x + i, y, s[i], invert);
600 }
601
602
603 //
604 // N.B.: This draws a char at an absolute X/Y position, not on a grid
605 //
606 void GUI::DrawStringVert(SDL_Renderer * renderer, int x, int y, const char * s, bool invert/*= false*/)
607 {
608         int len = strlen(s);
609
610         for(int i=0; i<len; i++)
611                 DrawCharacterVert(renderer, x, y - (FONT_WIDTH * i), s[i], invert);
612 }
613
614
615 void GUI::DrawBox(SDL_Renderer * renderer, int x, int y, int w, int h, int r/*= 0x00*/, int g/*= 0xAA*/, int b/*= 0x00*/)
616 {
617         SDL_SetRenderDrawColor(renderer, r, g, b, 0xFF);
618
619         for(int i=0; i<w; i++)
620         {
621                 SDL_RenderDrawPoint(renderer, x + i, y);
622                 SDL_RenderDrawPoint(renderer, x + i, y + h - 1);
623         }
624
625         for(int i=0; i<h; i++)
626         {
627                 SDL_RenderDrawPoint(renderer, x, y + i);
628                 SDL_RenderDrawPoint(renderer, x + w - 1, y + i);
629         }
630
631         SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00);
632 }
633
634
635 void GUI::HandleGUIState(void)
636 {
637         olDst.x += dx;
638
639         if (olDst.x < SIDEBAR_X_POS && sidebarState == SBS_SHOWING)
640         {
641                 olDst.x = SIDEBAR_X_POS;
642                 sidebarState = SBS_SHOWN;
643                 dx = 0;
644         }
645         else if (olDst.x > VIRTUAL_SCREEN_WIDTH && sidebarState == SBS_HIDING)
646         {
647                 olDst.x = VIRTUAL_SCREEN_WIDTH;
648                 sidebarState = SBS_HIDDEN;
649                 dx = 0;
650         }
651 }
652
653
654 void GUI::DrawSidebarIcons(SDL_Renderer * renderer)
655 {
656         SDL_Texture * icons[7] = { powerOnIcon, disk1Icon, disk2Icon, diskSwapIcon,
657                 stateSaveIcon, stateLoadIcon, configIcon };
658
659         icons[0] = (powerOnState ? powerOnIcon : powerOffIcon);
660         SDL_Rect dst = { 24, 2 + 7, 40, 40 };
661
662         for(int i=0; i<7; i++)
663         {
664                 SDL_RenderCopy(renderer, icons[i], NULL, &dst);
665                 dst.y += 54;
666         }
667 }
668
669
670 void GUI::Render(SDL_Renderer * renderer)
671 {
672         // Sanity check
673         if (!overlay)
674                 return;
675
676         HandleGUIState();
677
678         if (sidebarState != SBS_HIDDEN)
679                 HandleIconSelection(renderer);
680
681         SDL_RenderCopy(renderer, overlay, NULL, &olDst);
682
683         // Hmm.
684         DiskSelector::Render(renderer);
685         Config::Render(renderer);
686 }
687
688
689 /*
690 GUI Considerations:
691
692 screen is 560 x 384
693
694 cut into 7 pieces give ~54 pix per piece
695 So, let's try 40x40 icons, and see if that's good enough...
696 Selection is 54x54.
697
698 drive proportions: 1.62 : 1
699
700 Icon order:
701
702 +-----+
703 |     |
704 |Power|
705 |     |
706 +-----+
707
708 +-----+
709 |     |
710 |Disk1|
711 |    ^| <-- eject button
712 +-----+
713
714 +-----+
715 |     |
716 |Disk2|
717 |    ^|
718 +-----+
719
720 +-----+
721 |     |
722 |Swap |
723 |     |
724 +-----+
725
726 +-----+
727 |     |
728 |Confg|
729 |     |
730 +-----+
731
732 maybe state save/load
733
734 */
735