//
// Graphical User Interface support
// by James Hammons
-// © 2014 Underground Software
+// © 2014-2019 Underground Software
//
// JLH = James Hammons <jlhamm@acm.org>
//
//
#include "gui.h"
+#include <vector>
#include "apple2.h"
-#include "applevideo.h"
+#include "config.h"
#include "diskselector.h"
+#include "elements.h"
+#include "floppydrive.h"
+#include "font10pt.h"
#include "log.h"
#include "video.h"
"@@@@@@@@"
"@@@@@@@@";
+const char newDiskIcon[(30 * 6) + 1] =
+ "@@ @@ @@@@@ @@ @@ @@ "
+ "@@@ @@ @@ @@ @@ @@@@ "
+ "@@@@ @@ @@@@ @@ @ @@ @@@@@@"
+ "@@ @@@@ @@ @@@@@@@ @@ "
+ "@@ @@@ @@ @@@@@@@ @@@@@ "
+ "@@ @@ @@@@@ @@ @@ @@@@ ";
+
const char driveLight[(5 * 5) + 1] =
" @@@ "
"@@@@@"
" @@@ ";
-enum { SBS_SHOWING, SBS_HIDING, SBS_SHOWN, SBS_HIDDEN };
-
-
SDL_Texture * GUI::overlay = NULL;
SDL_Rect GUI::olDst;
int GUI::sidebarState = SBS_HIDDEN;
"Configure Apple2" };
bool disk1EjectHovered = false;
bool disk2EjectHovered = false;
+bool disk1NewDiskHovered = false;
+bool disk2NewDiskHovered = false;
+SDL_Texture * GUI::charStamp = NULL;
+uint32_t GUI::stamp[FONT_WIDTH * FONT_HEIGHT];
#define SIDEBAR_X_POS (VIRTUAL_SCREEN_WIDTH - 80)
+//std::vector<void *> objList;
+
GUI::GUI(void)
{
{
overlay = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888,
SDL_TEXTUREACCESS_TARGET, 128, 380);
+ charStamp = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888,
+ SDL_TEXTUREACCESS_TARGET, FONT_WIDTH, FONT_HEIGHT);
if (!overlay)
{
if (SDL_SetTextureBlendMode(overlay, SDL_BLENDMODE_BLEND) == -1)
WriteLog("GUI: Could not set blend mode for overlay.\n");
+ if (SDL_SetTextureBlendMode(charStamp, SDL_BLENDMODE_BLEND) == -1)
+ WriteLog("GUI: Could not set blend mode for charStamp.\n");
+
for(uint32_t i=0; i<128*380; i++)
texturePointer[i] = 0xB0A000A0;
}
DiskSelector::Init(renderer);
-// DiskSelector::showWindow = true;
-
+ Config::Init(renderer);
WriteLog("GUI: Successfully initialized.\n");
}
void GUI::MouseDown(int32_t x, int32_t y, uint32_t buttons)
{
DiskSelector::MouseDown(x, y, buttons);
+ Config::MouseDown(x, y, buttons);
if (sidebarState != SBS_SHOWN)
return;
case 1:
SpawnMessage("*** DISK #1 ***");
- if (disk1EjectHovered && !floppyDrive.IsEmpty(0))
+#if 0
+ if (disk1EjectHovered && !floppyDrive[0].IsEmpty(0))
{
- floppyDrive.EjectImage(0);
+ floppyDrive[0].EjectImage(0);
SpawnMessage("*** DISK #1 EJECTED ***");
}
- if (!disk1EjectHovered)
+ if (!disk1EjectHovered && !disk1NewDiskHovered)
{
// Load the disk selector
+ Config::HideWindow();
DiskSelector::ShowWindow(0);
}
+#else
+ if (disk1EjectHovered)
+ {
+ if (!floppyDrive[0].IsEmpty(0))
+ {
+ floppyDrive[0].EjectImage(0);
+ SpawnMessage("*** DISK #1 EJECTED ***");
+ }
+ }
+ else
+ {
+ if (disk1NewDiskHovered)
+ {
+ if (!floppyDrive[0].IsEmpty(0))
+ floppyDrive[0].EjectImage(0);
+
+ floppyDrive[0].CreateBlankImage(0);
+ }
+ else
+ {
+ // Load the disk selector
+ Config::HideWindow();
+ DiskSelector::ShowWindow(0);
+ }
+ }
+#endif
break;
// Disk #2
case 2:
SpawnMessage("*** DISK #2 ***");
- if (disk2EjectHovered && !floppyDrive.IsEmpty(1))
+ if (disk2EjectHovered && !floppyDrive[0].IsEmpty(1))
{
- floppyDrive.EjectImage(1);
+ floppyDrive[0].EjectImage(1);
SpawnMessage("*** DISK #2 EJECTED ***");
}
- if (!disk2EjectHovered)
+ if (!disk2EjectHovered && !disk2NewDiskHovered)
{
// Load the disk selector
+ Config::HideWindow();
DiskSelector::ShowWindow(1);
}
break;
// Swap disks
case 3:
- floppyDrive.SwapImages();
+ floppyDrive[0].SwapImages();
SpawnMessage("*** DISKS SWAPPED ***");
break;
// Save state
// Configuration
case 6:
SpawnMessage("*** CONFIGURATION ***");
+ // Load the configuration window
+ DiskSelector::HideWindow();
+ Config::ShowWindow();
break;
}
}
void GUI::MouseUp(int32_t x, int32_t y, uint32_t buttons)
{
DiskSelector::MouseUp(x, y, buttons);
+ Config::MouseUp(x, y, buttons);
}
void GUI::MouseMove(int32_t x, int32_t y, uint32_t buttons)
{
DiskSelector::MouseMove(x, y, buttons);
+ Config::MouseMove(x, y, buttons);
if (sidebarState != SBS_SHOWN)
{
&& (y >= (117 + 31 + 2))
&& (y < (117 + 31 + 2 + 7)) ? true : false);
+ disk1NewDiskHovered = ((x >= (SIDEBAR_X_POS + 24 + 6))
+ && (x < (SIDEBAR_X_POS + 24 + 6 + 30))
+ && (y >= (63 + 31 + 2))
+ && (y < (63 + 31 + 2 + 6)) ? true : false);
+
+ disk2NewDiskHovered = ((x >= (SIDEBAR_X_POS + 24 + 6))
+ && (x < (SIDEBAR_X_POS + 24 + 6 + 30))
+ && (y >= (117 + 31 + 2))
+ && (y < (117 + 31 + 2 + 6)) ? true : false);
+
if (iconSelected != lastIconSelected)
{
HandleIconSelection(sdlRenderer);
// Show what's in the selected drive
if (iconSelected >= 1 && iconSelected <= 2)
{
- if (!floppyDrive.IsEmpty(iconSelected - 1))
- SpawnMessage("\"%s\"", floppyDrive.ImageName(iconSelected - 1));
+ if (!floppyDrive[0].IsEmpty(iconSelected - 1))
+ SpawnMessage("\"%s\"", floppyDrive[0].ImageName(iconSelected - 1));
}
}
}
}
+bool GUI::KeyDown(uint32_t key)
+{
+ return Config::KeyDown(key);
+}
+
+
void GUI::HandleIconSelection(SDL_Renderer * renderer)
{
// Set up drive icons in their current states
// Drive door @ (16, 7)
SDL_Rect dst;
dst.w = 8, dst.h = 10, dst.x = 16, dst.y = 7;
- SDL_RenderCopy(renderer, (floppyDrive.IsEmpty(driveNumber) ?
+ SDL_RenderCopy(renderer, (floppyDrive[0].IsEmpty(driveNumber) ?
doorOpen : doorClosed), NULL, &dst);
// Numeral @ (30, 20)
DrawCharArray(renderer, number[driveNumber], 30, 20, 7, 7, 0xD0, 0xE0, 0xF0);
DrawDriveLight(renderer, driveNumber);
DrawEjectButton(renderer, driveNumber);
+ DrawNewDiskButton(renderer, driveNumber);
// Set render target back to default
SDL_SetRenderTarget(renderer, NULL);
void GUI::DrawEjectButton(SDL_Renderer * renderer, int driveNumber)
{
- if (floppyDrive.IsEmpty(driveNumber))
+ if (floppyDrive[0].IsEmpty(driveNumber))
return;
uint8_t r = 0x00, g = 0xAA, b = 0x00;
}
+void GUI::DrawNewDiskButton(SDL_Renderer * renderer, int driveNumber)
+{
+ if (!floppyDrive[0].IsEmpty(driveNumber))
+ return;
+
+ uint8_t r = 0x00, g = 0xAA, b = 0x00;
+
+ if ((driveNumber == 0 && disk1NewDiskHovered)
+ || (driveNumber == 1 && disk2NewDiskHovered))
+ r = 0x20, g = 0xFF, b = 0x20;
+
+ DrawCharArray(renderer, newDiskIcon, 6, 31, 30, 6, r, g, b);
+}
+
+
void GUI::DrawDriveLight(SDL_Renderer * renderer, int driveNumber)
{
- int lightState = floppyDrive.DriveLightStatus(driveNumber);
+ int lightState = floppyDrive[0].DriveLightStatus(driveNumber);
int r = 0x77, g = 0x00, b = 0x00;
if (lightState == DLS_READ)
}
+void GUI::DrawCharacter(SDL_Renderer * renderer, int x, int y, uint8_t c, bool invert/*= false*/)
+{
+ uint32_t inv = (invert ? 0x000000FF : 0x00000000);
+ uint32_t pixel = 0xFFFFC000; // RRGGBBAA
+ uint8_t * ptr = (uint8_t *)&font10pt[(c - 0x20) * FONT_WIDTH * FONT_HEIGHT];
+ SDL_Rect dst;
+ dst.x = x * FONT_WIDTH, dst.y = y * FONT_HEIGHT, dst.w = FONT_WIDTH, dst.h = FONT_HEIGHT;
+
+ for(int i=0; i<FONT_WIDTH*FONT_HEIGHT; i++)
+ stamp[i] = (pixel | ptr[i]) ^ inv;
+
+ SDL_UpdateTexture(charStamp, NULL, stamp, FONT_WIDTH * sizeof(Uint32));
+ SDL_RenderCopy(renderer, charStamp, NULL, &dst);
+}
+
+
+//
+// N.B.: This draws a char at an abosulte X/Y position, not on a grid
+//
+void GUI::DrawCharacterVert(SDL_Renderer * renderer, int x, int y, uint8_t c, bool invert/*= false*/)
+{
+ uint32_t inv = (invert ? 0x000000FF : 0x00000000);
+ uint32_t pixel = 0xFFFFC000; // RRGGBBAA
+ uint8_t * ptr = (uint8_t *)&font10pt[(c - 0x20) * FONT_WIDTH * FONT_HEIGHT];
+ SDL_Rect dst;
+ dst.x = x, dst.y = y, dst.w = FONT_WIDTH, dst.h = FONT_HEIGHT;
+ SDL_Point pt = { FONT_WIDTH - 1, FONT_HEIGHT - 1 };
+
+ for(int i=0; i<FONT_WIDTH*FONT_HEIGHT; i++)
+ stamp[i] = (pixel | ptr[i]) ^ inv;
+
+ SDL_SetTextureBlendMode(charStamp, SDL_BLENDMODE_NONE);
+ SDL_UpdateTexture(charStamp, NULL, stamp, FONT_WIDTH * sizeof(Uint32));
+ SDL_RenderCopyEx(renderer, charStamp, NULL, &dst, 270.0, &pt, SDL_FLIP_NONE);
+ SDL_SetTextureBlendMode(charStamp, SDL_BLENDMODE_BLEND);
+}
+
+
+void GUI::DrawString(SDL_Renderer * renderer, int x, int y, const char * s, bool invert/*= false*/)
+{
+ int len = strlen(s);
+
+ for(int i=0; i<len; i++)
+ DrawCharacter(renderer, x + i, y, s[i], invert);
+}
+
+
+//
+// N.B.: This draws a char at an absolute X/Y position, not on a grid
+//
+void GUI::DrawStringVert(SDL_Renderer * renderer, int x, int y, const char * s, bool invert/*= false*/)
+{
+ int len = strlen(s);
+
+ for(int i=0; i<len; i++)
+ DrawCharacterVert(renderer, x, y - (FONT_WIDTH * i), s[i], invert);
+}
+
+
+void GUI::DrawBox(SDL_Renderer * renderer, int x, int y, int w, int h, int r/*= 0x00*/, int g/*= 0xAA*/, int b/*= 0x00*/)
+{
+ SDL_SetRenderDrawColor(renderer, r, g, b, 0xFF);
+
+ for(int i=0; i<w; i++)
+ {
+ SDL_RenderDrawPoint(renderer, x + i, y);
+ SDL_RenderDrawPoint(renderer, x + i, y + h - 1);
+ }
+
+ for(int i=0; i<h; i++)
+ {
+ SDL_RenderDrawPoint(renderer, x, y + i);
+ SDL_RenderDrawPoint(renderer, x + w - 1, y + i);
+ }
+
+ SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00);
+}
+
+
void GUI::HandleGUIState(void)
{
olDst.x += dx;
stateSaveIcon, stateLoadIcon, configIcon };
icons[0] = (powerOnState ? powerOnIcon : powerOffIcon);
-
- SDL_Rect dst;
- dst.w = dst.h = 40, dst.x = 24, dst.y = 2 + 7;
+ SDL_Rect dst = { 24, 2 + 7, 40, 40 };
for(int i=0; i<7; i++)
{
void GUI::Render(SDL_Renderer * renderer)
{
+ // Sanity check
if (!overlay)
return;
// Hmm.
DiskSelector::Render(renderer);
+ Config::Render(renderer);
}