spicetools/overlay/windows/iidx_seg.cpp

149 lines
5.3 KiB
C++

#include <map>
#include "iidx_seg.h"
#include "games/io.h"
#include "games/iidx/iidx.h"
#include "util/logging.h"
namespace overlay::windows {
uint32_t IIDX_SEGMENT_FONT_SIZE = 64;
std::optional<uint32_t> IIDX_SEGMENT_FONT_COLOR = std::nullopt;
std::string IIDX_SEGMENT_LOCATION = "bottom";
static const size_t TICKER_SIZE = 9;
static const ImVec4 DARK_GRAY(0.1f, 0.1f, 0.1f, 1.f);
static const ImVec4 RED(1.f, 0.f, 0.f, 1.f);
static const int PADDING_Y = 8;
static const int PADDING_X = 4;
static const std::map<char, std::pair<char, char>> CHARMAP = {\
// period - add a space afterwards (game sends 'm' for period)
{'m', {'.', ' '}},
// exclamation mark (use ./ to make it look like one)
{'!', {'.', '/'}},
// font doesn't have tilde so using a dash instead
{'~', {'-', '\0'}},
};
IIDXSegmentDisplay::IIDXSegmentDisplay(SpiceOverlay *overlay) : Window(overlay) {
if (!DSEG_FONT) {
log_fatal("iidx_seg", "DSEG_FONT is null");
}
this->title = "IIDX LED Segment Display";
this->toggle_button = games::OverlayButtons::ToggleSubScreen;
this->remove_window_padding = true;
this->size_max = ImVec2(ImGui::GetIO().DisplaySize.x, ImGui::GetIO().DisplaySize.y);
this->flags = ImGuiWindowFlags_NoTitleBar
| ImGuiWindowFlags_NoScrollbar
| ImGuiWindowFlags_NoResize
| ImGuiWindowFlags_NoNavFocus
| ImGuiWindowFlags_NoNavInputs
| ImGuiWindowFlags_NoDocking;
if (IIDX_SEGMENT_FONT_COLOR.has_value()) {
const auto rgb = IIDX_SEGMENT_FONT_COLOR.value();
const auto rgba = IM_COL32_A_MASK | rgb;
this->color = ImGui::ColorConvertU32ToFloat4(rgba);
} else {
this->color = RED;
}
}
void IIDXSegmentDisplay::calculate_initial_window() {
// ImGui::CalcTextSize doesn't seem to work here, so manually calculate
ImGui::PushFont(DSEG_FONT);
this->init_size = ImGui::CalcTextSize("~.~.~.~.~.~.~.~.~.");
this->init_size.x += PADDING_X * 2;
this->init_size.y += PADDING_Y * 2;
ImGui::PopFont();
// initial horizontal position
if (IIDX_SEGMENT_LOCATION.find("left") != std::string::npos) {
this->init_pos.x = 0;
} else if (IIDX_SEGMENT_LOCATION.find("right") != std::string::npos) {
this->init_pos.x = ImGui::GetIO().DisplaySize.x - this->init_size.x;
} else {
// center
this->init_pos.x = ImGui::GetIO().DisplaySize.x / 2 - this->init_size.x / 2;
}
// initial vertical position
if (IIDX_SEGMENT_LOCATION.rfind("top", 0) == 0) {
this->init_pos.y = 0;
} else {
// bottom
this->init_pos.y = ImGui::GetIO().DisplaySize.y - this->init_size.y;
}
}
void IIDXSegmentDisplay::build_content() {
char input_ticker[TICKER_SIZE];
// get ticker content from game
games::iidx::IIDX_LED_TICKER_LOCK.lock();
memcpy(input_ticker, games::iidx::IIDXIO_LED_TICKER, TICKER_SIZE);
games::iidx::IIDX_LED_TICKER_LOCK.unlock();
// since every input character can result in up to two characters,
// need double size for output
const size_t TICKER_OUTPUT_SIZE = TICKER_SIZE * 2 + 1;
char output_ticker[TICKER_OUTPUT_SIZE];
int output_ticker_index = 0;
// look at each input char and convert into output char(s)
for (const auto c : input_ticker) {
// see if there is an applicable rule for this input character
if (0 == CHARMAP.count(c)) {
// if not, copy the first character and keep going
output_ticker[output_ticker_index] = c;
output_ticker_index += 1;
continue;
}
// there is a replacement rule for this input character
auto replacements = CHARMAP.at(c);
// replace the first character...
output_ticker[output_ticker_index] = replacements.first;
output_ticker_index += 1;
// and optionally add the second character
if (replacements.second != '\0') {
output_ticker[output_ticker_index] = replacements.second;
output_ticker_index += 1;
}
}
if ((int)TICKER_OUTPUT_SIZE <= output_ticker_index) {
log_fatal("iidx_seg", "{} is beyond array bounds", output_ticker_index);
}
// terminating null...
output_ticker[output_ticker_index] = '\0';
// finally, draw UI elements
draw_ticker(output_ticker);
}
void IIDXSegmentDisplay::draw_ticker(char *ticker_string) {
ImGui::PushFont(DSEG_FONT);
const auto pos = ImVec2(PADDING_X, PADDING_Y);
// to imitate LED "off"
ImGui::SetCursorPos(pos);
ImGui::PushStyleColor(ImGuiCol_Text, DARK_GRAY);
ImGui::TextUnformatted("~.~.~.~.~.~.~.~.~.");
ImGui::PopStyleColor();
// ... then draw the LED "on" above it
ImGui::SetCursorPos(pos);
ImGui::PushStyleColor(ImGuiCol_Text, this->color);
ImGui::TextUnformatted(ticker_string);
ImGui::PopStyleColor();
ImGui::PopFont();
}
}