]> Shamusworld >> Repos - apple2/blob - src/gui/diskselector.cpp
80595ee82e372fda31aaa2db345c05b1c2faddcf
[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 "floppydrive.h"
27 #include "font10pt.h"
28 #include "gui.h"
29 #include "log.h"
30 #include "settings.h"
31 #include "video.h"
32
33 // Icons, in GIMP "C" format
34 #include "gfx/scroll-left.c"
35 #include "gfx/scroll-right.c"
36
37
38 struct Bitmap {
39         unsigned int width;
40         unsigned int height;
41         unsigned int bytesPerPixel;                                     // 3:RGB, 4:RGBA
42         unsigned char pixelData[];
43 };
44
45 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 };
46
47 #define DS_WIDTH                        402
48 #define DS_HEIGHT                       322
49 #define SCROLL_HOT_WIDTH        48
50 #define DS_XPOS ((VIRTUAL_SCREEN_WIDTH - DS_WIDTH) / 2)
51 #define DS_YPOS ((VIRTUAL_SCREEN_HEIGHT - DS_HEIGHT) / 2)
52
53
54 bool entered = false;
55 int driveNumber;
56 int diskSelectorState = DSS_HIDDEN;
57 int diskSelected = -1;
58 int lastDiskSelected = -1;
59 int numColumns;
60 int colStart = 0;
61 int dxLeft = 0;
62 int dxRight = 0;
63 int rsbPos = DS_WIDTH;
64 int lsbPos = -40;
65 int textScrollCount = 0;
66 bool refresh = false;
67
68 /*
69 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.
70 */
71
72
73 // We make provision for sets of 32 or less...
74 /*
75 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.
76 */
77 struct DiskSet
78 {
79         uint8_t num;                    // # of disks in this set
80         std::string name;               // The name of this disk set
81 //      std::string fullPath;   // The path to the containing folder
82         std::string image[32];  // List of disk images in this set
83         std::string imgName[32];// List of human readable names of disk images
84         uint32_t crc[32];               // List of CRC32s of the disk images in the set
85         uint32_t crcFound[32];  // List of CRC32s actually discovered on filesystem
86
87         DiskSet(): num(0) {}
88 };
89
90
91 //
92 // Struct to hold filenames & full paths to same
93 //
94 struct FileStruct
95 {
96         std::string image;
97         std::string fullPath;
98         DiskSet diskSet;
99
100 //      FileStruct(): diskSet(NULL) {}
101 //      ~FileStruct() { if (diskSet != NULL) delete diskSet; }
102
103         // Functor, to presumably make the std::sort go faster
104         bool operator()(const FileStruct & a, const FileStruct & b) const
105         {
106                 return (strcasecmp(a.image.c_str(), b.image.c_str()) < 0 ? true : false);
107         }
108 };
109
110
111 static SDL_Texture * window = NULL;
112 static SDL_Texture * charStamp = NULL;
113 static uint32_t windowPixels[DS_WIDTH * DS_HEIGHT];
114 static uint32_t stamp[FONT_WIDTH * FONT_HEIGHT];
115 SDL_Texture * scrollLeftIcon = NULL;
116 SDL_Texture * scrollRightIcon = NULL;
117 bool DiskSelector::showWindow = false;
118 std::vector<FileStruct> fsList;
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         charStamp = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888,
126                 SDL_TEXTUREACCESS_TARGET, FONT_WIDTH, FONT_HEIGHT);
127
128         if (!window)
129         {
130                 WriteLog("GUI (DiskSelector): Could not create window!\n");
131                 return;
132         }
133
134         if (SDL_SetTextureBlendMode(window, SDL_BLENDMODE_BLEND) == -1)
135                 WriteLog("GUI (DiskSelector): Could not set blend mode for window.\n");
136
137         if (SDL_SetTextureBlendMode(charStamp, SDL_BLENDMODE_BLEND) == -1)
138                 WriteLog("GUI (DiskSelector): Could not set blend mode for charStamp.\n");
139
140         scrollLeftIcon  = GUI::CreateTexture(renderer, &scroll_left);
141         scrollRightIcon = GUI::CreateTexture(renderer, &scroll_right);
142
143         for(uint32_t i=0; i<DS_WIDTH*DS_HEIGHT; i++)
144                 windowPixels[i] = 0xEF007F00;
145
146         SDL_UpdateTexture(window, NULL, windowPixels, 128 * sizeof(Uint32));
147         FindDisks();
148         DrawFilenames(renderer);
149 }
150
151
152 //
153 // Find all disks images top level call
154 //
155 void DiskSelector::FindDisks(void)
156 {
157         fsList.clear();
158         FindDisks(settings.disksPath);
159         std::sort(fsList.begin(), fsList.end(), FileStruct());
160         // Calculate the number of columns in the file selector...
161         numColumns = (int)ceilf((float)fsList.size() / 27.0f);
162         WriteLog("GUI (DiskSelector)::FindDisks(): # of columns is %i (%i files)\n", numColumns, fsList.size());
163 }
164
165 /*
166 OK, so the way that you can determine if a file is a directory in a cross-platform way is to do an opendir() call on a discovered filename.  If it returns NULL, then it's a regular file and not a directory.  Though I think the 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 uint8_t * DiskSelector::ReadFile(const char * filename, uint32_t * size)
345 {
346         FILE * fp = fopen(filename, "r");
347
348         if (!fp)
349                 return NULL;
350
351         fseek(fp, 0, SEEK_END);
352         *size = ftell(fp);
353         fseek(fp, 0, SEEK_SET);
354
355         uint8_t * buffer = (uint8_t *)malloc(*size);
356         fread(buffer, 1, *size, fp);
357         fclose(fp);
358
359         return buffer;
360 }
361
362
363 bool DiskSelector::HasLegalExtension(const char * name)
364 {
365         // Find the file's extension, if any
366         const char * ext = strrchr(name, '.');
367
368         // No extension, so fuggetaboutit
369         if (ext == NULL)
370                 return false;
371
372         // Otherwise, look for a legal extension
373         // We should be smarter than this, and look at headers & file sizes instead
374         if ((strcasecmp(ext, ".dsk") == 0)
375                 || (strcasecmp(ext, ".do") == 0)
376                 || (strcasecmp(ext, ".po") == 0)
377                 || (strcasecmp(ext, ".woz") == 0))
378                 return true;
379
380         return false;
381 }
382
383
384 void DiskSelector::DrawFilenames(SDL_Renderer * renderer)
385 {
386         if (SDL_SetRenderTarget(renderer, window) < 0)
387         {
388                 WriteLog("GUI: Could not set Render Target to overlay... (%s)\n", SDL_GetError());
389                 return;
390         }
391
392         // 3 columns of 16 chars apiece (with 8X16 font), 18 rows
393         // 3 columns of 18 chars apiece (with 7X12 font), 24 rows
394         // 3 columns of 21 chars apiece (with 6X11 font), 27 rows
395
396         unsigned int count = 0;
397         unsigned int fsStart = colStart * 27;
398         int offset = 0;
399
400         // Draw partial columns (for scrolling left/right)
401         // [could probably combine these...]
402         if (textScrollCount < 0)
403         {
404                 int partialColStart = (colStart - 1) * 27;
405                 offset = -1 * textScrollCount;
406
407                 for(unsigned int y=0; y<27; y++)
408                 {
409                         for(unsigned int i=22+textScrollCount, x=0; i<21; i++, x++)
410                         {
411                                 if (i >= fsList[partialColStart + y].image.length())
412                                         break;
413
414                                 DrawCharacter(renderer, x + 1, y + 1, fsList[partialColStart + y].image[i], false);
415                         }
416                 }
417         }
418         else if (textScrollCount > 0)
419         {
420                 offset = 22 - textScrollCount;
421
422                 for(unsigned int y=0; y<27; y++)
423                 {
424                         for(unsigned int i=textScrollCount, x=0; i<21; i++, x++)
425                         {
426                                 if (i >= fsList[fsStart + y].image.length())
427                                         break;
428
429                                 DrawCharacter(renderer, x + 1, y + 1, fsList[fsStart + y].image[i], false);
430                         }
431                 }
432
433                 fsStart += 27;
434         }
435
436         while (fsStart < fsList.size())
437         {
438 //              int currentX = (count / 18) * 17;
439 //              int currentY = (count % 18);
440 //              int currentX = (count / 24) * 19;
441 //              int currentY = (count % 24);
442                 int currentX = (count / 27) * 22;
443                 int currentY = (count % 27);
444
445 //              for(unsigned int i=0; i<16; i++)
446 //              for(unsigned int i=0; i<18; i++)
447                 for(unsigned int i=0; i<21; i++)
448                 {
449                         if (i >= fsList[fsStart].image.length())
450                                 break;
451
452                         bool invert = (diskSelected == (int)fsStart ? true : false);
453                         DrawCharacter(renderer, currentX + i + 1 + offset, currentY + 1, fsList[fsStart].image[i], invert);
454                 }
455
456                 count++;
457                 fsStart++;
458
459 //              if (count >= (18 * 3))
460 //              if (count >= (24 * 3))
461                 if (count >= (27 * 3))
462                         break;
463         }
464
465         // If a disk is selected, show it on the top line in inverse video
466         if (diskSelected > -1)
467         {
468                 for(unsigned int i=0; i<65; i++)
469                 {
470                         if (i >= fsList[diskSelected].image.length())
471                                 break;
472
473                         DrawCharacter(renderer, i + 1, 0, fsList[diskSelected].image[i], true);
474                 }
475         }
476
477         // Set render target back to default
478         SDL_SetRenderTarget(renderer, NULL);
479 }
480
481
482 void DiskSelector::DrawCharacter(SDL_Renderer * renderer, int x, int y, uint8_t c, bool invert/*=false*/)
483 {
484         uint32_t inv = (invert ? 0x000000FF : 0x00000000);
485         uint32_t pixel = 0xFFFFC000;    // RRGGBBAA
486         uint8_t * ptr = (uint8_t *)&font10pt[(c - 0x20) * FONT_WIDTH * FONT_HEIGHT];
487         SDL_Rect dst;
488         dst.x = x * FONT_WIDTH, dst.y = y * FONT_HEIGHT, dst.w = FONT_WIDTH, dst.h = FONT_HEIGHT;
489
490         for(int i=0; i<FONT_WIDTH*FONT_HEIGHT; i++)
491                 stamp[i] = (pixel | ptr[i]) ^ inv;
492
493         SDL_UpdateTexture(charStamp, NULL, stamp, FONT_WIDTH * sizeof(Uint32));
494         SDL_RenderCopy(renderer, charStamp, NULL, &dst);
495 }
496
497
498 void DiskSelector::ShowWindow(int drive)
499 {
500         diskSelectorState = DSS_SHOWN;
501         entered = false;
502         showWindow = true;
503         driveNumber = drive;
504 }
505
506
507 void DiskSelector::MouseDown(int32_t x, int32_t y, uint32_t buttons)
508 {
509         if (!showWindow || !entered)
510                 return;
511
512         if ((diskSelectorState == DSS_LSB_SHOWING) || (diskSelectorState == DSS_LSB_SHOWN))
513         {
514                 colStart--;
515                 textScrollCount = 21;
516
517                 if (colStart == 0)
518                 {
519                         diskSelectorState = DSS_LSB_HIDING;
520                         dxLeft = -8;
521                 }
522
523                 return;
524         }
525
526         if ((diskSelectorState == DSS_RSB_SHOWING) || (diskSelectorState == DSS_RSB_SHOWN))
527         {
528                 colStart++;
529                 textScrollCount = -21;
530
531                 if ((colStart + 3) == numColumns)
532                 {
533                         diskSelectorState = DSS_RSB_HIDING;
534                         dxRight = 8;
535                 }
536
537                 return;
538         }
539
540         if (diskSelected != -1)
541         {
542                 floppyDrive[0].LoadImage(fsList[diskSelected].fullPath.c_str(), driveNumber);
543         }
544
545         showWindow = false;
546 }
547
548
549 void DiskSelector::MouseUp(int32_t x, int32_t y, uint32_t buttons)
550 {
551         if (!showWindow)
552                 return;
553
554 }
555
556
557 void DiskSelector::MouseMove(int32_t x, int32_t y, uint32_t buttons)
558 {
559         if (!showWindow)
560                 return;
561
562         // Check to see if DS has been hovered yet, and, if so, set a flag to show
563         // that it has
564         if (!entered && ((x >= DS_XPOS) && (x <= (DS_XPOS + DS_WIDTH))
565                 && (y >= DS_YPOS) && (y <= (DS_YPOS + DS_HEIGHT))))
566                 entered = true;
567
568         // Check to see if the DS, since being hovered, is now no longer being
569         // hovered
570 //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...
571         if (entered && ((x < DS_XPOS) || (x > (DS_XPOS + DS_WIDTH))
572                 || (y < DS_YPOS) || (y > (DS_YPOS + DS_HEIGHT))))
573         {
574                 diskSelectorState = DSS_HIDDEN;
575                 dxLeft = 0;
576                 dxRight = 0;
577                 rsbPos = DS_WIDTH;
578                 lsbPos = -40;
579                 showWindow = false;
580                 refresh = true;
581                 return;
582         }
583
584         // Bail out if the DS hasn't been entered yet
585         if (!entered)
586                 return;
587
588 /*
589 states:
590 +-----+---------------------+-----+
591 |     |                     |     |
592 |     |                     |     |
593 +-----+---------------------+-----+
594  ^           ^                ^
595  |           |                x is here and state is DSS_SHOWN
596  |           x is here and state is DSS_LSB_SHOWING or DSS_RSB_SHOWING
597  x is here and state is DSS_SHOWN
598
599 */
600         if (x < (DS_XPOS + SCROLL_HOT_WIDTH))
601         {
602                 if ((colStart > 0) && (diskSelectorState == DSS_SHOWN))
603                 {
604                         diskSelectorState = DSS_LSB_SHOWING;
605                         dxLeft = 8;
606                 }
607         }
608         else if (x > (DS_XPOS + DS_WIDTH - SCROLL_HOT_WIDTH))
609         {
610                 if (((colStart + 3) < numColumns) && (diskSelectorState == DSS_SHOWN))
611                 {
612                         diskSelectorState = DSS_RSB_SHOWING;
613                         dxRight = -8;
614                 }
615         }
616         else
617         {
618                 // Handle the excluded middle  :-P
619                 if ((diskSelectorState == DSS_LSB_SHOWING)
620                         || (diskSelectorState == DSS_LSB_SHOWN))
621                 {
622                         diskSelectorState = DSS_LSB_HIDING;
623                         dxLeft = -8;
624                 }
625                 else if ((diskSelectorState == DSS_RSB_SHOWING)
626                         || (diskSelectorState == DSS_RSB_SHOWN))
627                 {
628                         diskSelectorState = DSS_RSB_HIDING;
629                         dxRight = 8;
630                 }
631         }
632
633         // The -1 terms move the origin to the upper left corner (from 1 in, and 1
634         // down)
635         int xChar = ((x - DS_XPOS) / FONT_WIDTH) - 1;
636         int yChar = ((y - DS_YPOS) / FONT_HEIGHT) - 1;
637         diskSelected = ((xChar / 22) * 27) + yChar + (colStart * 27);
638
639         if ((yChar < 0) || (yChar >= 27)
640                 || (diskSelected >= (int)fsList.size())
641                 || (diskSelectorState == DSS_LSB_SHOWING)
642                 || (diskSelectorState == DSS_LSB_SHOWN)
643                 || (diskSelectorState == DSS_RSB_SHOWING)
644                 || (diskSelectorState == DSS_RSB_SHOWN))
645                 diskSelected = -1;
646
647         if (diskSelected != lastDiskSelected)
648         {
649                 HandleSelection(sdlRenderer);
650                 lastDiskSelected = diskSelected;
651         }
652 }
653
654
655 void DiskSelector::HandleGUIState(void)
656 {
657         lsbPos += dxLeft;
658         rsbPos += dxRight;
659
660         if ((lsbPos > (SCROLL_HOT_WIDTH - 40)) && (diskSelectorState == DSS_LSB_SHOWING))
661         {
662                 diskSelectorState = DSS_LSB_SHOWN;
663                 lsbPos = SCROLL_HOT_WIDTH - 40;
664                 dxLeft = 0;
665         }
666         else if ((lsbPos < -40) && (diskSelectorState == DSS_LSB_HIDING))
667         {
668                 diskSelectorState = DSS_SHOWN;
669                 lsbPos = -40;
670                 dxLeft = 0;
671         }
672         else if ((rsbPos < (DS_WIDTH - SCROLL_HOT_WIDTH)) && (diskSelectorState == DSS_RSB_SHOWING))
673         {
674                 diskSelectorState = DSS_RSB_SHOWN;
675                 rsbPos = DS_WIDTH - SCROLL_HOT_WIDTH;
676                 dxRight = 0;
677         }
678         else if ((rsbPos > DS_WIDTH) && (diskSelectorState == DSS_RSB_HIDING))
679         {
680                 diskSelectorState = DSS_SHOWN;
681                 rsbPos = DS_WIDTH;
682                 dxRight = 0;
683         }
684
685         if (textScrollCount < 0)
686         {
687                 textScrollCount += 2;
688
689                 if (textScrollCount > 0)
690                 {
691                         textScrollCount = 0;
692                         refresh = true;
693                 }
694         }
695         else if (textScrollCount > 0)
696         {
697                 textScrollCount -= 2;
698
699                 if (textScrollCount < 0)
700                 {
701                         textScrollCount = 0;
702                         refresh = true;
703                 }
704         }
705 }
706
707
708 void DiskSelector::HandleSelection(SDL_Renderer * renderer)
709 {
710         SDL_UpdateTexture(window, NULL, windowPixels, 128 * sizeof(Uint32));
711         DrawFilenames(renderer);
712         refresh = false;
713 }
714
715
716 void DiskSelector::Render(SDL_Renderer * renderer)
717 {
718         if (!(window && showWindow))
719                 return;
720
721         HandleGUIState();
722
723         if (((diskSelectorState != DSS_LSB_SHOWN)
724                 && (diskSelectorState != DSS_RSB_SHOWN)
725                 && (diskSelectorState != DSS_SHOWN))
726                 || (textScrollCount != 0) || refresh)
727                 HandleSelection(renderer);
728
729         // Render scroll arrows (need to figure out why no alpha!)
730         SDL_SetRenderTarget(renderer, window);
731         SDL_Rect dst2 = { 0, ((DS_HEIGHT - 40) / 2), 40, 40 };
732         dst2.x = lsbPos;
733         SDL_RenderCopy(renderer, scrollLeftIcon, NULL, &dst2);
734         SDL_Rect dst3 = { 0, ((DS_HEIGHT - 40) / 2), 40, 40 };
735         dst3.x = rsbPos;
736         SDL_RenderCopy(renderer, scrollRightIcon, NULL, &dst3);
737         SDL_SetRenderTarget(renderer, NULL);
738
739         SDL_Rect dst = { DS_XPOS, DS_YPOS, DS_WIDTH, DS_HEIGHT };
740         SDL_RenderCopy(renderer, window, NULL, &dst);
741 }
742