UI code reformatting, add PN532 NFC driver
Some checks failed
Build / Run build (push) Failing after 5m49s

This commit is contained in:
spicyjpeg 2025-01-03 12:31:13 +01:00
parent 7faa1612e0
commit 4667feb240
No known key found for this signature in database
GPG Key ID: 5CC87404C01DF393
9 changed files with 957 additions and 100 deletions

View File

@ -70,6 +70,7 @@ set(
set( set(
exthwSources exthwSources
src/common/exthw/lcd.cpp src/common/exthw/lcd.cpp
src/common/exthw/nfc.cpp
) )
set( set(
fsSources fsSources

View File

@ -1,5 +1,5 @@
/* /*
* 573in1 - Copyright (C) 2022-2024 spicyjpeg * 573in1 - Copyright (C) 2022-2025 spicyjpeg
* *
* 573in1 is free software: you can redistribute it and/or modify it under the * 573in1 is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software * terms of the GNU General Public License as published by the Free Software
@ -40,18 +40,25 @@
#define CH_RIGHT_ARROW_ALT "\u2192" #define CH_RIGHT_ARROW_ALT "\u2192"
#define CH_INVALID_CHAR "\ufffd" #define CH_INVALID_CHAR "\ufffd"
#define CH_CIRCLE_BUTTON "\u25cb" #define CH_PS1_CIRCLE_BUTTON "\u25cb"
#define CH_X_BUTTON "\u2715" #define CH_PS1_X_BUTTON "\u2715"
#define CH_TRIANGLE_BUTTON "\u25b3" #define CH_PS1_TRIANGLE_BUTTON "\u25b3"
#define CH_SQUARE_BUTTON "\u25a1" #define CH_PS1_SQUARE_BUTTON "\u25a1"
#define CH_LEFT_BUTTON "\u25c1" #define CH_PS1_SELECT_BUTTON "\u25ac"
#define CH_RIGHT_BUTTON "\u25b7" #define CH_PS1_START_BUTTON "\u25ba"
#define CH_START_BUTTON "\u25ad"
#define CH_CLOSED_LOCK "\U0001f512" #define CH_LEFT_BUTTON "\u25c1"
#define CH_OPEN_LOCK "\U0001f513" #define CH_RIGHT_BUTTON "\u25b7"
#define CH_CDROM_ICON "\U0001f4bf" #define CH_START_BUTTON "\u25ad"
#define CH_HDD_ICON "\U0001f4bd"
#define CH_HOST_ICON "\U0001f50c" #define CH_CLOSED_LOCK "\U0001f512"
#define CH_DIR_ICON "\U0001f4c1" #define CH_OPEN_LOCK "\U0001f513"
#define CH_PARENT_DIR_ICON "\U0001f4c2"
#define CH_FILE_ICON "\U0001f4c4" #define CH_CDROM_ICON "\U0001f4bf"
#define CH_HDD_ICON "\U0001f4bd"
#define CH_MEMORY_CARD_ICON "\U0001f4be"
#define CH_POCKETSTATION_ICON "\U0001f4f1"
#define CH_HOST_ICON "\U0001f50c"
#define CH_DIR_ICON "\U0001f4c1"
#define CH_PARENT_DIR_ICON "\U0001f4c2"
#define CH_FILE_ICON "\U0001f4c4"

View File

@ -24,8 +24,8 @@
#include "ps1/system.h" #include "ps1/system.h"
/* /*
* This is a simple driver for a character LCD module, wired to the EXT-OUT * This is a simple driver for a standard character LCD module, wired to the
* connector on the 573 main board as follows: * EXT-OUT (CN4) header on the 573 main board as follows:
* *
* | EXT-OUT pin | LCD pin | * | EXT-OUT pin | LCD pin |
* | ----------: | :----------- | * | ----------: | :----------- |
@ -40,6 +40,8 @@
* *
* The `V0` (bias voltage) pin shall be connected to ground through an * The `V0` (bias voltage) pin shall be connected to ground through an
* appropriate resistor or potentiometer in order to set the display's contrast. * appropriate resistor or potentiometer in order to set the display's contrast.
* The backlight is not controlled by this driver and can be left unconnected or
* hardwired to power.
*/ */
namespace exthw { namespace exthw {

411
src/common/exthw/nfc.cpp Normal file
View File

@ -0,0 +1,411 @@
/*
* 573in1 - Copyright (C) 2022-2025 spicyjpeg
*
* 573in1 is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* 573in1 is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* 573in1. If not, see <https://www.gnu.org/licenses/>.
*/
#include <stddef.h>
#include <stdint.h>
#include "common/exthw/nfc.hpp"
#include "common/util/log.hpp"
#include "common/util/templates.hpp"
#include "common/io.hpp"
#include "ps1/system.h"
/*
* This is a simple driver for a PN532 NFC reader module connected to the 573's
* serial port. Currently the only supported feature is reading the ID of a
* Mifare or FeliCa card. The module can be wired to the cartridge slot, or to
* the unpopulated CN24 header on main board revisions that have it, as follows:
*
* | CN24 pin | Cart slot pin | Module pin |
* | -------: |- ------------: | :------------------------------------------- |
* | | 21, 22, 41, 42 | `VCC` (via 3.3V regulator, see note) |
* | 1 | 5 | `SCL`/`HSU_RX` (via level shifter, see note) |
* | 2 | 6 | `SDA`/`HSU_TX` (via level shifter, see note) |
* | 3, 4 | 1, 2, 8, 9 | `GND`, `I0`, `I1` |
* | 5, 6 | 43, 44 | None (short pins together on 573 side) |
*
* The PN532 operates at 3.3V so a voltage regulator and level shifter are
* required to adapt it to the 573's 5V signals (some modules already include
* them and can thus be wired directly). The module must be configured for HSU
* (serial) mode through the appropriate jumpers or DIP switches, or by
* grounding the `I0` and `I1` pins.
*
* Alternatively the PN532 module may be connected through an RS-232 level
* translator to the "network" port on the security cartridge (if any):
*
* | "Network" pin | Module pin |
* | ------------: | :-------------------------------------- |
* | 1 | `SCL`/`HSU_RX` (via RS-232 transceiver) |
* | 2 | `SDA`/`HSU_TX` (via RS-232 transceiver) |
* | 5 | `GND`, `I0`, `I1` |
*
* The module and transceiver will have to be powered from an external source as
* the "network" port is galvanically isolated from the rest of the system.
*/
namespace exthw {
/* PN532 packet structures */
void PN532PacketHeader::updateChecksum(void) {
#if 0
lengthChecksum = -int(length) & 0xff;
#endif
auto data = &address;
data[length] = -int(util::sum(data, length)) & 0xff;
}
bool PN532PacketHeader::validateChecksum(void) const {
if (lengthChecksum != (-int(length) & 0xff))
return false;
auto data = &address;
uint8_t value = -int(util::sum(data, length)) & 0xff;
if (value != data[length]) {
LOG_IO("mismatch, exp=0x%02x, got=0x%02x", value, data[length]);
return false;
}
return true;
}
void PN532PacketHeader::encodeCommand(int paramLength) {
paramLength += 2;
preamble = PN532_PACKET_PREAMBLE;
startCode[0] = PN532_PACKET_START1;
startCode[1] = PN532_PACKET_START2;
length = paramLength & 0xff;
lengthChecksum = -paramLength & 0xff;
address = PN532_ADDR_DEVICE;
updateChecksum();
}
bool PN532PacketHeader::decodeResponse(void) const {
if (
(startCode[0] != PN532_PACKET_START1) ||
(startCode[1] != PN532_PACKET_START2)
)
return false;
if (address != PN532_ADDR_HOST)
return false;
return validateChecksum();
}
bool PN532PacketHeader::isErrorResponse(void) const {
if (
(startCode[0] != PN532_PACKET_START1) ||
(startCode[1] != PN532_PACKET_START2)
)
return false;
if ((length != 1) || (lengthChecksum != 0xff))
return false;
return (address == PN532_ADDR_ERROR);
}
void PN532ExtPacketHeader::updateChecksum(void) {
#if 0
lengthChecksum = -int(length[0] + length[1]) & 0xff;
#endif
auto data = &address;
auto length = getDataLength();
data[length] = -int(util::sum(data, length)) & 0xff;
}
bool PN532ExtPacketHeader::validateChecksum(void) const {
if (lengthChecksum != (-int(length[0] + length[1]) & 0xff))
return false;
auto data = &address;
auto _length = getDataLength();
uint8_t value = -int(util::sum(data, _length)) & 0xff;
if (value != data[_length]) {
LOG_IO("mismatch, exp=0x%02x, got=0x%02x", value, data[_length]);
return false;
}
return true;
}
void PN532ExtPacketHeader::encodeCommand(int paramLength) {
paramLength += 2;
preamble = PN532_PACKET_PREAMBLE;
startCode[0] = PN532_PACKET_START1;
startCode[1] = PN532_PACKET_START2;
packetMagic[0] = 0xff;
packetMagic[1] = 0xff;
length[0] = (paramLength >> 8) & 0xff;
length[1] = (paramLength >> 0) & 0xff;
lengthChecksum = -int(length[0] + length[1]) & 0xff;
address = PN532_ADDR_DEVICE;
updateChecksum();
}
bool PN532ExtPacketHeader::decodeResponse(void) const {
if (
(startCode[0] != PN532_PACKET_START1) ||
(startCode[1] != PN532_PACKET_START2)
)
return false;
if ((packetMagic[0] != 0xff) || (packetMagic[1] != 0xff))
return false;
if (address != PN532_ADDR_HOST)
return false;
return validateChecksum();
}
void PN532ACKPacket::encodeACK(bool isACK) {
preamble = PN532_PACKET_PREAMBLE;
startCode[0] = PN532_PACKET_START1;
startCode[1] = PN532_PACKET_START2;
packetMagic[0] = isACK ? 0x00 : 0xff;
packetMagic[1] = isACK ? 0xff : 0x00;
postamble = PN532_PACKET_PREAMBLE;
}
bool PN532ACKPacket::decodeACK(void) const {
if (
(startCode[0] != PN532_PACKET_START1) ||
(startCode[1] != PN532_PACKET_START2)
)
return false;
if ((packetMagic[0] != 0x00) || (packetMagic[1] != 0xff))
return false;
return true;
}
/* PN532 driver */
static constexpr int _POWER_DOWN_DELAY = 1000;
static constexpr int _WAKEUP_DELAY = 1000;
static constexpr int _ACK_TIMEOUT = 1000;
static constexpr int _RESPONSE_TIMEOUT = 3000000;
static constexpr int _DEFAULT_BAUD_RATE = 115200;
static constexpr int _MAX_SEND_ATTEMPTS = 3;
static constexpr int _MAX_RECEIVE_ATTEMPTS = 3;
bool PN532Driver::_transact(
const PN532PacketHeader &request,
PN532PacketHeader &response,
size_t maxRespLength
) {
if (!_serial.isConnected()) {
LOG_IO("serial port not connected");
return false;
}
if (_isIdle) {
// If the PN532 is powered down, it must be woken up by sending at least
// 5 rising edges on TX before it can accept a new command.
_serial.writeByte(PN532_PACKET_PREAMBLE);
delayMicroseconds(_WAKEUP_DELAY);
_isIdle = false;
}
// Keep sending the request until an acknowledge packet is received.
for (int i = _MAX_SEND_ATTEMPTS; i; i--) {
_serial.writeBytes(&request.preamble, request.getPacketLength());
PN532ACKPacket ack;
if (
_serial.readBytes(&ack.preamble, sizeof(ack), _ACK_TIMEOUT)
< sizeof(ack)
)
continue;
if (!ack.decodeACK())
continue;
// Wait for a response, then validate it and send a NACK to request
// retransmission if needed.
for (int i = _MAX_RECEIVE_ATTEMPTS; i; i--) {
if (_serial.readBytes(
&response.preamble,
maxRespLength,
_RESPONSE_TIMEOUT
) >= sizeof(response)) {
if (response.decodeResponse())
return true;
if (response.isErrorResponse()) {
LOG_IO("PN532 error");
return false;
}
}
ack.encodeACK(false);
_serial.writeBytes(&ack.preamble, sizeof(ack));
}
LOG_IO("too many receive attempts failed");
return false;
}
LOG_IO("too many send attempts failed");
return false;
}
bool PN532Driver::init(void) {
_serial.init(_DEFAULT_BAUD_RATE);
_isIdle = true;
PN532Packet<4> packet;
packet.command = PN532_GET_FIRMWARE_VERSION;
packet.encodeCommand(0);
if (!_transact(packet, packet))
return false;
if (packet.param[0] != 0x32) {
LOG_IO("unsupported NFC chip, id=0x%02x", packet.param[0]);
return false;
}
LOG_IO("found PN532 v%d.%d", packet.param[1], packet.param[2]);
// This command is required to exit "low VBAT" mode.
packet.command = PN532_SAM_CONFIG;
packet.param[0] = PN532_SAM_MODE_NORMAL;
packet.encodeCommand(1);
if (!_transact(packet, packet))
return false;
return setMaxRetries(0);
}
bool PN532Driver::setMaxRetries(int count) {
if (count < 0)
count = 0xff;
PN532Packet<4> packet;
packet.command = PN532_RF_CONFIG;
packet.param[0] = PN532_RF_CONFIG_MAX_RTY_COM;
packet.param[1] = count;
packet.encodeCommand(2);
if (!_transact(packet, packet))
return false;
packet.command = PN532_RF_CONFIG;
packet.param[0] = PN532_RF_CONFIG_MAX_RETRIES;
packet.param[1] = count;
packet.param[2] = count;
packet.param[3] = count;
packet.encodeCommand(4);
return _transact(packet, packet);
}
bool PN532Driver::goIdle(void) {
PN532Packet<1> packet;
packet.command = PN532_POWER_DOWN;
packet.param[0] = PN532_WAKEUP_SOURCE_HSU;
packet.encodeCommand(1);
if (!_transact(packet, packet))
return false;
delayMicroseconds(_POWER_DOWN_DELAY);
_isIdle = true;
return true;
}
size_t PN532Driver::readISO14443CardID(uint8_t *output) {
PN532Packet<1 + 11> packet;
packet.command = PN532_IN_LIST_TARGETS;
packet.param[0] = 1;
packet.param[1] = PN532_LIST_TARGETS_ISO14443A;
packet.encodeCommand(2);
if (!_transact(packet, packet))
return 0;
if (!packet.param[0])
return 0;
auto atqa = util::concat2(packet.param[2], packet.param[1]);
auto saq = packet.param[3];
auto idLength = packet.param[4];
#if 0
char buffer[32];
util::hexToString(buffer, &packet.param[5], idLength, '-');
LOG_IO("%s", buffer);
LOG_IO("atqa=0x%04x, saq=0x%02x", atqa, saq);
#endif
__builtin_memcpy(output, &packet.param[5], idLength);
return idLength;
}
size_t PN532Driver::readFeliCaCardID(uint8_t *output, uint16_t systemCode) {
PN532Packet<1 + 20> packet;
packet.command = PN532_IN_LIST_TARGETS;
packet.param[0] = 1;
packet.param[1] = PN532_LIST_TARGETS_FELICA_212;
packet.param[2] = FELICA_POLL;
packet.param[3] = (systemCode >> 8) & 0xff;
packet.param[4] = (systemCode >> 0) & 0xff;
packet.param[5] = FELICA_REQ_CODE_NONE;
packet.param[6] = 0;
packet.encodeCommand(7);
if (!_transact(packet, packet))
return 0;
if (!packet.param[0])
return 0;
auto respLength = packet.param[1];
auto respCode = packet.param[2];
if ((respLength != 18) && (respLength != 20)) {
LOG_IO("invalid response length: 0x%02x", respLength);
return 0;
}
if (respCode != 0x01) {
LOG_IO("invalid response code: 0x%02x", respCode);
return 0;
}
#if 0
char buffer[24];
util::hexToString(buffer, &packet.param[3], 8, '-');
LOG_IO("%s", buffer);
#endif
__builtin_memcpy(output, &packet.param[3], 8);
return 8;
}
}

228
src/common/exthw/nfc.hpp Normal file
View File

@ -0,0 +1,228 @@
/*
* 573in1 - Copyright (C) 2022-2025 spicyjpeg
*
* 573in1 is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* 573in1 is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* 573in1. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <stddef.h>
#include <stdint.h>
#include "common/util/templates.hpp"
#include "common/io.hpp"
namespace exthw {
/* PN532 command definitions */
enum PN532Command : uint8_t {
PN532_DIAGNOSE = 0x00,
PN532_GET_FIRMWARE_VERSION = 0x02,
PN532_GET_GENERAL_STATUS = 0x04,
PN532_READ_REG = 0x06,
PN532_WRITE_REG = 0x08,
PN532_READ_GPIO = 0x0c,
PN532_WRITE_GPIO = 0x0e,
PN532_SET_BAUD_RATE = 0x10,
PN532_SET_PARAMETERS = 0x12,
PN532_SAM_CONFIG = 0x14,
PN532_POWER_DOWN = 0x16,
PN532_RF_CONFIG = 0x32,
PN532_IN_DATA_EXCHANGE = 0x40,
PN532_IN_COMMUNICATE_THRU = 0x42,
PN532_IN_DESELECT = 0x44,
PN532_IN_JUMP_FOR_PSL = 0x46,
PN532_IN_LIST_TARGETS = 0x4a,
PN532_IN_PSL = 0x4e,
PN532_IN_ATR = 0x50,
PN532_IN_RELEASE = 0x52,
PN532_IN_SELECT = 0x54,
PN532_IN_JUMP_FOR_DEP = 0x56,
PN532_RF_REGULATION_TEST = 0x58,
PN532_IN_AUTO_POLL = 0x60,
PN532_TG_GET_DATA = 0x86,
PN532_TG_GET_COMMAND = 0x88,
PN532_TG_GET_TARGET_STATUS = 0x8a,
PN532_TG_INIT = 0x8c,
PN532_TG_SET_DATA = 0x8e,
PN532_TG_SEND_RESPONSE = 0x90,
PN532_TG_SET_GENERAL_BYTES = 0x92,
PN532_TG_SET_METADATA = 0x94
};
/* PN532 parameter definitions */
enum PN532SAMMode : uint8_t {
PN532_SAM_MODE_NORMAL = 0x01,
PN532_SAM_MODE_VIRTUAL_CARD = 0x02,
PN532_SAM_MODE_WIRED_CARD = 0x03,
PN532_SAM_MODE_DUAL_CARD = 0x04
};
enum PN532RFConfigItem : uint8_t {
PN532_RF_CONFIG_FIELD = 0x01,
PN532_RF_CONFIG_TIMEOUTS = 0x02,
PN532_RF_CONFIG_MAX_RTY_COM = 0x04,
PN532_RF_CONFIG_MAX_RETRIES = 0x05,
PN532_RF_CONFIG_CIU_ISO14443A = 0x0a,
PN532_RF_CONFIG_CIU_FELICA = 0x0b,
PN532_RF_CONFIG_CIU_ISO14443B = 0x0c,
PN532_RF_CONFIG_CIU_ISO14443_4 = 0x0d
};
enum PN532ListTargetsType : uint8_t {
PN532_LIST_TARGETS_ISO14443A = 0x00,
PN532_LIST_TARGETS_FELICA_212 = 0x01,
PN532_LIST_TARGETS_FELICA_414 = 0x02,
PN532_LIST_TARGETS_ISO14443B = 0x03
};
enum PN532WakeupSource : uint8_t {
PN532_WAKEUP_SOURCE_INT0 = 1 << 0,
PN532_WAKEUP_SOURCE_INT1 = 1 << 1,
PN532_WAKEUP_SOURCE_RF = 1 << 3,
PN532_WAKEUP_SOURCE_HSU = 1 << 4,
PN532_WAKEUP_SOURCE_SPI = 1 << 5,
PN532_WAKEUP_SOURCE_GPIO = 1 << 6,
PN532_WAKEUP_SOURCE_I2C = 1 << 7
};
/* FeliCa definitions */
enum FeliCaCommand : uint8_t {
FELICA_POLL = 0x00,
FELICA_REQUEST_SERVICE = 0x02,
FELICA_REQUEST_RESPONSE = 0x04,
FELICA_READ_WITHOUT_ENC = 0x06,
FELICA_WRITE_WITHOUT_ENC = 0x08,
FELICA_REQUEST_SYSTEM_CODE = 0x0c
};
enum FeliCaRequestCode : uint8_t {
FELICA_REQ_CODE_NONE = 0x00,
FELICA_REQ_CODE_SYSTEM_CODE = 0x01,
FELICA_REQ_CODE_COMMUNICATION = 0x00
};
/* PN532 packet structures */
static constexpr uint8_t PN532_PACKET_PREAMBLE = 0x55;
static constexpr uint8_t PN532_PACKET_START1 = 0x00;
static constexpr uint8_t PN532_PACKET_START2 = 0xff;
enum PN532PacketAddress : uint8_t {
PN532_ADDR_ERROR = 0x7f,
PN532_ADDR_DEVICE = 0xd4,
PN532_ADDR_HOST = 0xd5
};
class PN532PacketHeader {
public:
uint8_t preamble, startCode[2];
uint8_t length, lengthChecksum;
uint8_t address;
inline size_t getPacketLength(void) const {
return (sizeof(PN532PacketHeader) - 1) + length + 2;
}
inline uint8_t *getData(void) {
return reinterpret_cast<uint8_t *>(this + 1);
}
void updateChecksum(void);
bool validateChecksum(void) const;
void encodeCommand(int paramLength);
bool decodeResponse(void) const;
bool isErrorResponse(void) const;
};
template<size_t N> class PN532Packet : public PN532PacketHeader {
public:
uint8_t command;
uint8_t param[N];
};
class PN532ExtPacketHeader {
public:
uint8_t preamble, startCode[2];
uint8_t packetMagic[2];
uint8_t length[2], lengthChecksum;
uint8_t address;
inline size_t getDataLength(void) const {
return util::concat2(length[1], length[0]);
}
inline size_t getPacketLength(void) const {
return (sizeof(PN532PacketHeader) - 1) + getDataLength() + 2;
}
inline uint8_t *getData(void) {
return reinterpret_cast<uint8_t *>(this + 1);
}
void updateChecksum(void);
bool validateChecksum(void) const;
void encodeCommand(int paramLength);
bool decodeResponse(void) const;
};
template<size_t N> class PN532ExtPacket : public PN532ExtPacketHeader {
public:
uint8_t command;
uint8_t param[N];
};
class PN532ACKPacket {
public:
uint8_t preamble, startCode[2];
uint8_t packetMagic[2];
uint8_t postamble;
void encodeACK(bool isACK = true);
bool decodeACK(void) const;
};
/* PN532 driver */
class PN532Driver {
private:
io::UARTDriver &_serial;
bool _isIdle;
template<size_t N> inline bool _transact(
const PN532PacketHeader &request,
PN532Packet<N> &response
) {
_transact(request, response, sizeof(response));
}
bool _transact(
const PN532PacketHeader &request,
PN532PacketHeader &response,
size_t maxRespLength
);
public:
inline PN532Driver(io::UARTDriver &serial)
: _serial(serial) {}
bool init(void);
bool setMaxRetries(int count);
bool goIdle(void);
size_t readISO14443CardID(uint8_t *output);
size_t readFeliCaCardID(uint8_t *output, uint16_t systemCode = 0xffff);
};
}

View File

@ -282,14 +282,24 @@ void TextOverlay::draw(Context &ctx, bool active) const {
if (leftText) { if (leftText) {
rect.x = 8; rect.x = 8;
rect.w = ctx.gpuCtx.width - 16; rect.w = ctx.gpuCtx.width - 16;
ctx.font.draw(ctx.gpuCtx, leftText, rect, ctx.colors[COLOR_TEXT2]); ctx.font.draw(
ctx.gpuCtx,
leftText,
rect,
ctx.colors[COLOR_TEXT2]
);
} }
if (rightText) { if (rightText) {
int width = ctx.font.getStringWidth(rightText); int width = ctx.font.getStringWidth(rightText);
rect.x = ctx.gpuCtx.width - (8 + width); rect.x = ctx.gpuCtx.width - (8 + width);
rect.w = width; rect.w = width;
ctx.font.draw(ctx.gpuCtx, rightText, rect, ctx.colors[COLOR_TEXT2]); ctx.font.draw(
ctx.gpuCtx,
rightText,
rect,
ctx.colors[COLOR_TEXT2]
);
} }
} }
@ -302,7 +312,8 @@ void SplashOverlay::draw(Context &ctx, bool active) const {
// Backdrop // Backdrop
_newLayer(ctx, 0, 0, ctx.gpuCtx.width, ctx.gpuCtx.height); _newLayer(ctx, 0, 0, ctx.gpuCtx.width, ctx.gpuCtx.height);
ctx.gpuCtx.drawBackdrop( ctx.gpuCtx.drawBackdrop(
gp0_rgb(brightness, brightness, brightness), GP0_BLEND_SUBTRACT gp0_rgb(brightness, brightness, brightness),
GP0_BLEND_SUBTRACT
); );
if (brightness < 0xff) if (brightness < 0xff)
@ -337,7 +348,10 @@ void LogOverlay::draw(Context &ctx, bool active) const {
// Backdrop // Backdrop
_newLayer( _newLayer(
ctx, 0, offset - ctx.gpuCtx.height, ctx.gpuCtx.width, ctx,
0,
offset - ctx.gpuCtx.height,
ctx.gpuCtx.width,
ctx.gpuCtx.height ctx.gpuCtx.height
); );
ctx.gpuCtx.drawBackdrop(ctx.colors[COLOR_BACKDROP], GP0_BLEND_SUBTRACT); ctx.gpuCtx.drawBackdrop(ctx.colors[COLOR_BACKDROP], GP0_BLEND_SUBTRACT);
@ -355,7 +369,10 @@ void LogOverlay::draw(Context &ctx, bool active) const {
for (int i = (screenHeight / lineHeight) - 1; i >= 0; i--) { for (int i = (screenHeight / lineHeight) - 1; i >= 0; i--) {
ctx.font.draw( ctx.font.draw(
ctx.gpuCtx, _buffer.getLine(i), rect, ctx.colors[COLOR_TEXT1] ctx.gpuCtx,
_buffer.getLine(i),
rect,
ctx.colors[COLOR_TEXT1]
); );
rect.y1 = rect.y2; rect.y1 = rect.y2;
@ -378,7 +395,8 @@ void ScreenshotOverlay::draw(Context &ctx, bool active) const {
_newLayer(ctx, 0, 0, ctx.gpuCtx.width, ctx.gpuCtx.height); _newLayer(ctx, 0, 0, ctx.gpuCtx.width, ctx.gpuCtx.height);
ctx.gpuCtx.drawBackdrop( ctx.gpuCtx.drawBackdrop(
gp0_rgb(brightness, brightness, brightness), GP0_BLEND_ADD gp0_rgb(brightness, brightness, brightness),
GP0_BLEND_ADD
); );
} }
@ -423,7 +441,8 @@ void BackdropScreen::draw(Context &ctx, bool active) const {
_newLayer(ctx, 0, 0, ctx.gpuCtx.width, ctx.gpuCtx.height); _newLayer(ctx, 0, 0, ctx.gpuCtx.width, ctx.gpuCtx.height);
ctx.gpuCtx.drawBackdrop( ctx.gpuCtx.drawBackdrop(
gp0_rgb(brightness, brightness, brightness), GP0_BLEND_ADD gp0_rgb(brightness, brightness, brightness),
GP0_BLEND_ADD
); );
} }
@ -443,28 +462,47 @@ void ModalScreen::draw(Context &ctx, bool active) const {
int windowHeight = TITLE_BAR_HEIGHT + _height; int windowHeight = TITLE_BAR_HEIGHT + _height;
_newLayer( _newLayer(
ctx, (ctx.gpuCtx.width - _width) / 2, ctx,
(ctx.gpuCtx.height - windowHeight) / 2, _width + SHADOW_OFFSET, (ctx.gpuCtx.width - _width) / 2,
(ctx.gpuCtx.height - windowHeight) / 2,
_width + SHADOW_OFFSET,
windowHeight + SHADOW_OFFSET windowHeight + SHADOW_OFFSET
); );
_setBlendMode(ctx, GP0_BLEND_SEMITRANS, true); _setBlendMode(ctx, GP0_BLEND_SEMITRANS, true);
// Window // Window
ctx.gpuCtx.drawGradientRectD( ctx.gpuCtx.drawGradientRectD(
0, 0, _width, windowHeight, ctx.colors[COLOR_WINDOW1], 0,
ctx.colors[COLOR_WINDOW2], ctx.colors[COLOR_WINDOW3] 0,
_width,
windowHeight,
ctx.colors[COLOR_WINDOW1],
ctx.colors[COLOR_WINDOW2],
ctx.colors[COLOR_WINDOW3]
); );
ctx.gpuCtx.drawGradientRectH( ctx.gpuCtx.drawGradientRectH(
0, 0, _titleBarAnim.getValue(ctx.time), TITLE_BAR_HEIGHT, 0,
ctx.colors[COLOR_ACCENT1], ctx.colors[COLOR_ACCENT2] 0,
_titleBarAnim.getValue(ctx.time),
TITLE_BAR_HEIGHT,
ctx.colors[COLOR_ACCENT1],
ctx.colors[COLOR_ACCENT2]
); );
ctx.gpuCtx.drawRect( ctx.gpuCtx.drawRect(
_width, SHADOW_OFFSET, SHADOW_OFFSET, windowHeight, _width,
ctx.colors[COLOR_SHADOW], true SHADOW_OFFSET,
SHADOW_OFFSET,
windowHeight,
ctx.colors[COLOR_SHADOW],
true
); );
ctx.gpuCtx.drawRect( ctx.gpuCtx.drawRect(
SHADOW_OFFSET, windowHeight, _width - SHADOW_OFFSET, SHADOW_OFFSET, SHADOW_OFFSET,
ctx.colors[COLOR_SHADOW], true windowHeight,
_width - SHADOW_OFFSET,
SHADOW_OFFSET,
ctx.colors[COLOR_SHADOW],
true
); );
// Text // Text
@ -474,11 +512,22 @@ void ModalScreen::draw(Context &ctx, bool active) const {
rect.y1 = TITLE_BAR_PADDING; rect.y1 = TITLE_BAR_PADDING;
rect.x2 = _width - TITLE_BAR_PADDING; rect.x2 = _width - TITLE_BAR_PADDING;
rect.y2 = TITLE_BAR_PADDING + ctx.font.getLineHeight(); rect.y2 = TITLE_BAR_PADDING + ctx.font.getLineHeight();
ctx.font.draw(ctx.gpuCtx, _title, rect, ctx.colors[COLOR_TITLE]); ctx.font.draw(
ctx.gpuCtx,
_title,
rect,
ctx.colors[COLOR_TITLE]
);
rect.y1 = TITLE_BAR_HEIGHT + MODAL_PADDING; rect.y1 = TITLE_BAR_HEIGHT + MODAL_PADDING;
rect.y2 = _height - MODAL_PADDING; rect.y2 = _height - MODAL_PADDING;
ctx.font.draw(ctx.gpuCtx, _body, rect, ctx.colors[COLOR_TEXT1], true); ctx.font.draw(
ctx.gpuCtx,
_body,
rect,
ctx.colors[COLOR_TEXT1],
true
);
} }
} }

View File

@ -42,7 +42,11 @@ void TextScreen::draw(Context &ctx, bool active) const {
// Top/bottom text // Top/bottom text
_newLayer( _newLayer(
ctx, SCREEN_MARGIN_X, SCREEN_MARGIN_Y, screenWidth, screenHeight ctx,
SCREEN_MARGIN_X,
SCREEN_MARGIN_Y,
screenWidth,
screenHeight
); );
gpu::Rect rect; gpu::Rect rect;
@ -51,11 +55,22 @@ void TextScreen::draw(Context &ctx, bool active) const {
rect.y1 = 0; rect.y1 = 0;
rect.x2 = screenWidth; rect.x2 = screenWidth;
rect.y2 = lineHeight; rect.y2 = lineHeight;
ctx.font.draw(ctx.gpuCtx, _title, rect, ctx.colors[COLOR_TITLE]); ctx.font.draw(
ctx.gpuCtx,
_title,
rect,
ctx.colors[COLOR_TITLE]
);
rect.y1 = screenHeight - SCREEN_PROMPT_HEIGHT_MIN; rect.y1 = screenHeight - SCREEN_PROMPT_HEIGHT_MIN;
rect.y2 = screenHeight; rect.y2 = screenHeight;
ctx.font.draw(ctx.gpuCtx, _prompt, rect, ctx.colors[COLOR_TEXT1], true); ctx.font.draw(
ctx.gpuCtx,
_prompt,
rect,
ctx.colors[COLOR_TEXT1],
true
);
int bodyOffset = lineHeight + SCREEN_BLOCK_MARGIN; int bodyOffset = lineHeight + SCREEN_BLOCK_MARGIN;
int bodyHeight = screenHeight - int bodyHeight = screenHeight -
@ -63,7 +78,10 @@ void TextScreen::draw(Context &ctx, bool active) const {
// Scrollable text // Scrollable text
_newLayer( _newLayer(
ctx, SCREEN_MARGIN_X, SCREEN_MARGIN_Y + bodyOffset, screenWidth, ctx,
SCREEN_MARGIN_X,
SCREEN_MARGIN_Y + bodyOffset,
screenWidth,
bodyHeight bodyHeight
); );
@ -75,7 +93,14 @@ void TextScreen::draw(Context &ctx, bool active) const {
clip.y1 = 0; clip.y1 = 0;
clip.x2 = screenWidth; clip.x2 = screenWidth;
clip.y2 = bodyHeight; clip.y2 = bodyHeight;
ctx.font.draw(ctx.gpuCtx, _body, rect, clip, ctx.colors[COLOR_TEXT1], true); ctx.font.draw(
ctx.gpuCtx,
_body,
rect,
clip,
ctx.colors[COLOR_TEXT1],
true
);
} }
void TextScreen::update(Context &ctx) { void TextScreen::update(Context &ctx) {
@ -146,14 +171,22 @@ void ImageScreen::draw(Context &ctx, bool active) const {
int _height = height + _imagePadding; int _height = height + _imagePadding;
ctx.gpuCtx.drawRect( ctx.gpuCtx.drawRect(
x - _width, y - _height, _width * 2, _height * 2, _backdropColor x - _width,
y - _height,
_width * 2,
_height * 2,
_backdropColor
); );
} }
// Image // Image
if (_imageScale > 1) if (_imageScale > 1)
_image->drawScaled( _image->drawScaled(
ctx.gpuCtx, x - width - 1, y - height - 1, width * 2, height * 2 ctx.gpuCtx,
x - width - 1,
y - height - 1,
width * 2,
height * 2
); );
else else
_image->draw(ctx.gpuCtx, x - width, y - height); _image->draw(ctx.gpuCtx, x - width, y - height);
@ -166,11 +199,22 @@ void ImageScreen::draw(Context &ctx, bool active) const {
rect.y1 = SCREEN_MARGIN_Y; rect.y1 = SCREEN_MARGIN_Y;
rect.x2 = ctx.gpuCtx.width - SCREEN_MARGIN_X; rect.x2 = ctx.gpuCtx.width - SCREEN_MARGIN_X;
rect.y2 = SCREEN_MARGIN_Y + lineHeight; rect.y2 = SCREEN_MARGIN_Y + lineHeight;
ctx.font.draw(ctx.gpuCtx, _title, rect, ctx.colors[COLOR_TITLE]); ctx.font.draw(
ctx.gpuCtx,
_title,
rect,
ctx.colors[COLOR_TITLE]
);
rect.y1 = ctx.gpuCtx.height - (SCREEN_MARGIN_Y + SCREEN_PROMPT_HEIGHT); rect.y1 = ctx.gpuCtx.height - (SCREEN_MARGIN_Y + SCREEN_PROMPT_HEIGHT);
rect.y2 = ctx.gpuCtx.height - SCREEN_MARGIN_Y; rect.y2 = ctx.gpuCtx.height - SCREEN_MARGIN_Y;
ctx.font.draw(ctx.gpuCtx, _prompt, rect, ctx.colors[COLOR_TEXT1], true); ctx.font.draw(
ctx.gpuCtx,
_prompt,
rect,
ctx.colors[COLOR_TEXT1],
true
);
} }
ListScreen::ListScreen(void) ListScreen::ListScreen(void)
@ -196,25 +240,37 @@ void ListScreen::_drawItems(Context &ctx) const {
if ((itemY + itemHeight) >= 0) { if ((itemY + itemHeight) >= 0) {
if (i == _activeItem) { if (i == _activeItem) {
ctx.gpuCtx.drawRect( ctx.gpuCtx.drawRect(
LIST_BOX_PADDING, itemY, itemWidth, itemHeight, LIST_BOX_PADDING,
itemY,
itemWidth,
itemHeight,
ctx.colors[COLOR_HIGHLIGHT2] ctx.colors[COLOR_HIGHLIGHT2]
); );
ctx.gpuCtx.drawRect( ctx.gpuCtx.drawRect(
LIST_BOX_PADDING, itemY, _itemAnim.getValue(ctx.time), LIST_BOX_PADDING,
itemHeight, ctx.colors[COLOR_HIGHLIGHT1] itemY,
_itemAnim.getValue(ctx.time),
itemHeight,
ctx.colors[COLOR_HIGHLIGHT1]
); );
rect.y1 = itemY + LIST_ITEM_PADDING + lineHeight; rect.y1 = itemY + LIST_ITEM_PADDING + lineHeight;
rect.y2 = rect.y1 + lineHeight; rect.y2 = rect.y1 + lineHeight;
ctx.font.draw( ctx.font.draw(
ctx.gpuCtx, _itemPrompt, rect, ctx.colors[COLOR_SUBTITLE] ctx.gpuCtx,
_itemPrompt,
rect,
ctx.colors[COLOR_SUBTITLE]
); );
} }
rect.y1 = itemY + LIST_ITEM_PADDING; rect.y1 = itemY + LIST_ITEM_PADDING;
rect.y2 = rect.y1 + lineHeight; rect.y2 = rect.y1 + lineHeight;
ctx.font.draw( ctx.font.draw(
ctx.gpuCtx, _getItemName(ctx, i), rect, ctx.colors[COLOR_TITLE] ctx.gpuCtx,
_getItemName(ctx, i),
rect,
ctx.colors[COLOR_TITLE]
); );
} }
@ -237,7 +293,11 @@ void ListScreen::draw(Context &ctx, bool active) const {
int lineHeight = ctx.font.getLineHeight(); int lineHeight = ctx.font.getLineHeight();
_newLayer( _newLayer(
ctx, SCREEN_MARGIN_X, SCREEN_MARGIN_Y, screenWidth, screenHeight ctx,
SCREEN_MARGIN_X,
SCREEN_MARGIN_Y,
screenWidth,
screenHeight
); );
// Text // Text
@ -247,25 +307,46 @@ void ListScreen::draw(Context &ctx, bool active) const {
rect.y1 = 0; rect.y1 = 0;
rect.x2 = screenWidth; rect.x2 = screenWidth;
rect.y2 = lineHeight; rect.y2 = lineHeight;
ctx.font.draw(ctx.gpuCtx, _title, rect, ctx.colors[COLOR_TITLE]); ctx.font.draw(
ctx.gpuCtx,
_title,
rect,
ctx.colors[COLOR_TITLE]
);
rect.y1 = screenHeight - SCREEN_PROMPT_HEIGHT; rect.y1 = screenHeight - SCREEN_PROMPT_HEIGHT;
rect.y2 = screenHeight; rect.y2 = screenHeight;
ctx.font.draw(ctx.gpuCtx, _prompt, rect, ctx.colors[COLOR_TEXT1], true); ctx.font.draw(
ctx.gpuCtx,
_prompt,
rect,
ctx.colors[COLOR_TEXT1],
true
);
_newLayer( _newLayer(
ctx, SCREEN_MARGIN_X, ctx,
SCREEN_MARGIN_Y + lineHeight + SCREEN_BLOCK_MARGIN, screenWidth, SCREEN_MARGIN_X,
SCREEN_MARGIN_Y + lineHeight + SCREEN_BLOCK_MARGIN,
screenWidth,
listHeight listHeight
); );
_setBlendMode(ctx, GP0_BLEND_SEMITRANS, true); _setBlendMode(ctx, GP0_BLEND_SEMITRANS, true);
// List box // List box
ctx.gpuCtx.drawRect( ctx.gpuCtx.drawRect(
0, 0, screenWidth / 2, listHeight, ctx.colors[COLOR_BOX1] 0,
0,
screenWidth / 2,
listHeight,
ctx.colors[COLOR_BOX1]
); );
ctx.gpuCtx.drawGradientRectH( ctx.gpuCtx.drawGradientRectH(
screenWidth / 2, 0, screenWidth / 2, listHeight, ctx.colors[COLOR_BOX1], screenWidth / 2,
0,
screenWidth / 2,
listHeight,
ctx.colors[COLOR_BOX1],
ctx.colors[COLOR_BOX2] ctx.colors[COLOR_BOX2]
); );
@ -282,13 +363,19 @@ void ListScreen::draw(Context &ctx, bool active) const {
if (_activeItem) { if (_activeItem) {
iconRect.y = LIST_BOX_PADDING; iconRect.y = LIST_BOX_PADDING;
ctx.font.draw( ctx.font.draw(
ctx.gpuCtx, CH_UP_ARROW, iconRect, ctx.colors[COLOR_TEXT1] ctx.gpuCtx,
CH_UP_ARROW,
iconRect,
ctx.colors[COLOR_TEXT1]
); );
} }
if (_activeItem < (_listLength - 1)) { if (_activeItem < (_listLength - 1)) {
iconRect.y = listHeight - (lineHeight + LIST_BOX_PADDING); iconRect.y = listHeight - (lineHeight + LIST_BOX_PADDING);
ctx.font.draw( ctx.font.draw(
ctx.gpuCtx, CH_DOWN_ARROW, iconRect, ctx.colors[COLOR_TEXT1] ctx.gpuCtx,
CH_DOWN_ARROW,
iconRect,
ctx.colors[COLOR_TEXT1]
); );
} }
} }
@ -335,9 +422,17 @@ void ListScreen::update(Context &ctx) {
int currentOffset = -_scrollAnim.getTargetValue(); int currentOffset = -_scrollAnim.getTargetValue();
if (topOffset < currentOffset) if (topOffset < currentOffset)
_scrollAnim.setValue(ctx.time, LIST_BOX_PADDING - topOffset, SPEED_FAST); _scrollAnim.setValue(
ctx.time,
LIST_BOX_PADDING - topOffset,
SPEED_FAST
);
else if (bottomOffset > currentOffset) else if (bottomOffset > currentOffset)
_scrollAnim.setValue(ctx.time, -(LIST_BOX_PADDING + bottomOffset), SPEED_FAST); _scrollAnim.setValue(
ctx.time,
-(LIST_BOX_PADDING + bottomOffset),
SPEED_FAST
);
} }
} }

View File

@ -59,32 +59,51 @@ void MessageBoxScreen::draw(Context &ctx, bool active) const {
if (_locked) { if (_locked) {
ctx.gpuCtx.drawRect( ctx.gpuCtx.drawRect(
buttonX, buttonY, rect.w, BUTTON_HEIGHT, buttonX,
ctx.colors[COLOR_SHADOW], true buttonY,
rect.w,
BUTTON_HEIGHT,
ctx.colors[COLOR_SHADOW],
true
); );
ctx.font.draw( ctx.font.draw(
ctx.gpuCtx, _buttons[i], rect, ctx.colors[COLOR_TEXT2] ctx.gpuCtx,
_buttons[i],
rect,
ctx.colors[COLOR_TEXT2]
); );
} else { } else {
if (i == activeButton) { if (i == activeButton) {
ctx.gpuCtx.drawRect( ctx.gpuCtx.drawRect(
buttonX, buttonY, rect.w, BUTTON_HEIGHT, buttonX,
buttonY,
rect.w,
BUTTON_HEIGHT,
ctx.colors[COLOR_HIGHLIGHT2] ctx.colors[COLOR_HIGHLIGHT2]
); );
ctx.gpuCtx.drawRect( ctx.gpuCtx.drawRect(
buttonX, buttonY, _buttonAnim.getValue(ctx.time), buttonX,
BUTTON_HEIGHT, ctx.colors[COLOR_HIGHLIGHT1] buttonY,
_buttonAnim.getValue(ctx.time),
BUTTON_HEIGHT,
ctx.colors[COLOR_HIGHLIGHT1]
); );
} else { } else {
ctx.gpuCtx.drawRect( ctx.gpuCtx.drawRect(
buttonX, buttonY, rect.w, BUTTON_HEIGHT, buttonX,
buttonY,
rect.w,
BUTTON_HEIGHT,
ctx.colors[COLOR_WINDOW3] ctx.colors[COLOR_WINDOW3]
); );
} }
ctx.font.draw( ctx.font.draw(
ctx.gpuCtx, _buttons[i], rect, ctx.colors[COLOR_TITLE] ctx.gpuCtx,
_buttons[i],
rect,
ctx.colors[COLOR_TITLE]
); );
} }
@ -152,13 +171,17 @@ void HexEntryScreen::draw(Context &ctx, bool active) const {
if (!active) if (!active)
return; return;
int boxY = TITLE_BAR_HEIGHT + _height - int boxY =
(BUTTON_HEIGHT + MODAL_PADDING) * 2; TITLE_BAR_HEIGHT + _height - (BUTTON_HEIGHT + MODAL_PADDING) * 2;
int boxWidth = _width - MODAL_PADDING * 2; int boxWidth = _width - MODAL_PADDING * 2;
// Text box // Text box
ctx.gpuCtx.drawRect( ctx.gpuCtx.drawRect(
MODAL_PADDING, boxY, boxWidth, BUTTON_HEIGHT, ctx.colors[COLOR_BOX1] MODAL_PADDING,
boxY,
boxWidth,
BUTTON_HEIGHT,
ctx.colors[COLOR_BOX1]
); );
char string[128]; char string[128];
@ -173,8 +196,11 @@ void HexEntryScreen::draw(Context &ctx, bool active) const {
if (_activeButton < _buttonIndexOffset) if (_activeButton < _buttonIndexOffset)
ctx.gpuCtx.drawGradientRectV( ctx.gpuCtx.drawGradientRectV(
stringOffset + _cursorAnim.getValue(ctx.time), stringOffset + _cursorAnim.getValue(ctx.time),
boxY + BUTTON_HEIGHT / 2, _charWidth, BUTTON_HEIGHT / 2, boxY + BUTTON_HEIGHT / 2,
ctx.colors[COLOR_BOX1], ctx.colors[COLOR_HIGHLIGHT1] _charWidth,
BUTTON_HEIGHT / 2,
ctx.colors[COLOR_BOX1],
ctx.colors[COLOR_HIGHLIGHT1]
); );
// Current string // Current string
@ -182,7 +208,12 @@ void HexEntryScreen::draw(Context &ctx, bool active) const {
rect.y1 = boxY + BUTTON_PADDING; rect.y1 = boxY + BUTTON_PADDING;
rect.x2 = _width - MODAL_PADDING; rect.x2 = _width - MODAL_PADDING;
rect.y2 = boxY + BUTTON_PADDING + ctx.font.getLineHeight(); rect.y2 = boxY + BUTTON_PADDING + ctx.font.getLineHeight();
ctx.font.draw(ctx.gpuCtx, string, rect, ctx.colors[COLOR_TITLE]); ctx.font.draw(
ctx.gpuCtx,
string,
rect,
ctx.colors[COLOR_TITLE]
);
// Highlighted field // Highlighted field
if (_activeButton < _buttonIndexOffset) { if (_activeButton < _buttonIndexOffset) {
@ -190,7 +221,12 @@ void HexEntryScreen::draw(Context &ctx, bool active) const {
ptr[1] = 0; ptr[1] = 0;
rect.x1 = stringOffset + _cursorAnim.getTargetValue(); rect.x1 = stringOffset + _cursorAnim.getTargetValue();
ctx.font.draw(ctx.gpuCtx, ptr, rect, ctx.colors[COLOR_SUBTITLE]); ctx.font.draw(
ctx.gpuCtx,
ptr,
rect,
ctx.colors[COLOR_SUBTITLE]
);
} }
} }
@ -314,13 +350,17 @@ void DateEntryScreen::draw(Context &ctx, bool active) const {
if (!active) if (!active)
return; return;
int boxY = TITLE_BAR_HEIGHT + _height - int boxY =
(BUTTON_HEIGHT + MODAL_PADDING) * 2; TITLE_BAR_HEIGHT + _height - (BUTTON_HEIGHT + MODAL_PADDING) * 2;
int boxWidth = _width - MODAL_PADDING * 2; int boxWidth = _width - MODAL_PADDING * 2;
// Text box // Text box
ctx.gpuCtx.drawRect( ctx.gpuCtx.drawRect(
MODAL_PADDING, boxY, boxWidth, BUTTON_HEIGHT, ctx.colors[COLOR_BOX1] MODAL_PADDING,
boxY,
boxWidth,
BUTTON_HEIGHT,
ctx.colors[COLOR_BOX1]
); );
char string[24]; char string[24];
@ -342,8 +382,10 @@ void DateEntryScreen::draw(Context &ctx, bool active) const {
if (_activeButton < _buttonIndexOffset) if (_activeButton < _buttonIndexOffset)
ctx.gpuCtx.drawGradientRectV( ctx.gpuCtx.drawGradientRectV(
stringOffset + _cursorAnim.getValue(ctx.time), stringOffset + _cursorAnim.getValue(ctx.time),
boxY + BUTTON_HEIGHT / 2, _charWidth * fieldLength, boxY + BUTTON_HEIGHT / 2,
BUTTON_HEIGHT / 2,ctx.colors[COLOR_BOX1], _charWidth * fieldLength,
BUTTON_HEIGHT / 2,
ctx.colors[COLOR_BOX1],
ctx.colors[COLOR_HIGHLIGHT1] ctx.colors[COLOR_HIGHLIGHT1]
); );
@ -352,7 +394,12 @@ void DateEntryScreen::draw(Context &ctx, bool active) const {
rect.y1 = boxY + BUTTON_PADDING; rect.y1 = boxY + BUTTON_PADDING;
rect.x2 = _width - MODAL_PADDING; rect.x2 = _width - MODAL_PADDING;
rect.y2 = boxY + BUTTON_PADDING + ctx.font.getLineHeight(); rect.y2 = boxY + BUTTON_PADDING + ctx.font.getLineHeight();
ctx.font.draw(ctx.gpuCtx, string, rect, ctx.colors[COLOR_TITLE]); ctx.font.draw(
ctx.gpuCtx,
string,
rect,
ctx.colors[COLOR_TITLE]
);
// Highlighted field // Highlighted field
if (_activeButton < _buttonIndexOffset) { if (_activeButton < _buttonIndexOffset) {
@ -360,7 +407,12 @@ void DateEntryScreen::draw(Context &ctx, bool active) const {
ptr[fieldLength] = 0; ptr[fieldLength] = 0;
rect.x1 = stringOffset + _cursorAnim.getTargetValue(); rect.x1 = stringOffset + _cursorAnim.getTargetValue();
ctx.font.draw(ctx.gpuCtx, ptr, rect, ctx.colors[COLOR_SUBTITLE]); ctx.font.draw(
ctx.gpuCtx,
ptr,
rect,
ctx.colors[COLOR_SUBTITLE]
);
} }
} }
@ -422,7 +474,9 @@ void DateEntryScreen::update(Context &ctx) {
// Update the cursor's position if necessary. // Update the cursor's position if necessary.
if (oldActive != _activeButton) if (oldActive != _activeButton)
_cursorAnim.setValue( _cursorAnim.setValue(
ctx.time, _fieldOffsets[_activeButton], SPEED_FASTEST ctx.time,
_fieldOffsets[_activeButton],
SPEED_FASTEST
); );
} }
} }
@ -430,6 +484,17 @@ void DateEntryScreen::update(Context &ctx) {
ProgressScreen::ProgressScreen(void) ProgressScreen::ProgressScreen(void)
: ModalScreen(MODAL_WIDTH, MODAL_HEIGHT_REDUCED) {} : ModalScreen(MODAL_WIDTH, MODAL_HEIGHT_REDUCED) {}
void ProgressScreen::_setProgress(Context &ctx, int part, int total) {
if (!total)
total = 1;
int totalWidth = _width - MODAL_PADDING * 2;
int partWidth = (totalWidth * part) / total;
if (_progressBarAnim.getTargetValue() != partWidth)
_progressBarAnim.setValue(ctx.time, partWidth, SPEED_FASTEST);
}
void ProgressScreen::show(Context &ctx, bool goBack) { void ProgressScreen::show(Context &ctx, bool goBack) {
ModalScreen::show(ctx, goBack); ModalScreen::show(ctx, goBack);
@ -445,17 +510,25 @@ void ProgressScreen::draw(Context &ctx, bool active) const {
int fullBarWidth = _width - MODAL_PADDING * 2; int fullBarWidth = _width - MODAL_PADDING * 2;
int barX = (_width - fullBarWidth) / 2; int barX = (_width - fullBarWidth) / 2;
int barY = TITLE_BAR_HEIGHT + _height - int barY =
(PROGRESS_BAR_HEIGHT + MODAL_PADDING); TITLE_BAR_HEIGHT + _height - (PROGRESS_BAR_HEIGHT + MODAL_PADDING);
_setBlendMode(ctx, GP0_BLEND_SEMITRANS, true); _setBlendMode(ctx, GP0_BLEND_SEMITRANS, true);
ctx.gpuCtx.drawRect( ctx.gpuCtx.drawRect(
barX, barY, fullBarWidth, PROGRESS_BAR_HEIGHT, ctx.colors[COLOR_WINDOW3] barX,
barY,
fullBarWidth,
PROGRESS_BAR_HEIGHT,
ctx.colors[COLOR_WINDOW3]
); );
ctx.gpuCtx.drawGradientRectH( ctx.gpuCtx.drawGradientRectH(
barX, barY, _progressBarAnim.getValue(ctx.time), PROGRESS_BAR_HEIGHT, barX,
ctx.colors[COLOR_PROGRESS2], ctx.colors[COLOR_PROGRESS1] barY,
_progressBarAnim.getValue(ctx.time),
PROGRESS_BAR_HEIGHT,
ctx.colors[COLOR_PROGRESS2],
ctx.colors[COLOR_PROGRESS1]
); );
} }

View File

@ -86,16 +86,7 @@ private:
util::Tween<int, util::QuadOutEasing> _progressBarAnim; util::Tween<int, util::QuadOutEasing> _progressBarAnim;
protected: protected:
inline void _setProgress(Context &ctx, int part, int total) { void _setProgress(Context &ctx, int part, int total);
if (!total)
total = 1;
int totalWidth = _width - MODAL_PADDING * 2;
int partWidth = (totalWidth * part) / total;
if (_progressBarAnim.getTargetValue() != partWidth)
_progressBarAnim.setValue(ctx.time, partWidth, SPEED_FASTEST);
}
public: public:
ProgressScreen(void); ProgressScreen(void);