]> Shamusworld >> Repos - apple2/blob - src/gui/diskselector.cpp
57014b5e3cb1986a6798a9f91058d9bc4d8e14f1
[apple2] / src / gui / diskselector.cpp
1 //
2 // diskselector.cpp
3 //
4 // Floppy disk selector GUI
5 // by James Hammons
6 // © 2014-2018 Underground Software
7 //
8 // JLH = James Hammons <jlhamm@acm.org>
9 //
10 // WHO  WHEN        WHAT
11 // ---  ----------  -----------------------------------------------------------
12 // JLH  10/13/2013  Created this file
13 //
14 // STILL TO DO:
15 //
16 // - Fix bug where hovering on scroll image causes it to fly across the screen
17 //   [DONE]
18 //
19
20 #include "diskselector.h"
21 #include <dirent.h>
22 #include <algorithm>
23 #include <string>
24 #include <vector>
25 #include "crc32.h"
26 #include "fileio.h"
27 #include "floppydrive.h"
28 #include "font10pt.h"
29 #include "gui.h"
30 #include "log.h"
31 #include "settings.h"
32 #include "video.h"
33
34 // Icons, in GIMP "C" format
35 #include "gfx/scroll-left.c"
36 #include "gfx/scroll-right.c"
37
38
39 struct Bitmap {
40         unsigned int width;
41         unsigned int height;
42         unsigned int bytesPerPixel;                                     // 3:RGB, 4:RGBA
43         unsigned char pixelData[];
44 };
45
46 enum { DSS_SHOWING, DSS_HIDING, DSS_SHOWN, DSS_HIDDEN, DSS_LSB_SHOWING, DSS_LSB_SHOWN, DSS_LSB_HIDING, DSS_RSB_SHOWING, DSS_RSB_SHOWN, DSS_RSB_HIDING, DSS_TEXT_SCROLLING };
47
48 #define DS_WIDTH                        402
49 #define DS_HEIGHT                       322
50 #define SCROLL_HOT_WIDTH        48
51 #define DS_XPOS ((VIRTUAL_SCREEN_WIDTH - DS_WIDTH) / 2)
52 #define DS_YPOS ((VIRTUAL_SCREEN_HEIGHT - DS_HEIGHT) / 2)
53
54
55 static bool entered = false;
56 static int driveNumber;
57 static int diskSelectorState = DSS_HIDDEN;
58 static int diskSelected = -1;
59 static int lastDiskSelected = -1;
60 static int numColumns;
61 static int colStart = 0;
62 static int dxLeft = 0;
63 static int dxRight = 0;
64 static int rsbPos = DS_WIDTH;
65 static int lsbPos = -40;
66 static int textScrollCount = 0;
67 static bool refresh = false;
68
69 /*
70 So, how this will work for multiple columns, where the number of columns is greater than 3, is to have an arrow button pop up on the left or right hand side (putting the mouse on the left or right side of the disk selector activates (shows) the button, if such a move can be made. Button hides when the mouse moves out of the hot zone or when it has no more effect.
71 */
72
73
74 // We make provision for sets of 32 or less...
75 /*
76 The way the manifests are laid out, we make the assumption that the boot disk of a set is always listed first.  Therefore, image[0] will always be the boot disk.
77 */
78 struct DiskSet
79 {
80         uint8_t num;                    // # of disks in this set
81         std::string name;               // The name of this disk set
82 //      std::string fullPath;   // The path to the containing folder
83         std::string image[32];  // List of disk images in this set
84         std::string imgName[32];// List of human readable names of disk images
85         uint32_t crc[32];               // List of CRC32s of the disk images in the set
86         uint32_t crcFound[32];  // List of CRC32s actually discovered on filesystem
87
88         DiskSet(): num(0) {}
89 };
90
91
92 //
93 // Struct to hold filenames & full paths to same
94 //
95 struct FileStruct
96 {
97         std::string image;
98         std::string fullPath;
99         DiskSet diskSet;
100
101 //      FileStruct(): diskSet(NULL) {}
102 //      ~FileStruct() { if (diskSet != NULL) delete diskSet; }
103
104         // Functor, to presumably make the std::sort go faster
105         bool operator()(const FileStruct & a, const FileStruct & b) const
106         {
107                 return (strcasecmp(a.image.c_str(), b.image.c_str()) < 0 ? true : false);
108         }
109 };
110
111
112 static SDL_Texture * window = NULL;
113 static uint32_t windowPixels[DS_WIDTH * DS_HEIGHT];
114 SDL_Texture * scrollLeftIcon = NULL;
115 SDL_Texture * scrollRightIcon = NULL;
116 bool DiskSelector::showWindow = false;
117 std::vector<FileStruct> fsList;
118 std::vector<FileStruct> hdList;
119
120
121 void DiskSelector::Init(SDL_Renderer * renderer)
122 {
123         window = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888,
124                 SDL_TEXTUREACCESS_TARGET, DS_WIDTH, DS_HEIGHT);
125
126         if (!window)
127         {
128                 WriteLog("GUI (DiskSelector): Could not create window!\n");
129                 return;
130         }
131
132         if (SDL_SetTextureBlendMode(window, SDL_BLENDMODE_BLEND) == -1)
133                 WriteLog("GUI (DiskSelector): Could not set blend mode for window.\n");
134
135         scrollLeftIcon  = GUI::CreateTexture(renderer, &scroll_left);
136         scrollRightIcon = GUI::CreateTexture(renderer, &scroll_right);
137
138         for(uint32_t i=0; i<DS_WIDTH*DS_HEIGHT; i++)
139                 windowPixels[i] = 0xEF007F00;
140
141         SDL_UpdateTexture(window, NULL, windowPixels, DS_WIDTH * sizeof(Uint32));
142         FindDisks();
143         FindHardDisks();
144         DrawFilenames(renderer);
145 }
146
147
148 //
149 // Find all disks images top level call
150 //
151 void DiskSelector::FindDisks(void)
152 {
153         fsList.clear();
154         FindDisks(settings.disksPath);
155         std::sort(fsList.begin(), fsList.end(), FileStruct());
156         // Calculate the number of columns in the file selector...
157         numColumns = (int)ceilf((float)fsList.size() / 27.0f);
158         WriteLog("GUI (DiskSelector)::FindDisks(): # of columns is %i (%i files)\n", numColumns, fsList.size());
159 }
160
161
162 /*
163 OK, so the way that you can determine if a file is a directory in a cross-
164 platform way is to do an opendir() call on a discovered filename.  If it
165 returns NULL, then it's a regular file and not a directory.  Though I think the
166 Linux method is more elegant.  :-P
167 */
168 //
169 // Find all disks images within path (recursive call does depth first search)
170 //
171 void DiskSelector::FindDisks(const char * path)
172 {
173         DIR * dir = opendir(path);
174
175         if (!dir)
176         {
177                 WriteLog("GUI (DiskSelector)::FindDisks: Could not open directory \"%s\%!\n", path);
178                 return;
179         }
180
181         dirent * ent;
182
183         while ((ent = readdir(dir)) != NULL)
184         {
185                 char buf[0x10000];
186                 sprintf(buf, "%s/%s", path, ent->d_name);
187
188                 // Cross-platform way to test if it's a directory...
189                 DIR * test = opendir(buf);
190
191 //              if ((ent->d_type == DT_REG) && HasLegalExtension(ent->d_name))
192                 if (test == NULL)
193                 {
194                         if (HasLegalExtension(ent->d_name))
195                         {
196                                 FileStruct fs;
197                                 fs.image = ent->d_name;
198                                 fs.fullPath = buf;
199                                 fsList.push_back(fs);
200                         }
201                 }
202 //              else if (ent->d_type == DT_DIR)
203                 else
204                 {
205                         // Make sure we close the thing, since it's a bona-fide dir!
206                         closedir(test);
207
208                         // Only recurse if the directory is not one of the special ones...
209                         if ((strcmp(ent->d_name, "..") != 0)
210                                 && (strcmp(ent->d_name, ".") != 0))
211                         {
212                                 // Check to see if this is a special directory with a manifest
213                                 char buf2[0x10000];
214                                 sprintf(buf2, "%s/manifest.txt", buf);
215                                 FILE * fp = fopen(buf2, "r");
216
217                                 // No manifest means it's just a regular directory...
218                                 if (fp == NULL)
219                                         FindDisks(buf);
220                                 else
221                                 {
222                                         // Read the manifest and all that good stuff
223                                         FileStruct fs;
224                                         ReadManifest(fp, &fs.diskSet);
225                                         fclose(fp);
226
227                                         // Finally, check that the stuff in the manifest is
228                                         // actually in the directory...
229                                         if (CheckManifest(buf, &fs.diskSet) == true)
230                                         {
231                                                 fs.fullPath = buf;
232                                                 fs.image = fs.diskSet.name;
233                                                 fsList.push_back(fs);
234                                         }
235                                         else
236                                                 WriteLog("Manifest for '%s' failed check phase.\n", fs.diskSet.name.c_str());
237 #if 0
238                                         printf("Name found: \"%s\" (%d)\nDisks:\n", fs.diskSet.name.c_str(), fs.diskSet.num);
239                                         for(int i=0; i<fs.diskSet.num; i++)
240                                                 printf("%s (CRC: %08X)\n", fs.diskSet.image[i].c_str(), fs.diskSet.crc[i]);
241 #endif
242
243                                 }
244                         }
245                 }
246         }
247
248         closedir(dir);
249 }
250
251
252 void DiskSelector::ReadManifest(FILE * fp, DiskSet * ds)
253 {
254         char line[0x10000];
255         int disksFound = 0;
256         int lineNo = 0;
257
258         while (!feof(fp))
259         {
260                 fgets(line, 0x10000, fp);
261                 lineNo++;
262
263                 if ((line[0] == '#') || (line[0] == '\n'))
264                         ; // Do nothing with comments or blank lines...
265                 else
266                 {
267                         char buf[1024];
268                         char crcbuf[16];
269                         char altName[1024];
270
271                         if (strncmp(line, "diskset", 7) == 0)
272                         {
273                                 sscanf(line, "diskset=\"%[^\"]\"", buf);
274                                 ds->name = buf;
275                         }
276                         else if (strncmp(line, "disks", 5) == 0)
277                         {
278                                 sscanf(line, "disks=%hhd", &ds->num);
279                         }
280                         else if (strncmp(line, "disk", 4) == 0)
281                         {
282                                 int n = sscanf(line, "disk=%s %s (%s)", buf, crcbuf, altName);
283
284                                 if ((n == 2) || (n == 3))
285                                 {
286                                         ds->image[disksFound] = buf;
287                                         ds->crc[disksFound] = strtoul(crcbuf, NULL, 16);
288                                         disksFound++;
289
290                                         if (n == 3)
291                                                 ds->imgName[disksFound] = altName;
292                                         else
293                                         {
294                                                 // Find the file's extension, if any
295                                                 char * ext = strrchr(buf, '.');
296
297                                                 // Kill the disk extension, if it exists
298                                                 if (ext != NULL)
299                                                         *ext = 0;
300
301                                                 ds->imgName[disksFound] = buf;
302                                         }
303                                 }
304                                 else
305                                         WriteLog("Malformed disk descriptor in manifest at line %d\n", lineNo);
306                         }
307                 }
308         }
309
310         if (disksFound != ds->num)
311                 WriteLog("Found only %d entries in manifest, expected %hhd\n", disksFound, ds->num);
312 }
313
314
315 bool DiskSelector::CheckManifest(const char * path, DiskSet * ds)
316 {
317         uint8_t found = 0;
318
319         for(int i=0; i<ds->num; i++)
320         {
321                 std::string filename = path;
322                 filename += "/";
323                 filename += ds->image[i];
324                 uint32_t size;
325                 uint8_t * buf = ReadFile(filename.c_str(), &size);
326
327                 if (buf != NULL)
328                 {
329                         ds->crcFound[i] = CRC32(buf, size);
330                         free(buf);
331                         found++;
332
333                         if (ds->crc[i] != ds->crcFound[i])
334                         {
335                                 WriteLog("Warning: Bad CRC32 for '%s'. Expected: %08X, found: %08X\n", ds->image[i], ds->crc[i], ds->crcFound[i]);
336                         }
337                 }
338         }
339
340         return (found == ds->num ? true : false);
341 }
342
343
344 bool DiskSelector::HasLegalExtension(const char * name)
345 {
346         // Find the file's extension, if any
347         const char * ext = strrchr(name, '.');
348
349         // No extension, so fuggetaboutit
350         if (ext == NULL)
351                 return false;
352
353         // Otherwise, look for a legal extension
354         // We should be smarter than this, and look at headers & file sizes instead
355         if ((strcasecmp(ext, ".dsk") == 0)
356                 || (strcasecmp(ext, ".do") == 0)
357                 || (strcasecmp(ext, ".po") == 0)
358                 || (strcasecmp(ext, ".woz") == 0))
359                 return true;
360
361         return false;
362 }
363
364
365 //
366 // Find all disks images top level call
367 //
368 void DiskSelector::FindHardDisks(void)
369 {
370         hdList.clear();
371         FindHardDisks(settings.disksPath);
372         std::sort(hdList.begin(), hdList.end(), FileStruct());
373         WriteLog("GUI (DiskSelector)::FindHardDisks(): # of HDs is %i\n", hdList.size());
374 }
375
376
377 //
378 // Find all hard disk images within path (recursive call does depth first search)
379 //
380 void DiskSelector::FindHardDisks(const char * path)
381 {
382         DIR * dir = opendir(path);
383
384         if (!dir)
385         {
386                 WriteLog("GUI (DiskSelector)::FindHardDisks: Could not open directory \"%s\%!\n", path);
387                 return;
388         }
389
390         dirent * ent;
391
392         while ((ent = readdir(dir)) != NULL)
393         {
394                 char buf[0x10000];
395                 sprintf(buf, "%s/%s", path, ent->d_name);
396
397                 // Cross-platform way to test if it's a directory (test = NULL -> file)
398                 DIR * test = opendir(buf);
399
400                 if (test == NULL)
401                 {
402                         const char * ext = strrchr(ent->d_name, '.');
403
404                         if ((ext != NULL)
405                                 && ((strcasecmp(ext, ".2mg") == 0)
406                                         || (strcasecmp(ext, ".hdv") == 0)))
407                         {
408                                 FileStruct fs;
409                                 fs.image = ent->d_name;
410                                 fs.fullPath = buf;
411                                 hdList.push_back(fs);
412                         }
413                 }
414                 else
415                 {
416                         // Make sure we close the thing, since it's a bona-fide dir!
417                         closedir(test);
418
419                         // Only recurse if the directory is not one of the special ones...
420                         if ((strcmp(ent->d_name, "..") != 0)
421                                 && (strcmp(ent->d_name, ".") != 0))
422                         {
423                                 FindHardDisks(buf);
424                         }
425                 }
426         }
427
428         closedir(dir);
429 }
430
431
432 void DiskSelector::DrawFilenames(SDL_Renderer * renderer)
433 {
434         if (SDL_SetRenderTarget(renderer, window) < 0)
435         {
436                 WriteLog("GUI: Could not set Render Target to overlay... (%s)\n", SDL_GetError());
437                 return;
438         }
439
440         // 3 columns of 16 chars apiece (with 8X16 font), 18 rows
441         // 3 columns of 18 chars apiece (with 7X12 font), 24 rows
442         // 3 columns of 21 chars apiece (with 6X11 font), 27 rows
443
444         unsigned int count = 0;
445         unsigned int fsStart = colStart * 27;
446         int offset = 0;
447
448         // Draw partial columns (for scrolling left/right)
449         // [could probably combine these...]
450         if (textScrollCount < 0)
451         {
452                 int partialColStart = (colStart - 1) * 27;
453                 offset = -1 * textScrollCount;
454
455                 for(unsigned int y=0; y<27; y++)
456                 {
457                         for(unsigned int i=22+textScrollCount, x=0; i<21; i++, x++)
458                         {
459                                 if (i >= fsList[partialColStart + y].image.length())
460                                         break;
461
462                                 GUI::DrawCharacter(renderer, x + 1, y + 1, fsList[partialColStart + y].image[i], false);
463                         }
464                 }
465         }
466         else if (textScrollCount > 0)
467         {
468                 offset = 22 - textScrollCount;
469
470                 for(unsigned int y=0; y<27; y++)
471                 {
472                         for(unsigned int i=textScrollCount, x=0; i<21; i++, x++)
473                         {
474                                 if (i >= fsList[fsStart + y].image.length())
475                                         break;
476
477                                 GUI::DrawCharacter(renderer, x + 1, y + 1, fsList[fsStart + y].image[i], false);
478                         }
479                 }
480
481                 fsStart += 27;
482         }
483
484         while (fsStart < fsList.size())
485         {
486 //              int currentX = (count / 18) * 17;
487 //              int currentY = (count % 18);
488 //              int currentX = (count / 24) * 19;
489 //              int currentY = (count % 24);
490                 int currentX = (count / 27) * 22;
491                 int currentY = (count % 27);
492
493 //              for(unsigned int i=0; i<16; i++)
494 //              for(unsigned int i=0; i<18; i++)
495                 for(unsigned int i=0; i<21; i++)
496                 {
497                         if (i >= fsList[fsStart].image.length())
498                                 break;
499
500                         bool invert = (diskSelected == (int)fsStart ? true : false);
501                         GUI::DrawCharacter(renderer, currentX + i + 1 + offset, currentY + 1, fsList[fsStart].image[i], invert);
502                 }
503
504                 count++;
505                 fsStart++;
506
507 //              if (count >= (18 * 3))
508 //              if (count >= (24 * 3))
509                 if (count >= (27 * 3))
510                         break;
511         }
512
513         // If a disk is selected, show it on the top line in inverse video
514         if (diskSelected > -1)
515         {
516                 for(unsigned int i=0; i<65; i++)
517                 {
518                         if (i >= fsList[diskSelected].image.length())
519                                 break;
520
521                         GUI::DrawCharacter(renderer, i + 1, 0, fsList[diskSelected].image[i], true);
522                 }
523         }
524
525         // Set render target back to default
526         SDL_SetRenderTarget(renderer, NULL);
527 }
528
529
530 void DiskSelector::ShowWindow(int drive)
531 {
532         diskSelectorState = DSS_SHOWN;
533         entered = false;
534         showWindow = true;
535         driveNumber = drive;
536 }
537
538
539 void DiskSelector::HideWindow(void)
540 {
541         diskSelectorState = DSS_HIDDEN;
542         dxLeft = 0;
543         dxRight = 0;
544         rsbPos = DS_WIDTH;
545         lsbPos = -40;
546         showWindow = false;
547         refresh = true;
548 }
549
550
551 void DiskSelector::MouseDown(int32_t x, int32_t y, uint32_t buttons)
552 {
553         if (!showWindow || !entered)
554                 return;
555
556         if ((diskSelectorState == DSS_LSB_SHOWING) || (diskSelectorState == DSS_LSB_SHOWN))
557         {
558                 colStart--;
559                 textScrollCount = 21;
560
561                 if (colStart == 0)
562                 {
563                         diskSelectorState = DSS_LSB_HIDING;
564                         dxLeft = -8;
565                 }
566
567                 return;
568         }
569
570         if ((diskSelectorState == DSS_RSB_SHOWING) || (diskSelectorState == DSS_RSB_SHOWN))
571         {
572                 colStart++;
573                 textScrollCount = -21;
574
575                 if ((colStart + 3) == numColumns)
576                 {
577                         diskSelectorState = DSS_RSB_HIDING;
578                         dxRight = 8;
579                 }
580
581                 return;
582         }
583
584         if (diskSelected != -1)
585         {
586                 floppyDrive[0].LoadImage(fsList[diskSelected].fullPath.c_str(), driveNumber);
587         }
588
589         showWindow = false;
590 }
591
592
593 void DiskSelector::MouseUp(int32_t x, int32_t y, uint32_t buttons)
594 {
595         if (!showWindow)
596                 return;
597
598 }
599
600
601 void DiskSelector::MouseMove(int32_t x, int32_t y, uint32_t buttons)
602 {
603         if (!showWindow)
604                 return;
605
606         // Check to see if DS has been hovered yet, and, if so, set a flag to show
607         // that it has
608         if (!entered && ((x >= DS_XPOS) && (x <= (DS_XPOS + DS_WIDTH))
609                 && (y >= DS_YPOS) && (y <= (DS_YPOS + DS_HEIGHT))))
610                 entered = true;
611
612         // Check to see if the DS, since being hovered, is now no longer being
613         // hovered
614 //N.B.: Should probably make like a 1/2 to 1 second timeout to allow for overshooting the edge of the thing, maybe have the window fade out gradually and let it come back if you enter before it leaves...
615         if (entered && ((x < DS_XPOS) || (x > (DS_XPOS + DS_WIDTH))
616                 || (y < DS_YPOS) || (y > (DS_YPOS + DS_HEIGHT))))
617         {
618                 diskSelectorState = DSS_HIDDEN;
619                 dxLeft = 0;
620                 dxRight = 0;
621                 rsbPos = DS_WIDTH;
622                 lsbPos = -40;
623                 showWindow = false;
624                 refresh = true;
625                 return;
626         }
627
628         // Bail out if the DS hasn't been entered yet
629         if (!entered)
630                 return;
631
632 /*
633 states:
634 +-----+---------------------+-----+
635 |     |                     |     |
636 |     |                     |     |
637 +-----+---------------------+-----+
638  ^           ^                ^
639  |           |                x is here and state is DSS_SHOWN
640  |           x is here and state is DSS_LSB_SHOWING or DSS_RSB_SHOWING
641  x is here and state is DSS_SHOWN
642
643 */
644         if (x < (DS_XPOS + SCROLL_HOT_WIDTH))
645         {
646                 if ((colStart > 0) && (diskSelectorState == DSS_SHOWN))
647                 {
648                         diskSelectorState = DSS_LSB_SHOWING;
649                         dxLeft = 8;
650                 }
651         }
652         else if (x > (DS_XPOS + DS_WIDTH - SCROLL_HOT_WIDTH))
653         {
654                 if (((colStart + 3) < numColumns) && (diskSelectorState == DSS_SHOWN))
655                 {
656                         diskSelectorState = DSS_RSB_SHOWING;
657                         dxRight = -8;
658                 }
659         }
660         else
661         {
662                 // Handle the excluded middle  :-P
663                 if ((diskSelectorState == DSS_LSB_SHOWING)
664                         || (diskSelectorState == DSS_LSB_SHOWN))
665                 {
666                         diskSelectorState = DSS_LSB_HIDING;
667                         dxLeft = -8;
668                 }
669                 else if ((diskSelectorState == DSS_RSB_SHOWING)
670                         || (diskSelectorState == DSS_RSB_SHOWN))
671                 {
672                         diskSelectorState = DSS_RSB_HIDING;
673                         dxRight = 8;
674                 }
675         }
676
677         // The -1 terms move the origin to the upper left corner (from 1 in, and 1
678         // down)
679         int xChar = ((x - DS_XPOS) / FONT_WIDTH) - 1;
680         int yChar = ((y - DS_YPOS) / FONT_HEIGHT) - 1;
681         diskSelected = ((xChar / 22) * 27) + yChar + (colStart * 27);
682
683         if ((yChar < 0) || (yChar >= 27)
684                 || (diskSelected >= (int)fsList.size())
685                 || (diskSelectorState == DSS_LSB_SHOWING)
686                 || (diskSelectorState == DSS_LSB_SHOWN)
687                 || (diskSelectorState == DSS_RSB_SHOWING)
688                 || (diskSelectorState == DSS_RSB_SHOWN))
689                 diskSelected = -1;
690
691         if (diskSelected != lastDiskSelected)
692         {
693                 HandleSelection(sdlRenderer);
694                 lastDiskSelected = diskSelected;
695         }
696 }
697
698
699 void DiskSelector::HandleGUIState(void)
700 {
701         lsbPos += dxLeft;
702         rsbPos += dxRight;
703
704         if ((lsbPos > (SCROLL_HOT_WIDTH - 40)) && (diskSelectorState == DSS_LSB_SHOWING))
705         {
706                 diskSelectorState = DSS_LSB_SHOWN;
707                 lsbPos = SCROLL_HOT_WIDTH - 40;
708                 dxLeft = 0;
709         }
710         else if ((lsbPos < -40) && (diskSelectorState == DSS_LSB_HIDING))
711         {
712                 diskSelectorState = DSS_SHOWN;
713                 lsbPos = -40;
714                 dxLeft = 0;
715         }
716         else if ((rsbPos < (DS_WIDTH - SCROLL_HOT_WIDTH)) && (diskSelectorState == DSS_RSB_SHOWING))
717         {
718                 diskSelectorState = DSS_RSB_SHOWN;
719                 rsbPos = DS_WIDTH - SCROLL_HOT_WIDTH;
720                 dxRight = 0;
721         }
722         else if ((rsbPos > DS_WIDTH) && (diskSelectorState == DSS_RSB_HIDING))
723         {
724                 diskSelectorState = DSS_SHOWN;
725                 rsbPos = DS_WIDTH;
726                 dxRight = 0;
727         }
728
729         if (textScrollCount < 0)
730         {
731                 textScrollCount += 2;
732
733                 if (textScrollCount > 0)
734                 {
735                         textScrollCount = 0;
736                         refresh = true;
737                 }
738         }
739         else if (textScrollCount > 0)
740         {
741                 textScrollCount -= 2;
742
743                 if (textScrollCount < 0)
744                 {
745                         textScrollCount = 0;
746                         refresh = true;
747                 }
748         }
749 }
750
751
752 void DiskSelector::HandleSelection(SDL_Renderer * renderer)
753 {
754         SDL_UpdateTexture(window, NULL, windowPixels, DS_WIDTH * sizeof(Uint32));
755         DrawFilenames(renderer);
756         refresh = false;
757 }
758
759
760 void DiskSelector::Render(SDL_Renderer * renderer)
761 {
762         if (!(window && showWindow))
763                 return;
764
765         HandleGUIState();
766
767         if (((diskSelectorState != DSS_LSB_SHOWN)
768                 && (diskSelectorState != DSS_RSB_SHOWN)
769                 && (diskSelectorState != DSS_SHOWN))
770                 || (textScrollCount != 0) || refresh)
771                 HandleSelection(renderer);
772
773         // Render scroll arrows (need to figure out why no alpha!)
774         SDL_SetRenderTarget(renderer, window);
775         SDL_Rect dst2 = { 0, ((DS_HEIGHT - 40) / 2), 40, 40 };
776         dst2.x = lsbPos;
777         SDL_RenderCopy(renderer, scrollLeftIcon, NULL, &dst2);
778         SDL_Rect dst3 = { 0, ((DS_HEIGHT - 40) / 2), 40, 40 };
779         dst3.x = rsbPos;
780         SDL_RenderCopy(renderer, scrollRightIcon, NULL, &dst3);
781         SDL_SetRenderTarget(renderer, NULL);
782
783         SDL_Rect dst = { DS_XPOS, DS_YPOS, DS_WIDTH, DS_HEIGHT };
784         SDL_RenderCopy(renderer, window, NULL, &dst);
785 }
786