initial commit
This commit is contained in:
@@ -0,0 +1,251 @@
|
||||
#include "license_key_page.h"
|
||||
#include "app_state.h"
|
||||
#include "db/db_keys.h"
|
||||
#include "network/license_service.h"
|
||||
#include "provisioning.h"
|
||||
#include "validation.h"
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
void LicenseKeyPage::onPush(AppState& state)
|
||||
{
|
||||
state_ = &state;
|
||||
TTF_Init();
|
||||
createFonts();
|
||||
|
||||
storedValue_ = state.db.getKV(radiantwave::db::KV_LICENSE_KEY);
|
||||
raw_ = radiantwave::validation::sanitizeLicenseKey(storedValue_);
|
||||
statusMessage_ = "Enter the 16-character key (XXXX-XXXX-XXXX-XXXX), then press ENTER";
|
||||
|
||||
if (state.window) {
|
||||
SDL_StartTextInput(state.window);
|
||||
}
|
||||
}
|
||||
|
||||
void LicenseKeyPage::onResume(AppState& state)
|
||||
{
|
||||
state_ = &state;
|
||||
createFonts();
|
||||
storedValue_ = state.db.getKV(radiantwave::db::KV_LICENSE_KEY);
|
||||
raw_ = radiantwave::validation::sanitizeLicenseKey(storedValue_);
|
||||
|
||||
if (state.window) {
|
||||
SDL_StartTextInput(state.window);
|
||||
}
|
||||
}
|
||||
|
||||
void LicenseKeyPage::onPause(AppState& state)
|
||||
{
|
||||
state.keybinds.unbindAll();
|
||||
if (state.window) {
|
||||
SDL_StopTextInput(state.window);
|
||||
}
|
||||
destroyFonts();
|
||||
destroyTextures();
|
||||
}
|
||||
|
||||
void LicenseKeyPage::onPop(AppState& state)
|
||||
{
|
||||
if (state.window) {
|
||||
SDL_StopTextInput(state.window);
|
||||
}
|
||||
destroyFonts();
|
||||
destroyTextures();
|
||||
TTF_Quit();
|
||||
}
|
||||
|
||||
void LicenseKeyPage::createFonts()
|
||||
{
|
||||
destroyFonts();
|
||||
fontTitle_ = TTF_OpenFont("assets/fonts/Roboto-Regular.ttf", 52);
|
||||
fontHeader_ = TTF_OpenFont("assets/fonts/Roboto-Regular.ttf", 28);
|
||||
fontBody_ = TTF_OpenFont("assets/fonts/Roboto-Regular.ttf", 32);
|
||||
fontSmall_ = TTF_OpenFont("assets/fonts/Roboto-Regular.ttf", 22);
|
||||
}
|
||||
|
||||
void LicenseKeyPage::destroyFonts()
|
||||
{
|
||||
if (fontTitle_) { TTF_CloseFont(fontTitle_); fontTitle_ = nullptr; }
|
||||
if (fontHeader_) { TTF_CloseFont(fontHeader_); fontHeader_ = nullptr; }
|
||||
if (fontBody_) { TTF_CloseFont(fontBody_); fontBody_ = nullptr; }
|
||||
if (fontSmall_) { TTF_CloseFont(fontSmall_); fontSmall_ = nullptr; }
|
||||
}
|
||||
|
||||
void LicenseKeyPage::destroyTextures()
|
||||
{
|
||||
titleText_.destroy();
|
||||
storedText_.destroy();
|
||||
valueText_.destroy();
|
||||
statusText_.destroy();
|
||||
hintText_.destroy();
|
||||
}
|
||||
|
||||
void LicenseKeyPage::submit()
|
||||
{
|
||||
if (raw_.size() != 16) {
|
||||
statusMessage_ = "License key must be 16 characters (XXXX-XXXX-XXXX-XXXX)";
|
||||
return;
|
||||
}
|
||||
|
||||
std::string formatted = radiantwave::validation::formatLicenseKey(raw_);
|
||||
if (!state_->db.setKV(radiantwave::db::KV_LICENSE_KEY, formatted)) {
|
||||
statusMessage_ = "Failed to save license key";
|
||||
return;
|
||||
}
|
||||
storedValue_ = formatted;
|
||||
|
||||
// Both the email address and the license key are required to validate.
|
||||
// When the email is missing the key is saved but the check is skipped.
|
||||
std::string levelName;
|
||||
std::string errorMessage;
|
||||
const radiantwave::license::Result result = radiantwave::license::validateStoredLicense(
|
||||
state_->db, "", &levelName, &errorMessage);
|
||||
|
||||
switch (result) {
|
||||
case radiantwave::license::Result::Ok:
|
||||
statusMessage_ = "License validated (" + levelName + ")";
|
||||
break;
|
||||
case radiantwave::license::Result::NotConfigured:
|
||||
statusMessage_ = "Saved - add your email address to validate the license";
|
||||
break;
|
||||
case radiantwave::license::Result::Invalid:
|
||||
statusMessage_ = "License check failed: " +
|
||||
(errorMessage.empty() ? "invalid license" : errorMessage);
|
||||
break;
|
||||
case radiantwave::license::Result::NetworkError:
|
||||
statusMessage_ = "License saved, but the server could not be reached to validate";
|
||||
break;
|
||||
}
|
||||
returnTimer_ = 1.0f;
|
||||
}
|
||||
|
||||
void LicenseKeyPage::handleEvent(const SDL_Event& event)
|
||||
{
|
||||
if (!state_) return;
|
||||
|
||||
if (event.type == SDL_EVENT_TEXT_INPUT) {
|
||||
std::string addition = radiantwave::validation::sanitizeLicenseKey(event.text.text);
|
||||
if (raw_.size() + addition.size() > 16) {
|
||||
addition.resize(16 - raw_.size());
|
||||
}
|
||||
raw_ += addition;
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.type == SDL_EVENT_KEY_DOWN) {
|
||||
SDL_Keycode key = event.key.key;
|
||||
|
||||
if (key == SDLK_BACKSPACE) {
|
||||
if (!raw_.empty()) {
|
||||
raw_.pop_back();
|
||||
}
|
||||
} else if (key == SDLK_RETURN || key == SDLK_KP_ENTER) {
|
||||
submit();
|
||||
} else if (key == SDLK_ESCAPE || key == SDLK_TAB) {
|
||||
exitPage(*state_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LicenseKeyPage::update(float dt)
|
||||
{
|
||||
if (returnTimer_ > 0.0f) {
|
||||
returnTimer_ -= dt;
|
||||
if (returnTimer_ <= 0.0f) {
|
||||
returnTimer_ = -1.0f;
|
||||
if (state_) {
|
||||
exitPage(*state_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// In the First-Run walkthrough a finished step moves on to the next missing
|
||||
// prerequisite; otherwise the page returns to whatever pushed it (Settings).
|
||||
void LicenseKeyPage::exitPage(AppState& state)
|
||||
{
|
||||
if (state.provisioningMode) {
|
||||
radiantwave::provisioning::advance(state);
|
||||
} else {
|
||||
state.pages.pop(state);
|
||||
}
|
||||
}
|
||||
|
||||
void LicenseKeyPage::render(SDL_Renderer* renderer)
|
||||
{
|
||||
int winW, winH;
|
||||
SDL_GetWindowSize(SDL_GetRenderWindow(renderer), &winW, &winH);
|
||||
float fw = static_cast<float>(winW);
|
||||
float fh = static_cast<float>(winH);
|
||||
|
||||
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetRenderDrawColor(renderer, 20, 20, 30, 255);
|
||||
SDL_FRect bgRect{0, 0, fw, fh};
|
||||
SDL_RenderFillRect(renderer, &bgRect);
|
||||
|
||||
if (!fontTitle_ || !fontHeader_ || !fontBody_ || !fontSmall_) return;
|
||||
|
||||
SDL_Color white = {255, 255, 255, 255};
|
||||
SDL_Color yellow = {255, 255, 0, 255};
|
||||
SDL_Color red = {255, 80, 80, 255};
|
||||
SDL_Color cyan = {100, 220, 255, 255};
|
||||
SDL_Color gray = {180, 180, 180, 255};
|
||||
|
||||
float currentY = 30.0f;
|
||||
|
||||
// Input box (centered on screen); messages align to its left edge
|
||||
float boxW = 700.0f;
|
||||
float boxH = 70.0f;
|
||||
float boxX = (fw - boxW) / 2.0f;
|
||||
|
||||
titleText_.ensure(renderer, fontTitle_, "License Key", white);
|
||||
if (titleText_.texture) {
|
||||
float x = (fw - titleText_.width) / 2.0f;
|
||||
SDL_FRect dst{x, currentY, titleText_.width, titleText_.height};
|
||||
SDL_RenderTexture(renderer, titleText_.texture, nullptr, &dst);
|
||||
currentY += titleText_.height + 30.0f;
|
||||
}
|
||||
|
||||
std::string storedStr = storedValue_.empty() ? "Not set" : storedValue_;
|
||||
storedText_.ensure(renderer, fontHeader_, "Saved: " + storedStr,
|
||||
storedValue_.empty() ? gray : yellow);
|
||||
if (storedText_.texture) {
|
||||
SDL_FRect dst{boxX, currentY, storedText_.width, storedText_.height};
|
||||
SDL_RenderTexture(renderer, storedText_.texture, nullptr, &dst);
|
||||
}
|
||||
currentY += 50.0f;
|
||||
|
||||
SDL_SetRenderDrawColor(renderer, 35, 40, 60, 255);
|
||||
SDL_FRect boxRect{boxX, currentY, boxW, boxH};
|
||||
SDL_RenderFillRect(renderer, &boxRect);
|
||||
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
|
||||
SDL_RenderRect(renderer, &boxRect);
|
||||
|
||||
std::string display = radiantwave::validation::formatLicenseKey(raw_);
|
||||
if (!raw_.empty() && raw_.size() < 16) {
|
||||
display += "_";
|
||||
}
|
||||
valueText_.ensure(renderer, fontBody_, display, cyan);
|
||||
if (valueText_.texture) {
|
||||
SDL_FRect dst{boxX + 20.0f, currentY + (boxH - valueText_.height) / 2.0f, valueText_.width, valueText_.height};
|
||||
SDL_RenderTexture(renderer, valueText_.texture, nullptr, &dst);
|
||||
}
|
||||
currentY += boxH + 30.0f;
|
||||
|
||||
// Status message (left-aligned with input box)
|
||||
if (!statusMessage_.empty()) {
|
||||
statusText_.ensure(renderer, fontHeader_, statusMessage_, gray);
|
||||
if (statusText_.texture) {
|
||||
SDL_FRect dst{boxX, currentY, statusText_.width, statusText_.height};
|
||||
SDL_RenderTexture(renderer, statusText_.texture, nullptr, &dst);
|
||||
}
|
||||
currentY += 50.0f;
|
||||
}
|
||||
|
||||
hintText_.ensure(renderer, fontSmall_,
|
||||
"[Type] Enter key (dashes added automatically) [ENTER] Save [BACKSPACE] Delete [ESC/TAB] Back", gray);
|
||||
if (hintText_.texture) {
|
||||
float x = (fw - hintText_.width) / 2.0f;
|
||||
SDL_FRect dst{x, fh - 40.0f, hintText_.width, hintText_.height};
|
||||
SDL_RenderTexture(renderer, hintText_.texture, nullptr, &dst);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user