21 panel_sprite =
nullptr;
22 cursor_sprite =
nullptr;
23 scroll_track_sprite =
nullptr;
24 scroll_thumb_sprite =
nullptr;
28 fade_start_alpha = 0.0f;
29 fade_target_alpha = 0.0f;
36 scrollbar_dragging =
false;
37 scrollbar_drag_offset = 0;
41 console_font.reset(fontPath, fontSize);
43 refreshVisibleLineCount();
47 commandCallback = std::move(callback);
51 if (!prompt.empty()) {
59 fade_start_alpha = fade_alpha;
60 fade_target_alpha = 1.0f;
61 fade_start_ns = SDL_GetTicksNS();
63 if (windowPtr !=
nullptr) {
64 SDL_StartTextInput(windowPtr->getSDLWindow());
70 fade_start_alpha = fade_alpha;
71 fade_target_alpha = 0.0f;
72 fade_start_ns = SDL_GetTicksNS();
74 if (windowPtr !=
nullptr) {
75 SDL_StopTextInput(windowPtr->getSDLWindow());
97 return visible || fade_alpha > 0.0f || fade_active;
101 max_lines = std::max<std::size_t>(1, maxLines);
102 trimOutputToLimits();
103 invalidateWrappedCache();
104 scroll_offset = std::min(scroll_offset, maxScrollOffset());
108 max_visible_lines = std::max<std::size_t>(1, maxVisibleLines);
112 invalidateWrappedCache();
113 scroll_offset = std::min(scroll_offset, maxScrollOffset());
121 std::size_t start = 0;
122 while (start <= line.size()) {
123 const std::size_t nl = line.find(
'\n', start);
124 if (nl == std::string::npos) {
125 pushOutputLine(line.substr(start), color);
129 pushOutputLine(line.substr(start, nl - start), color);
133 if (start == line.size()) {
134 pushOutputLine(std::string{}, color);
139 trimOutputToLimits();
140 invalidateWrappedCache();
144 scroll_offset = std::min(scroll_offset, maxScrollOffset());
149 total_line_chars = 0;
151 invalidateWrappedCache();
159 void VK_Console::pushOutputLine(std::string line) {
160 pushOutputLine(std::move(line), text_color);
163 void VK_Console::pushOutputLine(std::string line,
const SDL_Color color) {
164 if (max_total_chars > 0 && line.size() > max_total_chars) {
165 line = line.substr(line.size() - max_total_chars);
168 total_line_chars += line.size();
169 lines.push_back(OutputLine{std::move(line), color});
170 wrapped_cache_dirty =
true;
173 void VK_Console::trimOutputToLimits() {
174 while (!lines.empty() && (lines.size() > max_lines || total_line_chars > max_total_chars)) {
175 total_line_chars -= lines.front().text.size();
178 wrapped_cache_dirty =
true;
181 std::size_t VK_Console::effectiveVisibleLineCount() const noexcept {
182 return std::max<std::size_t>(1, std::min(max_visible_lines, last_visible_line_count));
185 std::size_t VK_Console::maxScrollOffset() const noexcept {
186 const int width = usableTextWidth();
187 ensureWrappedCache(width);
188 const std::size_t total_rows = wrapped_cache_line_count;
189 const std::size_t visible_rows = effectiveVisibleLineCount();
190 if (total_rows <= visible_rows) {
193 return total_rows - visible_rows;
196 int VK_Console::measureTextWidth(
const std::string &text)
const {
197 if (windowPtr ==
nullptr || text.empty()) {
203 if (!windowPtr->getTextDimensions(text, width, height, console_font)) {
209 int VK_Console::usableTextWidth()
const {
210 constexpr int padding = 10;
211 const int reserved_scrollbar = (scrollbar_h > 0) ? (scrollbar_w + padding) : 0;
212 return std::max(1, panel_w - (padding * 2) - reserved_scrollbar);
215 void VK_Console::invalidateWrappedCache() {
216 wrapped_cache_dirty =
true;
217 wrapped_cache_width = -1;
220 void VK_Console::ensureWrappedCache(
const int maxWidth)
const {
221 if (!wrapped_cache_dirty && wrapped_cache_width == maxWidth) {
225 wrapped_cache.clear();
226 wrapped_cache_line_count = 0;
227 wrapped_cache_width = maxWidth;
228 wrapped_cache_dirty =
false;
230 for (
const OutputLine &line : lines) {
231 const std::vector<std::string> rows = wrapTextToWidth(line.text, maxWidth);
232 wrapped_cache_line_count += rows.size();
233 for (
const std::string &row : rows) {
234 wrapped_cache.push_back(OutputLine{row, line.color});
239 std::vector<std::string> VK_Console::wrapTextToWidth(
const std::string &text,
const int maxWidth)
const {
240 std::vector<std::string> wrapped;
242 wrapped.emplace_back();
247 wrapped.push_back(text);
251 auto appendChunk = [&](std::string chunk) {
252 if (!chunk.empty() || wrapped.empty()) {
253 wrapped.push_back(std::move(chunk));
258 std::size_t index = 0;
259 while (index < text.size()) {
260 const std::size_t tokenStart = index;
261 const bool isSpace = (text[index] ==
' ' || text[index] ==
'\t');
262 while (index < text.size() && ((text[index] ==
' ' || text[index] ==
'\t') == isSpace)) {
266 const std::string token = text.substr(tokenStart, index - tokenStart);
267 const std::string combined = current + token;
268 if (!current.empty() && measureTextWidth(combined) > maxWidth) {
269 appendChunk(std::move(current));
273 if (measureTextWidth(current + token) <= maxWidth) {
278 if (!current.empty()) {
279 appendChunk(std::move(current));
283 std::string fragment;
284 for (
char ch : token) {
285 const std::string candidate = fragment + ch;
286 if (!fragment.empty() && measureTextWidth(candidate) > maxWidth) {
287 appendChunk(std::move(fragment));
290 fragment.push_back(ch);
291 if (measureTextWidth(fragment) > maxWidth) {
292 const char last = fragment.back();
294 appendChunk(std::move(fragment));
296 fragment.push_back(last);
302 appendChunk(std::move(current));
306 void VK_Console::scrollUp(
const std::size_t amount) {
311 const std::size_t max_offset = maxScrollOffset();
312 if (max_offset == 0) {
317 scroll_offset = std::min(max_offset, scroll_offset + amount);
320 void VK_Console::scrollDown(
const std::size_t amount) {
325 if (scroll_offset <= amount) {
331 scroll_offset -= amount;
334 std::vector<std::string> VK_Console::tokenize(
const std::string &line) {
335 std::istringstream stream(line);
336 std::vector<std::string> tokens;
338 while (stream >> token) {
339 tokens.push_back(token);
344 bool VK_Console::handleDefaultCommand(
const std::vector<std::string> &args, std::ostream &output) {
349 const std::string &cmd = args.front();
350 if (cmd ==
"clear") {
357 void VK_Console::submitInput() {
358 if (input.empty() || windowPtr ==
nullptr) {
366 history.push_back(input);
369 std::ostringstream reply;
370 const std::vector<std::string> args = tokenize(input);
372 bool handled = handleDefaultCommand(args, reply);
373 if (!handled && commandCallback) {
374 handled = commandCallback(*windowPtr, args, reply);
376 if (!handled && !args.empty()) {
377 reply << std::format(
"Unknown command: {}", args.front());
380 const std::string out = reply.str();
389 void VK_Console::historyUp() {
390 if (history.empty()) {
394 if (history_index < 0) {
395 history_index =
static_cast<int>(history.size()) - 1;
396 }
else if (history_index > 0) {
400 if (history_index >= 0 && history_index <
static_cast<int>(history.size())) {
401 input = history[
static_cast<std::size_t
>(history_index)];
402 cursor_pos = input.size();
406 void VK_Console::historyDown() {
407 if (history.empty()) {
411 if (history_index < 0) {
415 if (history_index <
static_cast<int>(history.size()) - 1) {
417 input = history[
static_cast<std::size_t
>(history_index)];
418 cursor_pos = input.size();
426 void VK_Console::ensurePanelSprite() {
427 if (panel_sprite !=
nullptr || windowPtr ==
nullptr) {
431 SDL_Surface *surface = SDL_CreateSurface(2, 2, SDL_PIXELFORMAT_RGBA32);
432 if (surface ==
nullptr) {
436 const SDL_PixelFormatDetails *
const fmt = SDL_GetPixelFormatDetails(SDL_PIXELFORMAT_RGBA32);
437 SDL_FillSurfaceRect(surface,
nullptr, SDL_MapRGBA(fmt,
nullptr, 0, 0, 0, 170));
439 panel_sprite = windowPtr->createSprite(surface);
441 panel_sprite =
nullptr;
444 SDL_DestroySurface(surface);
447 void VK_Console::ensureCursorSprite() {
448 if (cursor_sprite !=
nullptr || windowPtr ==
nullptr) {
452 SDL_Surface *surface = SDL_CreateSurface(2, 2, SDL_PIXELFORMAT_RGBA32);
453 if (surface ==
nullptr) {
457 const SDL_PixelFormatDetails *
const fmt = SDL_GetPixelFormatDetails(SDL_PIXELFORMAT_RGBA32);
458 SDL_FillSurfaceRect(surface,
nullptr, SDL_MapRGBA(fmt,
nullptr, 210, 255, 220, 255));
460 cursor_sprite = windowPtr->createSprite(surface);
462 cursor_sprite =
nullptr;
465 SDL_DestroySurface(surface);
468 void VK_Console::ensureScrollSprites() {
469 if (windowPtr ==
nullptr) {
473 if (scroll_track_sprite ==
nullptr) {
474 SDL_Surface *trackSurface = SDL_CreateSurface(2, 2, SDL_PIXELFORMAT_RGBA32);
475 if (trackSurface !=
nullptr) {
476 const SDL_PixelFormatDetails *
const fmt = SDL_GetPixelFormatDetails(SDL_PIXELFORMAT_RGBA32);
477 SDL_FillSurfaceRect(trackSurface,
nullptr, SDL_MapRGBA(fmt,
nullptr, 255, 255, 255, 80));
479 scroll_track_sprite = windowPtr->createSprite(trackSurface);
481 scroll_track_sprite =
nullptr;
483 SDL_DestroySurface(trackSurface);
487 if (scroll_thumb_sprite ==
nullptr) {
488 SDL_Surface *thumbSurface = SDL_CreateSurface(2, 2, SDL_PIXELFORMAT_RGBA32);
489 if (thumbSurface !=
nullptr) {
490 const SDL_PixelFormatDetails *
const fmt = SDL_GetPixelFormatDetails(SDL_PIXELFORMAT_RGBA32);
491 SDL_FillSurfaceRect(thumbSurface,
nullptr, SDL_MapRGBA(fmt,
nullptr, 190, 225, 255, 180));
493 scroll_thumb_sprite = windowPtr->createSprite(thumbSurface);
495 scroll_thumb_sprite =
nullptr;
497 SDL_DestroySurface(thumbSurface);
502 void VK_Console::updatePanelLayout() {
503 if (windowPtr ==
nullptr) {
507 const VkExtent2D extent = windowPtr->getSwapchainExtent();
508 const int screen_w =
static_cast<int>(extent.width);
509 const int screen_h =
static_cast<int>(extent.height);
510 if (screen_w <= 0 || screen_h <= 0) {
514 constexpr int horizontal_margin = 10;
515 constexpr int top_margin = 8;
516 constexpr int bottom_margin = 24;
518 panel_x = horizontal_margin;
519 panel_y = top_margin;
520 panel_w = std::max(300, screen_w - (horizontal_margin * 2));
521 panel_h = std::clamp((screen_h * 42) / 100, 180, std::max(180, screen_h - bottom_margin));
524 void VK_Console::refreshVisibleLineCount() {
525 if (windowPtr ==
nullptr) {
533 if (!windowPtr->getTextDimensions(
"M", glyph_w, glyph_h, console_font)) {
537 const int line_height = std::max(1, glyph_h + 2);
538 const int padding = 10;
539 const int title_y = panel_y + padding;
540 const int first_line_y = title_y + line_height;
541 const int input_y = panel_y + panel_h - padding - line_height;
542 const int available_height = std::max(line_height, input_y - first_line_y - 6);
544 content_top_y = first_line_y;
545 content_bottom_y = first_line_y + available_height;
546 last_visible_line_count = std::max<std::size_t>(1,
static_cast<std::size_t
>(available_height / line_height));
551 const std::size_t max_offset = maxScrollOffset();
552 scroll_offset = std::min(scroll_offset, max_offset);
553 updateScrollbarGeometry();
556 void VK_Console::updateScrollbarGeometry() {
557 const int padding = 10;
559 scrollbar_x = panel_x + panel_w - padding - scrollbar_w;
560 scrollbar_y = content_top_y;
561 scrollbar_h = std::max(0, content_bottom_y - content_top_y);
563 if (scrollbar_h <= 0) {
564 scrollbar_thumb_h = 0;
565 scrollbar_thumb_y = scrollbar_y;
569 ensureWrappedCache(usableTextWidth());
570 const std::size_t total_lines = wrapped_cache_line_count;
571 const std::size_t visible_lines = effectiveVisibleLineCount();
573 if (total_lines <= visible_lines) {
574 scrollbar_thumb_h = scrollbar_h;
575 scrollbar_thumb_y = scrollbar_y;
579 const std::size_t max_offset = maxScrollOffset();
580 const double visible_ratio =
static_cast<double>(visible_lines) /
static_cast<double>(total_lines);
581 scrollbar_thumb_h = std::clamp(
static_cast<int>(std::lround(
static_cast<double>(scrollbar_h) * visible_ratio)), 16, scrollbar_h);
583 const int travel = std::max(0, scrollbar_h - scrollbar_thumb_h);
584 if (travel == 0 || max_offset == 0) {
585 scrollbar_thumb_y = scrollbar_y;
589 const double ratio_top =
static_cast<double>(scroll_offset) /
static_cast<double>(max_offset);
590 const int offset_px =
static_cast<int>(std::lround((1.0 - ratio_top) *
static_cast<double>(travel)));
591 scrollbar_thumb_y = scrollbar_y + std::clamp(offset_px, 0, travel);
594 bool VK_Console::isPointInScrollbar(
const int x,
const int y)
const noexcept {
595 return scrollbar_h > 0 && x >= scrollbar_x && x < (scrollbar_x + scrollbar_w) && y >= scrollbar_y && y < (scrollbar_y + scrollbar_h);
598 bool VK_Console::isPointInScrollbarThumb(
const int x,
const int y)
const noexcept {
599 return scrollbar_thumb_h > 0 && x >= scrollbar_x && x < (scrollbar_x + scrollbar_w) && y >= scrollbar_thumb_y && y < (scrollbar_thumb_y + scrollbar_thumb_h);
602 void VK_Console::updateScrollFromThumbY(
const int thumbY) {
603 const std::size_t max_offset = maxScrollOffset();
604 if (max_offset == 0) {
610 const int travel = std::max(0, scrollbar_h - scrollbar_thumb_h);
617 const int clamped_thumb_y = std::clamp(thumbY, scrollbar_y, scrollbar_y + travel);
618 scrollbar_thumb_y = clamped_thumb_y;
619 const double ratio_bottom_to_top =
static_cast<double>(clamped_thumb_y - scrollbar_y) /
static_cast<double>(travel);
620 const double ratio_top = 1.0 - ratio_bottom_to_top;
621 scroll_offset =
static_cast<std::size_t
>(std::lround(ratio_top *
static_cast<double>(max_offset)));
622 scroll_offset = std::min(scroll_offset, max_offset);
623 follow_tail = (scroll_offset == 0);
627 if (event.type == SDL_EVENT_KEY_DOWN && (event.key.key == SDLK_F3)) {
636 refreshVisibleLineCount();
638 if (event.type == SDL_EVENT_MOUSE_WHEEL) {
639 if (event.wheel.y > 0) {
640 scrollUp(
static_cast<std::size_t
>(event.wheel.y));
641 }
else if (event.wheel.y < 0) {
642 scrollDown(
static_cast<std::size_t
>(-event.wheel.y));
647 if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN && event.button.button == SDL_BUTTON_LEFT) {
648 const int mx =
event.button.x;
649 const int my =
event.button.y;
650 if (isPointInScrollbarThumb(
mx, my)) {
651 scrollbar_dragging =
true;
652 scrollbar_drag_offset = my - scrollbar_thumb_y;
656 if (isPointInScrollbar(
mx, my)) {
657 const std::size_t page = std::max<std::size_t>(1, effectiveVisibleLineCount() - 1);
658 if (my < scrollbar_thumb_y) {
660 }
else if (my >= (scrollbar_thumb_y + scrollbar_thumb_h)) {
667 if (event.type == SDL_EVENT_MOUSE_BUTTON_UP && event.button.button == SDL_BUTTON_LEFT) {
668 scrollbar_dragging =
false;
672 if (event.type == SDL_EVENT_MOUSE_MOTION && scrollbar_dragging) {
673 updateScrollFromThumbY(event.motion.y - scrollbar_drag_offset);
677 if (event.type == SDL_EVENT_KEY_DOWN) {
678 switch (event.key.key) {
684 if (cursor_pos > 0 && cursor_pos <= input.size()) {
685 input.erase(cursor_pos - 1, 1);
690 if (cursor_pos < input.size()) {
691 input.erase(cursor_pos, 1);
695 if (cursor_pos > 0) {
700 if (cursor_pos < input.size()) {
710 cursor_pos = input.size();
719 scrollUp(std::max<std::size_t>(1, effectiveVisibleLineCount() - 1));
722 scrollDown(std::max<std::size_t>(1, effectiveVisibleLineCount() - 1));
733 if (event.type == SDL_EVENT_TEXT_INPUT) {
734 if (event.text.text[0] !=
'\0') {
735 input.insert(cursor_pos, event.text.text);
736 cursor_pos += SDL_strlen(event.text.text);
742 if (windowPtr ==
nullptr) {
747 const Uint64 now = SDL_GetTicksNS();
748 const double elapsed =
static_cast<double>(now - fade_start_ns);
749 const double duration =
static_cast<double>(fade_duration_ns);
750 const float t =
static_cast<float>(std::clamp(elapsed / std::max(1.0, duration), 0.0, 1.0));
751 fade_alpha = fade_start_alpha + ((fade_target_alpha - fade_start_alpha) * t);
753 fade_alpha = fade_target_alpha;
758 if (!(visible || fade_alpha > 0.0f)) {
762 const float alpha = std::clamp(fade_alpha, 0.0f, 1.0f);
767 const auto scaledColor = [alpha](
const SDL_Color &src) {
769 const int scaled =
static_cast<int>(std::lround(
static_cast<double>(src.a) * alpha));
770 out.a =
static_cast<Uint8
>(std::clamp(scaled, 0, 255));
776 ensureCursorSprite();
777 ensureScrollSprites();
778 const auto consoleSpriteY = [
this](
const int y,
const int h) {
779 if (sprite_y_origin_top_left) {
780 return std::max(0, y);
783 const int screen_h =
static_cast<int>(windowPtr->getSwapchainExtent().height);
784 return std::max(0, screen_h - y - h);
787 if (panel_sprite !=
nullptr) {
788 SDL_Surface *
surface = SDL_CreateSurface(2, 2, SDL_PIXELFORMAT_RGBA32);
790 const SDL_PixelFormatDetails *
const fmt = SDL_GetPixelFormatDetails(SDL_PIXELFORMAT_RGBA32);
791 const int panel_alpha =
static_cast<int>(std::lround(170.0 * alpha));
792 SDL_FillSurfaceRect(
surface,
nullptr, SDL_MapRGBA(fmt,
nullptr, 0, 0, 0,
static_cast<Uint8
>(std::clamp(panel_alpha, 0, 255))));
793 panel_sprite->updateTexture(
surface);
797 panel_sprite->drawSpriteRect(panel_x, consoleSpriteY(panel_y, panel_h), panel_w, panel_h);
802 if (!windowPtr->getTextDimensions(
"M", glyph_w, glyph_h, console_font)) {
806 const int line_height = std::max(1, glyph_h + 2);
807 const int padding = 10;
808 const int text_width = usableTextWidth();
809 ensureWrappedCache(text_width);
811 int y = panel_y + padding;
812 windowPtr->printText(
"MXVK Console (F3 to toggle)", panel_x + padding, y, scaledColor(info_color), console_font);
815 const int input_y = panel_y + panel_h - padding - line_height;
816 const int available_height = std::max(line_height, input_y - y - 6);
818 content_bottom_y = y + available_height;
819 last_visible_line_count = std::max<std::size_t>(1,
static_cast<std::size_t
>(available_height / line_height));
824 const std::size_t max_offset = maxScrollOffset();
825 scroll_offset = std::min(scroll_offset, max_offset);
826 updateScrollbarGeometry();
828 const std::size_t visible_lines = std::min(effectiveVisibleLineCount(), wrapped_cache.size());
829 const std::size_t start = (wrapped_cache.size() > visible_lines) ? (wrapped_cache.size() - visible_lines - scroll_offset) : 0;
830 const std::size_t end = std::min(wrapped_cache.size(), start + visible_lines);
831 for (std::size_t i = start; i < end; ++i) {
832 windowPtr->printText(wrapped_cache[i].text, panel_x + padding, y, scaledColor(wrapped_cache[i].color), console_font);
836 if (scroll_track_sprite !=
nullptr && scrollbar_h > 0) {
837 SDL_Surface *
surface = SDL_CreateSurface(2, 2, SDL_PIXELFORMAT_RGBA32);
839 const SDL_PixelFormatDetails *
const fmt = SDL_GetPixelFormatDetails(SDL_PIXELFORMAT_RGBA32);
840 const int track_alpha =
static_cast<int>(std::lround(96.0 * alpha));
841 SDL_FillSurfaceRect(
surface,
nullptr, SDL_MapRGBA(fmt,
nullptr, 255, 255, 255,
static_cast<Uint8
>(std::clamp(track_alpha, 0, 255))));
842 scroll_track_sprite->updateTexture(
surface);
846 scroll_track_sprite->drawSpriteRect(scrollbar_x, consoleSpriteY(scrollbar_y, scrollbar_h), scrollbar_w, scrollbar_h);
849 if (scroll_thumb_sprite !=
nullptr && scrollbar_thumb_h > 0) {
850 SDL_Surface *
surface = SDL_CreateSurface(2, 2, SDL_PIXELFORMAT_RGBA32);
852 const SDL_PixelFormatDetails *
const fmt = SDL_GetPixelFormatDetails(SDL_PIXELFORMAT_RGBA32);
853 const int base_alpha = scrollbar_dragging ? 240 : 190;
854 const int thumb_alpha =
static_cast<int>(std::lround(
static_cast<double>(base_alpha) * alpha));
855 SDL_FillSurfaceRect(
surface,
nullptr, SDL_MapRGBA(fmt,
nullptr, 190, 225, 255,
static_cast<Uint8
>(std::clamp(thumb_alpha, 0, 255))));
856 scroll_thumb_sprite->updateTexture(
surface);
860 scroll_thumb_sprite->drawSpriteRect(scrollbar_x,
861 consoleSpriteY(scrollbar_thumb_y, scrollbar_thumb_h),
866 if (scroll_offset > 0) {
867 windowPtr->printText(std::format(
"^ {} line(s) newer below", scroll_offset),
869 input_y - line_height,
870 scaledColor(info_color),
874 const Uint64 now = SDL_GetTicksNS();
875 if (now - last_cursor_toggle_ns > 500000000ULL) {
876 cursor_visible = !cursor_visible;
877 last_cursor_toggle_ns = now;
880 cursor_pos = std::min(cursor_pos, input.size());
882 const std::string line = promptText + input;
883 windowPtr->printText(line, panel_x + padding, input_y, scaledColor(prompt_color), console_font);
885 if (cursor_visible && cursor_sprite !=
nullptr) {
886 int prefix_width = 0;
887 int prefix_height = 0;
888 const std::string prefix = promptText + input.substr(0, cursor_pos);
889 if (!windowPtr->getTextDimensions(prefix, prefix_width, prefix_height, console_font)) {
890 prefix_width =
static_cast<int>((promptText.size() + cursor_pos) *
static_cast<std::size_t
>(glyph_w));
894 const int cursor_w = 2;
895 const int cursor_x = std::max(panel_x + padding, panel_x + padding + prefix_width - 1);
896 const int cursor_y = input_y + 2;
897 const int cursor_h = std::max(4, line_height - 4);
899 SDL_Surface *
surface = SDL_CreateSurface(2, 2, SDL_PIXELFORMAT_RGBA32);
901 const SDL_PixelFormatDetails *
const fmt = SDL_GetPixelFormatDetails(SDL_PIXELFORMAT_RGBA32);
902 const int cursor_alpha =
static_cast<int>(std::lround(220.0 * alpha));
903 SDL_FillSurfaceRect(
surface,
nullptr, SDL_MapRGBA(fmt,
nullptr, 210, 255, 220,
static_cast<Uint8
>(std::clamp(cursor_alpha, 0, 255))));
904 cursor_sprite->updateTexture(
surface);
908 cursor_sprite->drawSpriteRect(cursor_x, consoleSpriteY(cursor_y, cursor_h), cursor_w, cursor_h);
void setVisible(bool value) noexcept
Show or hide the console.
std::function< bool(VK_Window &window, const std::vector< std::string > &args, std::ostream &output)> CommandCallback
void draw()
Draw the current console overlay.
void invalidateLayoutCache()
Recompute cached line wrapping after font or layout metrics change.
const std::string & inputBuffer() const noexcept
void toggle() noexcept
Toggle console visibility.
void handleEvent(const SDL_Event &event)
Handle keyboard/text events for the console.
bool isVisible() const noexcept
void hide() noexcept
Hide console.
void setFont(const std::string &fontPath, int fontSize)
Change only the console font, leaving the window default font unchanged.
void setPrompt(const std::string &prompt)
Set the input prompt text.
void show() noexcept
Show console.
void clear()
Clear all buffered output lines.
void setMaxVisibleLines(std::size_t maxVisibleLines)
Set max number of lines rendered on screen.
void setCommandCallback(CommandCallback callback)
Register a command callback.
void printLine(const std::string &line, SDL_Color color=SDL_Color{255, 255, 255, 255})
Append one line to console output.
void attach(VK_Window &window, const std::string &fontPath, int fontSize=18)
Attach this console to a window and configure console font rendering.
void setMaxLines(std::size_t maxLines)
Set max number of retained output lines.
Main Vulkan window wrapper for MXVK.
In-game command console for MXVK / Vulkan windows.
Vulkan 2-D sprite renderer with optional custom shaders and instancing.
Utilities for loading and saving PNG images.