libmx2 2.2.1
C++20 multimedia and game-engine library (SDL2 / OpenGL / Vulkan)
Loading...
Searching...
No Matches
console.cpp
Go to the documentation of this file.
1/**
2 * @file console.cpp
3 * @brief Implementation of console::Console, console::GLConsole, and console::ConsoleChars.
4 */
5#include"console.hpp"
6#ifdef WITH_GL
7#include"gl.hpp"
8#endif
9
10namespace console {
11
12 SDL_Surface* flipSurface(SDL_Surface* surface) {
13 SDL_LockSurface(surface);
14 Uint8* pixels = (Uint8*)surface->pixels;
15 int pitch = surface->pitch;
16 Uint8* tempRow = new Uint8[pitch];
17 for (int y = 0; y < surface->h / 2; ++y) {
18 Uint8* row1 = pixels + y * pitch;
19 Uint8* row2 = pixels + (surface->h - y - 1) * pitch;
20 memcpy(tempRow, row1, pitch);
21 memcpy(row1, row2, pitch);
22 memcpy(row2, tempRow, pitch);
23 }
24
25 delete[] tempRow;
26 SDL_UnlockSurface(surface);
27 return surface;
28 }
29
31 font = std::make_unique<mx::Font>();
32 if (!font) {
33 std::cerr << "Failed to create font object" << std::endl;
34 }
35 }
36
40
41 void ConsoleChars::load(const std::string &fnt, int size, const SDL_Color &col) {
42 clear();
43 font.reset(new mx::Font(fnt, size));
44 if(font) {
45 std::string chars = " 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_+[]{}|;:,.<>?`~\"\'/\\-=";
46 for (char c : chars) {
47 SDL_Surface *surface = TTF_RenderGlyph_Solid(font->unwrap(), c, col);
48 if (surface) {
49 characters[c] = surface;
50 } else {
51 std::cerr << "Failed to load character: " << c << std::endl;
52 }
53 }
54 }
55 }
56
58 if(!characters.empty()) {
59 for(auto &p : characters) {
60 if(p.second != nullptr) {
61 SDL_FreeSurface(p.second);
62 p.second = nullptr;
63 }
64 }
65 characters.clear();
66 font.reset();
67 }
68 }
69
70 void CommandQueue::push(const Command& cmd) {
71#if THREAD_ENABLED
72 std::lock_guard<std::mutex> lock(queue_mutex);
73 queue.push(cmd);
74 cv.notify_one();
75#else
76 queue.push(cmd);
77#endif
78 }
79
81#if THREAD_ENABLED
82 std::unique_lock<std::mutex> lock(queue_mutex);
83 cv.wait_for(lock, std::chrono::milliseconds(100),
84 [this] { return !queue.empty() || !active; });
85
86 if (!active && queue.empty())
87 return false;
88#else
89 if (queue.empty())
90 return false;
91#endif
92
93 if (!queue.empty()) {
94 cmd = queue.front();
95 queue.pop();
96 return true;
97 }
98 return false;
99 }
100
102#if THREAD_ENABLED
103 {
104 std::lock_guard<std::mutex> lock(queue_mutex);
105 active = false;
106 }
107 cv.notify_all();
108#else
109 active = false;
110#endif
111 }
112
114 inMultilineMode = false;
115 braceCount = 0;
116 multilineBuffer = "";
117 originalPrompt = "$ ";
118 worker_active = true;
119
120#if THREAD_ENABLED
121 worker_thread = std::make_unique<std::thread>(&Console::worker_thread_func, this);
122#endif
123 }
124
126 worker_active = false;
127 command_queue.shutdown();
128
129#if THREAD_ENABLED
130 if (worker_thread && worker_thread->joinable()) {
131 worker_thread->join();
132 }
133#endif
134
135 if(surface) {
136 SDL_FreeSurface(surface);
137 surface = nullptr;
138 }
139 clear();
140 }
141
142 void Console::create(int x, int y, int w, int h) {
143 console_rect.x = x;
144 console_rect.y = y;
145 console_rect.w = w;
146 console_rect.h = h;
147
148 if(surface != nullptr) {
149 SDL_FreeSurface(surface);
150 surface = nullptr;
151 }
152
153 surface = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_RGBA32);
154 if (!surface) {
155 std::cerr << "Failed to create surface: " << SDL_GetError() << std::endl;
156 }
157 }
158
160 c_chars.load(font, font_size, color);
161 }
162
163 void Console::load(const std::string &fnt, int size, const SDL_Color &col) {
164 font = fnt;
165 font_size = size;
166 color = col;
167 c_chars.load(fnt, size, col);
169 }
170
172 c_chars.clear();
173 }
174
177 userScrolling = true;
178 int maxOffset = std::max(0, (int)lines.size() - visibleLineCount);
179 if (scrollOffset < maxOffset) {
180 ++scrollOffset;
181 needsRedraw = true;
182
183 }
184 }
185
188 userScrolling = true;
189 if (scrollOffset > 0) {
190 --scrollOffset;
191 needsRedraw = true;
192 }
193 }
194
196 if (scrollOffset != 0) {
197 scrollOffset = 0;
198 needsRedraw = true;
199 userScrolling = false;
200 }
201 }
202
203
204
205 void Console::print(const std::string &str) {
207 data << str;
208 cursorPos = data.str().length();
209 needsReflow = true;
210 needsRedraw = true;
212 }
213
214 void Console::setCallback(gl::GLWindow *window, std::function<bool(gl::GLWindow *win, const std::vector<std::string> &)> callback) {
215 this->callback = callback;
216 callbackSet = true;
217 }
218
220 if (c_chars.characters.empty()) return;
221
222 int maxWidth = console_rect.w - 50;
223 int promptWidth = 0;
224
225
226 for (char c : promptText) {
227 int charWidth = 0;
228 if (c == '\t') {
229 charWidth = c_chars.characters['A']->w * 4;
230 } else {
231 auto it = c_chars.characters.find(c);
232 charWidth = (it != c_chars.characters.end()) ?
233 it->second->w : c_chars.characters['A']->w;
234 }
235 promptWidth += charWidth;
236 }
237
238
239 int cursorPixelPos = promptWidth;
240 for (int i = inputScrollOffset; i < static_cast<int>(inputCursorPos); ++i) {
241 if (i >= 0 && i < (int)inputBuffer.size()) {
242 char c = inputBuffer[i];
243 int charWidth = 0;
244 if (c == '\t') {
245 charWidth = c_chars.characters['A']->w * 4;
246 } else {
247 auto it = c_chars.characters.find(c);
248 charWidth = (it != c_chars.characters.end()) ?
249 it->second->w : c_chars.characters['A']->w;
250 }
251 cursorPixelPos += charWidth;
252 }
253 }
254
255
256 while (cursorPixelPos > maxWidth && inputScrollOffset < (int)inputBuffer.size()) {
257 if (inputScrollOffset < (int)inputBuffer.size()) {
259 int charWidth = 0;
260 if (c == '\t') {
261 charWidth = c_chars.characters['A']->w * 4;
262 } else {
263 auto it = c_chars.characters.find(c);
264 charWidth = (it != c_chars.characters.end()) ?
265 it->second->w : c_chars.characters['A']->w;
266 }
267 cursorPixelPos -= charWidth;
269 } else {
270 break;
271 }
272 }
273
274 if (static_cast<int>(inputCursorPos) < inputScrollOffset) {
276 }
277 }
278
279 void Console::keypress(char c) {
281 needsRedraw = true;
282 try {
283 if (c == 8) {
284 if (!inputBuffer.empty() && inputCursorPos > 0 && inputCursorPos <= inputBuffer.size()) {
285 inputBuffer.erase(inputCursorPos - 1, 1);
287 if (inputScrollOffset > 0) {
289 }
290
291 if (static_cast<int>(inputCursorPos) < inputScrollOffset) {
293 }
294
296 }
297 } else if (c == 127) {
298 if (!inputBuffer.empty() && inputCursorPos < inputBuffer.size()) {
299 inputBuffer.erase(inputCursorPos, 1);
300
301 if (inputScrollOffset > static_cast<int>(inputBuffer.size())) {
302 inputScrollOffset = std::max(0, static_cast<int>(inputBuffer.size()));
303 }
304
306 }
307 } else if (c == 13) {
308
310
311 std::string cmd = inputBuffer;
312
313 if (inMultilineMode) {
314 multilineBuffer += cmd + "\n";
315 inputBuffer.clear();
316 inputCursorPos = 0;
317
318 for (char ch : cmd) {
319 if (ch == '{') braceCount++;
320 else if (ch == '}') braceCount--;
321 }
322
323 if (braceCount == 0) {
324 inMultilineMode = false;
326
327 std::string processedCommand = multilineBuffer;
328 size_t firstOpenBrace = processedCommand.find('{');
329 if (firstOpenBrace != std::string::npos) {
330 processedCommand.erase(firstOpenBrace, 1);
331 }
332 size_t lastCloseBrace = processedCommand.find_last_of('}');
333 if (lastCloseBrace != std::string::npos) {
334 processedCommand.erase(lastCloseBrace, 1);
335 }
337 procCmd(processedCommand);
338 multilineBuffer.clear();
339 scrollOffset = 0;
340 userScrolling = false;
342 this->process_message_queue();
343 } else {
344 this->thread_safe_print(cmd + "\n");
345 needsRedraw = true;
346 needsReflow = true;
347 scrollOffset = 0;
348 userScrolling = false;
350 this->process_message_queue();
351 }
352 } else {
353 inputBuffer.clear();
354 inputCursorPos = 0;
355
356 if (cmd.find('{') != std::string::npos && cmd.find('}') == std::string::npos) {
357 braceCount = 0;
358 for (char ch : cmd) {
359 if (ch == '{') braceCount++;
360 else if (ch == '}') braceCount--;
361 }
362
363 if (braceCount > 0) {
364 inMultilineMode = true;
366 promptText = "... ";
367 multilineBuffer = cmd + "\n";
368 this->thread_safe_print(cmd + "\n");
369 return;
370 }
371 }
372 procCmd(cmd);
373 }
374
376 } else if (c == '{') {
377 inputBuffer.insert(inputCursorPos, 1, c);
380
381 if (!inMultilineMode) {
382 braceCount++;
383 }
384 } else if (c == '}') {
385 inputBuffer.insert(inputCursorPos, 1, c);
388
389 if (inMultilineMode) {
390 braceCount--;
391 if (braceCount == 0) {
392 std::string processedCommand;
393 size_t firstOpenBrace = multilineBuffer.find('{');
394 if (firstOpenBrace != std::string::npos) {
395 multilineBuffer.erase(firstOpenBrace, 1);
396 }
397 size_t lastCloseBrace = inputBuffer.find_last_of('}');
398 if (lastCloseBrace != std::string::npos) {
399 inputBuffer.erase(lastCloseBrace, 1);
400 }
401 processedCommand = multilineBuffer + inputBuffer;
402 inMultilineMode = false;
404 this->thread_safe_print(inputBuffer + "\n");
405 inputBuffer.clear();
406 inputCursorPos = 0;
407 multilineBuffer.clear();
408 procCmd(processedCommand + "\n");
409 needsRedraw = true;
411 this->process_message_queue();
412 }
413 }
414 } else {
415 inputBuffer.insert(inputCursorPos, 1, c);
418 }
419 } catch (const std::exception &e) {
420 std::cerr << "Error in keypress: " << e.what() << std::endl;
421 }
422 }
423
426 std::string text = data.str();
427 if (cursorPos == 0 || c_chars.characters.empty()) return;
428
429 int x = console_rect.x;
430 int y = console_rect.y;
431 int charWidth = c_chars.characters['A']->w;
432 int maxWidth = console_rect.w - 50;
433
434 for (size_t i = 0; i < text.length(); ++i) {
435 if (i >= cursorPos) break;
436
437 char c = text[i];
438 if (c == '\n') {
439 x = console_rect.x;
440 y += c_chars.characters['A']->h;
441 } else {
442 int width = 0;
443 if (c == '\t') {
444 width = charWidth * 4;
445 } else {
446 auto it = c_chars.characters.find(c);
447 if (it != c_chars.characters.end()) {
448 width = it->second->w;
449 } else {
450 width = c_chars.characters['A']->w;
451 }
452 }
453
454 x += width;
455
456 if (x >= (console_rect.x + maxWidth)) {
457 x = console_rect.x;
458 y += c_chars.characters['A']->h;
459 }
460 }
461 }
462 }
463
466 if (!surface || c_chars.characters.empty()) {
467 return;
468 }
469
470 std::string text = data.str();
471 if (text.empty()) {
474 return;
475 }
476
477 if (cursorPos > text.length()) {
478 cursorPos = text.length();
479 }
480
481 int x = console_rect.x;
482 int y = console_rect.y;
483 int maxWidth = console_rect.w - 50;
484 for (size_t i = 0; i < cursorPos; ++i) {
485 char c = text[i];
486
487 if (c == '\n') {
488 x = console_rect.x;
489 y += c_chars.characters['A']->h;
490 continue;
491 }
492
493 if (x + c_chars.characters['A']->w > console_rect.x + maxWidth) {
494 x = console_rect.x;
495 y += c_chars.characters['A']->h;
496 }
497
498 int width = 0;
499 if (c == '\t') {
500 width = c_chars.characters['A']->w * 4;
501 } else {
502 auto it = c_chars.characters.find(c);
503 if (it != c_chars.characters.end()) {
504 width = it->second->w;
505 } else {
506 width = c_chars.characters['A']->w;
507 }
508 }
509
510 x += width;
511 }
512
513 cursorX = x;
514 cursorY = y;
515 }
516
519 if (!surface || c_chars.characters.empty() || c_chars.characters.find('A') == c_chars.characters.end()) {
520 return;
521 }
522 if (needsReflow) {
524 }
525 int charHeight = c_chars.characters['A']->h;
526 int bottomPadding = charHeight + 10;
527 int promptY = console_rect.y + console_rect.h - charHeight - bottomPadding;
528 int maxVisibleLines = (promptY - console_rect.y) / charHeight;
529 int maxScrollbackLines = maxVisibleLines * 50;
530
531 while (static_cast<int>(lines.size()) > maxScrollbackLines && !lines.empty()) {
532 size_t removeLen = lines[0].length;
533 std::string buffer = data.str();
534 if (removeLen < buffer.length() && buffer[removeLen] == '\n') {
535 removeLen++;
536 }
537 if (removeLen > buffer.length()) removeLen = buffer.length();
538 std::string newBuffer = buffer.substr(removeLen);
539 data.str("");
540 data << newBuffer;
541 lines.clear();
543 }
544 std::string finalText = data.str();
545 if (cursorPos > finalText.length()) {
546 cursorPos = finalText.length();
547 }
548 }
549
552
553 auto it_A_check = c_chars.characters.find('A');
554 if (!surface || c_chars.characters.empty() || it_A_check == c_chars.characters.end() || !it_A_check->second || it_A_check->second->h == 0) {
555 return;
556 }
557
558 lines.clear();
559 std::string text = data.str();
560 int maxWidth = console_rect.w - 50;
561
562 size_t lineStart = 0;
563 size_t currentPos = 0;
564 int currentLineWidth = 0;
565
566 SDL_Surface* char_A_surface = it_A_check->second;
567 int defaultCharWidth = char_A_surface->w;
568 int lineHeight = char_A_surface->h;
569
570 while (currentPos < text.length()) {
571 char c = text[currentPos];
572 int charWidth = 0;
573
574 if (c == '\n') {
575 lines.push_back({text.substr(lineStart, currentPos - lineStart), lineStart, currentPos - lineStart});
576 lineStart = currentPos + 1;
577 currentLineWidth = 0;
578 currentPos++;
579 continue;
580 }
581
582 if (c == '\t') {
583 charWidth = defaultCharWidth * 4;
584 } else {
585 auto it_char = c_chars.characters.find(c);
586 if (it_char != c_chars.characters.end() && it_char->second) {
587 charWidth = it_char->second->w;
588 } else {
589 charWidth = defaultCharWidth;
590 }
591 }
592
593 if (lineStart < currentPos && (currentLineWidth + charWidth) > maxWidth) {
594 lines.push_back({text.substr(lineStart, currentPos - lineStart), lineStart, currentPos - lineStart});
595 lineStart = currentPos;
596 currentLineWidth = charWidth;
597 } else {
598 currentLineWidth += charWidth;
599 }
600 currentPos++;
601 }
602
603 if (lineStart < text.length()) {
604 lines.push_back({text.substr(lineStart), lineStart, text.length() - lineStart});
605 }
606
607 needsReflow = false;
608
609
610 int bottomPadding = lineHeight + 10;
611 int promptY = console_rect.y + console_rect.h - lineHeight - bottomPadding;
612
613
614 int maxVisibleLines = (promptY - console_rect.y) / lineHeight;
615 maxVisibleLines -= 1;
616 if (maxVisibleLines < 1) maxVisibleLines = 1;
617
618
619 visibleLineCount = maxVisibleLines;
620 }
621
622 SDL_Surface *Console::drawText() {
624 if (fadeState != FADE_NONE) {
625 Uint32 currentTime = SDL_GetTicks();
626 Uint32 elapsedTime = currentTime - fadeStartTime;
627
628 if (fadeState == FADE_IN) {
629 if (elapsedTime >= fadeDuration) {
630 alpha = 188;
632 } else {
633 float t = (float)elapsedTime / (float)fadeDuration;
634 if (t < 0.0f) t = 0.0f;
635 if (t > 1.0f) t = 1.0f;
636 alpha = (unsigned char)(1 + (188 - 1) * t);
637 }
638 } else if (fadeState == FADE_OUT) {
639 if (elapsedTime >= fadeDuration) {
640 alpha = 0;
642 } else {
643 float t = (float)elapsedTime / (float)fadeDuration;
644 if (t < 0.0f) t = 0.0f;
645 if (t > 1.0f) t = 1.0f;
646 alpha = (unsigned char)(188 * (1.0f - t));
647 }
648 }
649 needsRedraw = true;
650 }
651
652 if (!surface) return nullptr;
653 if(!needsRedraw && fadeState == FADE_NONE && surface) return surface;
654
655 SDL_FillRect(surface, 0, SDL_MapRGBA(surface->format, 0, 0, 0, alpha));
656
657 if (alpha < 5) {
658 return surface;
659 }
660
661 if (needsReflow) {
663 }
664
665 int lineHeight = c_chars.characters['A']->h;
666 int bottomPadding = lineHeight + 10;
667 int promptY = console_rect.y + console_rect.h - lineHeight - bottomPadding;
668
669 SDL_Rect separatorRect = {
670 console_rect.x,
671 promptY - 5,
672 console_rect.w,
673 2
674 };
675 SDL_FillRect(surface, &separatorRect, SDL_MapRGBA(surface->format, 75, 75, 75, 220));
676
677 int maxVisibleLines = (promptY - console_rect.y) / lineHeight;
678 maxVisibleLines -= 1;
679 if (maxVisibleLines < 1) maxVisibleLines = 1;
680
681 size_t startLine = 0;
682 if (static_cast<int>(lines.size()) > maxVisibleLines) {
683 int totalLines = lines.size();
684 int lastVisibleLine = totalLines - 1 - scrollOffset;
685 startLine = std::max(0, lastVisibleLine - maxVisibleLines + 1);
686 } else {
687 startLine = 0;
688 }
689
690 int y = console_rect.y;
691 for (size_t i = startLine; i < lines.size() && i < startLine + maxVisibleLines; ++i) {
692 const auto& line = lines[i];
693 if (y >= promptY) break;
694
695 int x = console_rect.x;
696 for (char c : line.text) {
697 auto it = c_chars.characters.find(c);
698 if (it != c_chars.characters.end()) {
699 SDL_Surface *char_surface = it->second;
700 SDL_Rect dest_rect = {x, y, char_surface->w, char_surface->h};
701 SDL_BlitSurface(char_surface, nullptr, surface, &dest_rect);
702 x += char_surface->w;
703 } else if (c == '\t') {
704 x += c_chars.characters['A']->w * 4;
705 }
706 }
707
708 y += lineHeight;
709 }
710
711
712 int x = console_rect.x;
713 if (inputCursorPos > inputBuffer.size())
715
716 for (char c : promptText) {
717 auto it = c_chars.characters.find(c);
718 if (it != c_chars.characters.end()) {
719 SDL_Surface *char_surface = it->second;
720 SDL_Rect dest_rect = {x, promptY, char_surface->w, char_surface->h};
721 SDL_BlitSurface(char_surface, nullptr, surface, &dest_rect);
722 x += char_surface->w;
723 } else if (c == '\t') {
724 x += c_chars.characters['A']->w * 4;
725 }
726 }
727
728 int cursorXPos = x;
729 int maxWidth = console_rect.w - 50;
730
731 for (int i = inputScrollOffset; i < (int)inputBuffer.size(); ++i) {
732 char c = inputBuffer[i];
733
734 if (i == static_cast<int>(inputCursorPos)) {
735 cursorXPos = x;
736 }
737
738 int charWidth = 0;
739 if (c == '\t') {
740 charWidth = c_chars.characters['A']->w * 4;
741 } else {
742 auto it = c_chars.characters.find(c);
743 charWidth = (it != c_chars.characters.end()) ?
744 it->second->w : c_chars.characters['A']->w;
745 }
746
747 if (x + charWidth > console_rect.x + maxWidth) {
748 break;
749 }
750
751 if (c != '\t') {
752 auto it = c_chars.characters.find(c);
753 if (it != c_chars.characters.end()) {
754 SDL_Surface *char_surface = it->second;
755 SDL_Rect dest_rect = {x, promptY, char_surface->w, char_surface->h};
756 SDL_BlitSurface(char_surface, nullptr, surface, &dest_rect);
757 }
758 }
759
760 x += charWidth;
761 }
762
763 if (inputCursorPos >= inputBuffer.size()) {
764 cursorXPos = x;
765 }
766
767 if (cursorVisible) {
768 SDL_Rect cursorRect = {cursorXPos, promptY, 2, c_chars.characters['A']->h};
769 SDL_FillRect(surface, &cursorRect, SDL_MapRGBA(surface->format, 255, 255, 255, 255));
770 }
771 needsRedraw = false;
772
773 int total = lines.size();
774 if (total > visibleLineCount) {
775 int scrollbarWidth = 15;
776 int scrollbarX = console_rect.w - scrollbarWidth - 5;
777 int scrollbarHeight = console_rect.h - 80;
778 int scrollbarY = 10;
779
780 SDL_Rect trackRect = {scrollbarX, scrollbarY, scrollbarWidth, scrollbarHeight};
781 SDL_FillRect(surface, &trackRect, SDL_MapRGBA(surface->format, 40, 40, 40, 180));
782
783 scrollbarRect.x = console_rect.x + trackRect.x;
784 scrollbarRect.y = console_rect.y + trackRect.y;
785 scrollbarRect.w = trackRect.w;
786 scrollbarRect.h = trackRect.h;
787
788 float thumbRatio = (float)visibleLineCount / total;
789 int thumbHeight = std::max(20, (int)(thumbRatio * scrollbarHeight));
790 float scrollProgress = 1.0f - ((float)scrollOffset / (total - visibleLineCount));
791 int thumbY = scrollbarY + (int)(scrollProgress * (scrollbarHeight - thumbHeight));
792
793 SDL_Rect thumbRect = {scrollbarX + 1, thumbY, scrollbarWidth - 2, thumbHeight};
794 SDL_FillRect(surface, &thumbRect, SDL_MapRGBA(surface->format, 120, 120, 120, 0));
795 }
797 return surface;
798 }
799
800 std::vector<std::string> tokenize(const std::string& input) {
801 std::vector<std::string> tokens;
802 std::istringstream iss(input);
803 std::string token;
804 while (iss >> token) {
805 tokens.push_back(token);
806 }
807 return tokens;
808 }
809
812 scrollOffset = 0;
813 userScrolling = false;
814 cursorPos = data.str().length();
816 checkScroll();
817 inputCursorPos = inputBuffer.length();
818 needsRedraw = true;
819 }
820
823 if (inputCursorPos > 0) {
825
826 if (static_cast<int>(inputCursorPos) < inputScrollOffset) {
828 needsRedraw = true;
829 }
830
832 }
833 }
834
842
848
854
857 if (historyIndex == -1 && !inputBuffer.empty()) {
859 }
860 if (!commandHistory.empty() && (historyIndex < static_cast<int>(commandHistory.size()) - 1)) {
861 historyIndex++;
863 inputCursorPos = inputBuffer.length();
866 }
867 }
868
886
887 std::istream &GLConsole::inputData() {
888 inputStream.str(console.inputBuffer);
889 inputStream.clear();
890 return inputStream;
891 }
892
894 // Release font/TTF resources before TTF_Quit
895 console.clear();
896 if (!SDL_GL_GetCurrentContext()) {
897 texture = 0;
898 sprite.reset();
899 shader.reset();
900 return;
901 }
902 if(texture && glIsTexture(texture)) {
903 glDeleteTextures(1, &texture);
904 texture = 0;
905 }
906 if(sprite) {
907 sprite.reset();
908 }
909
910 if(shader) {
911 shader->release();
912 shader.reset();
913 }
914 }
915
916 std::ostringstream &Console::bufferData() {
917 return data;
918 }
919
920 void Console::thread_safe_print(const std::string &str) {
922 message_queue.push(str);
923 needsRedraw = true;
924 needsReflow = true;
925 }
926
928 while (worker_active) {
930 if (command_queue.pop(cmd)) {
931 std::ostringstream result;
932 try {
933 cmd.callback(cmd.text, result);
934 if (!result.str().empty()) {
935 thread_safe_print(result.str());
936 }
937 } catch (const std::exception& e) {
938 thread_safe_print("Error: " + std::string(e.what()) + "\n");
939 } catch (...) {
940 thread_safe_print("Unknown error occurred\n");
941 }
942
943 if (window) {
944 SDL_Event ev;
945 ev.type = SDL_USEREVENT;
946 SDL_PushEvent(&ev);
947 }
948 }
949 }
950 }
951
954 if(message_queue.empty()) return;
955 while(!message_queue.empty()) {
956 data << message_queue.front();
957 message_queue.pop();
958 }
959 static constexpr size_t MAX_CHARS = 300000;
960 auto s = data.str();
961 if (s.size() > MAX_CHARS) {
962 s = s.substr(s.size() - MAX_CHARS);
963 data.str("");
964 data << s;
965 }
966 needsReflow = needsRedraw = true;
968 }
969 void Console::setInputCallback(std::function<int(gl::GLWindow *win, const std::string &)> callback) {
972 this->enterCallbackSet = true;
973 }
974
975 void Console::procCmd(const std::string &cmd_text) {
976
977 if (cmd_text.empty()) {
978 return;
979 }
980
981 if (!inMultilineMode || braceCount == 0) {
982 if (commandHistory.empty() || commandHistory.back() != cmd_text) {
983 commandHistory.push_back(cmd_text);
984 const size_t MAX_HISTORY = 50;
985 if (commandHistory.size() > MAX_HISTORY) {
986 commandHistory.erase(commandHistory.begin());
987 }
988 }
989 }
990
991 historyIndex = -1;
992 tempBuffer.clear();
993 std::vector<std::string> tokens = tokenize(cmd_text);
994 if (callbackEnter != nullptr && window != nullptr && enterCallbackSet) {
995 command_queue.push({cmd_text, [this, cmd_text](const std::string& text, std::ostream& output) {
996 if (callbackEnter && window) {
997 callbackEnter(window, text);
998 }
999 }});
1000#if !THREAD_ENABLED
1001 processCommandQueue();
1002#endif
1003
1004 needsReflow = true;
1005 }
1006 else if (callbackSet && window != nullptr) {
1007 if (!callback(this->window, tokens)) {
1008 needsReflow = true;
1009 }
1010 }
1011
1013 checkScroll();
1014 }
1015
1016#ifdef __EMSCRIPTEN__
1017 void Console::processCommandQueue() {
1018#if !THREAD_ENABLED
1020 while (command_queue.pop(cmd)) {
1021 std::ostringstream result;
1022 try {
1023 cmd.callback(cmd.text, result);
1024 if (!result.str().empty()) {
1025 thread_safe_print(result.str());
1026 }
1027 } catch (const std::exception& e) {
1028 thread_safe_print("Error: " + std::string(e.what()) + "\n");
1029 } catch (...) {
1030 thread_safe_print("Unknown error occurred\n");
1031 }
1032
1033 if (window) {
1034 SDL_Event ev;
1035 ev.type = SDL_USEREVENT;
1036 SDL_PushEvent(&ev);
1037 }
1038 }
1039#endif
1040 }
1041#endif
1044 fadeStartTime = SDL_GetTicks();
1045 if (alpha == 0)
1046 alpha = 1;
1047 }
1048
1051 data.str("");
1052 data.clear();
1053 lines.clear();
1054 needsReflow = true;
1055 needsRedraw = true;
1056 }
1057
1060 fadeStartTime = SDL_GetTicks();
1061 }
1062
1064 startFadeIn();
1065 }
1066
1068 startFadeOut();
1069 }
1070
1071 void Console::setTextAttrib(const int size, const SDL_Color &col) {
1073 font_size = size;
1074 color = col;
1075 reload();
1076 }
1077
1079 console.startFadeIn();
1080 }
1081
1083 console.startFadeOut();
1084 }
1085
1087 rc = {0, 0, 0, 0};
1088 }
1089
1091 release();
1092 }
1093
1095 console.clearText();
1096 }
1097
1098 void GLConsole::thread_safe_print(const std::string &data) {
1099 console.thread_safe_print(data);
1100 }
1101
1103 console.process_message_queue();
1104 }
1105
1106 void GLConsole::setInputCallback(std::function<int(gl::GLWindow *window, const std::string &)> callback) {
1107 console.setInputCallback(callback);
1108 }
1109
1111 if (!surf) {
1112 std::cerr << "Surface is null." << std::endl;
1113 return 0;
1114 }
1115 GLuint texture_id = 0;
1116 glGenTextures(1, &texture_id);
1117 glBindTexture(GL_TEXTURE_2D, texture_id);
1118 GLenum pixelFormat = GL_RGBA;
1119 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0,
1120 pixelFormat, GL_UNSIGNED_BYTE, surf->pixels);
1121
1122 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1123 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1124 return texture_id;
1125 }
1126
1127 void GLConsole::load(gl::GLWindow *win, const SDL_Rect &rc, const std::string &fnt, int size, const SDL_Color &col) {
1128 font = fnt;
1129 font_size = size;
1130 color = col;
1131 console.util = &win->util;
1132 this->rc = rc;
1133 stretch_height = 0;
1134 console.create(rc.x, rc.y, rc.w, rc.h);
1135 if(console.loaded()) {
1136 console.reload();
1137 } else {
1138 console.load(fnt, size, col);
1139 }
1140 console.promptText = "$ ";
1141 shader = std::make_unique<gl::ShaderProgram>();
1142 if(!shader->loadProgramFromText(gl::vSource, gl::fSource)) {
1143 throw mx::Exception("Error loading shader");
1144 }
1145 if(sprite == nullptr)
1146 sprite = std::make_unique<gl::GLSprite>();
1147 else
1148 sprite.reset(new gl::GLSprite());
1149
1150 texture = loadTextFromSurface(console.drawText());
1151 sprite->initSize(win->w, win->h);
1152 sprite->initWithTexture(shader.get(), texture, 0.0f, 0.0f, win->w, win->h);
1153 stretch_value = true;
1154 }
1155
1156 void GLConsole::setTextAttrib(const int size, const SDL_Color &col) {;
1157 console.setTextAttrib(size, col);
1158 }
1159 void GLConsole::load(gl::GLWindow *win, const std::string &fnt, int size, const SDL_Color &col) {
1160 font = fnt;
1161 font_size = size;
1162 color = col;
1163 int consoleWidth = win->w - 50;
1164 int consoleHeight = (win->h / 2) - 50;
1165 SDL_Rect rc;
1166 rc.x = 25;
1167 rc.y = 25;
1168 rc.w = consoleWidth;
1169 rc.h = consoleHeight;
1170 this->rc = rc;
1171 console.util = &win->util;
1172 load(win, rc, fnt, size, col);
1173 stretch_height = 1;
1174 stretch_value = true;
1175 }
1176
1177 void GLConsole::updateTexture(GLuint tex, SDL_Surface *surf) {
1178 glActiveTexture(GL_TEXTURE0);
1179 glBindTexture(GL_TEXTURE_2D, tex);
1180 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, surf->w, surf->h, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels);
1181 glBindTexture(GL_TEXTURE_2D, 0);
1182 }
1183
1185 Uint32 currentTime = SDL_GetTicks();
1186
1187
1188 if (console.fadeState != FADE_NONE) {
1189 console.needsRedraw = true;
1190 }
1191
1192
1193 if (currentTime - console.cursorBlinkTime > 500) {
1194 console.cursorVisible = !console.cursorVisible;
1195 console.cursorBlinkTime = currentTime;
1196 console.needsRedraw = true;
1197 }
1198
1199 SDL_Surface *surface = nullptr;
1200 if (console.needsRedraw) {
1201 surface = console.drawText();
1202 if (surface) {
1203 shader->useProgram();
1204 shader->setUniform("textTexture", 0);
1205 updateTexture(texture, surface);
1206 }
1207 }
1208
1209 int consoleWidth = console.getWidth();
1210 int consoleHeight = console.getHeight();
1211 sprite->draw(texture, 25.0f, 25.0f, consoleWidth, consoleHeight);
1212 }
1213
1214 void GLConsole::print(const std::string &data) {
1215 console.thread_safe_print(data);
1216 SDL_Event ev{SDL_USEREVENT};
1217 SDL_PushEvent(&ev);
1218 }
1219
1220 void GLConsole::println(const std::string &data) {
1221 print(data + "\n");
1222 }
1223
1224 void GLConsole::event(gl::GLWindow *win, SDL_Event &e) {
1225 if(e.type == SDL_TEXTINPUT) {
1226 for(int i = 0; i < SDL_TEXTINPUTEVENT_TEXT_SIZE && e.text.text[i] != '\0'; ++i) {
1227 console.keypress(e.text.text[i]);
1228 }
1229 return;
1230 }
1231 if(e.type == SDL_KEYDOWN) {
1232 console.needsRedraw = true;
1233 if(e.key.keysym.sym == SDLK_ESCAPE) {
1234 win->quit();
1235 } else if(e.key.keysym.sym == SDLK_RETURN) {
1236 console.keypress(13);
1237 } else if(e.key.keysym.sym == SDLK_BACKSPACE) {
1238 console.keypress(8);
1239 } else if(e.key.keysym.sym == SDLK_TAB) {
1240 console.keypress('\t');
1241 } else if(e.key.keysym.sym == SDLK_LEFT) {
1242 console.moveCursorLeft();
1243 return;
1244 } else if(e.key.keysym.sym == SDLK_RIGHT) {
1245 console.moveCursorRight();
1246 return;
1247 } else if(e.key.keysym.sym == SDLK_HOME) {
1248 console.moveCursorHome();
1249 return;
1250 } else if(e.key.keysym.sym == SDLK_END) {
1251 console.moveCursorEnd();
1252 return;
1253 } else if(e.key.keysym.sym == SDLK_UP) {
1254 console.moveHistoryUp();
1255 return;
1256 } else if(e.key.keysym.sym == SDLK_DOWN) {
1257 console.moveHistoryDown();
1258 return;
1259 }
1260 else if(e.key.keysym.sym == SDLK_PAGEUP) {
1261 console.scrollPageUp();
1262 return;
1263 }
1264 else if(e.key.keysym.sym == SDLK_PAGEDOWN) {
1265 console.scrollPageDown();
1266 return;
1267 } else if(e.key.keysym.sym == SDLK_DELETE) {
1268 console.keypress(127);
1269 }
1270 }
1271 else if(e.type == SDL_MOUSEWHEEL) {
1272 if (win->console_visible) {
1273 int x, y;
1274 SDL_GetMouseState(&x, &y);
1275 console.handleMouseScroll(x, y, e.wheel.y);
1276 return;
1277 }
1278 }
1279 else if(e.type == SDL_MOUSEBUTTONDOWN) {
1280 if (win->console_visible && e.button.button == SDL_BUTTON_LEFT) {
1281 if (console.isMouseOverScrollbar(e.button.x, e.button.y)) {
1282 console.beginScrollDrag(e.button.y);
1283 return;
1284 }
1285 }
1286 }
1287 else if(e.type == SDL_MOUSEMOTION) {
1288 if (win->console_visible && console.scrollDragging) {
1289 console.updateScrollDrag(e.motion.y);
1290 return;
1291 }
1292 }
1293 else if(e.type == SDL_MOUSEBUTTONUP) {
1294 if (win->console_visible && e.button.button == SDL_BUTTON_LEFT) {
1295 if (console.scrollDragging) {
1296 console.endScrollDrag();
1297 }
1298 return;
1299 }
1300 }
1301
1302#if defined(__EMSCRIPTEN__)
1303 refresh();
1304#endif
1305 }
1306
1308 stretch_height = value;
1309 }
1310
1311 void GLConsole::resize(gl::GLWindow *win, int w, int h) {
1312 if(stretch_value) {
1313 rc.x = 25;
1314 rc.y = 25;
1315 rc.w = w - 50;
1316 if(stretch_height == 1)
1317 rc.h = (h / 2) - 50;
1318 else
1319 rc.h = h - 50;
1320 }
1321
1322 if(!font.empty())
1323 load(win, rc, font, font_size, color);
1324
1325 console.needsRedraw = true;
1326 }
1327
1328 void GLConsole::setCallback(gl::GLWindow *window, std::function<bool(gl::GLWindow *win, const std::vector<std::string> &)> callback) {
1329 console.setCallback(window, callback);
1330 }
1331
1332 void GLConsole::setPrompt(const std::string &p) {
1333 console.promptText = p;
1334 }
1335 int GLConsole::getWidth() const { return console.getWidth(); }
1336 int GLConsole::getHeight() const { return console.getHeight(); }
1337
1338 bool GLConsole::procDefaultCommands(const std::vector<std::string> &cmd) {
1339 if (cmd.empty()) {
1340 return true;
1341 } else if(cmd.size() == 1 && (cmd[0] == "about" || cmd[0] == "version")) {
1342 print("MX2 version " + std::to_string(PROJECT_VERSION_MAJOR) + "." + std::to_string(PROJECT_VERSION_MINOR) + "\n");
1343 print("writen by Jared Bruni\n");
1344 print("https://lostsidedead.biz\n");
1345 }
1346 else if (cmd.size() > 0 && cmd[0] == "clear") {
1347 console.clearText();
1348 return true;
1349 } else if (cmd.size() > 0 && cmd[0] == "exit") {
1350 print("Exiting...\n");
1351 if (console.window) {
1352 console.window->quit();
1353 }
1354 return true;
1355 } else if(cmd.size() == 5 && cmd[0] == "settext") {
1356 int size = std::stoi(cmd[1]);
1357 SDL_Color color;
1358 color.r = std::stoi(cmd[2]);
1359 color.g = std::stoi(cmd[3]);
1360 color.b = std::stoi(cmd[4]);
1361 color.a = 255;
1362 setTextAttrib(size, color);
1363 print("Text attributes set to size " + std::to_string(size) +
1364 " and color (" + std::to_string(color.r) + ", " +
1365 std::to_string(color.g) + ", " + std::to_string(color.b) + ")\n");
1366 return true;
1367 } else {
1368 print("Unknown command: " + cmd[0] + "\n");
1369 return false;
1370 }
1371 return false;
1372 }
1373
1376 int pageSize = std::max(1, visibleLineCount - 1);
1377 int maxOffset = std::max(0, (int)lines.size() - visibleLineCount);
1378 scrollOffset = std::min(maxOffset, scrollOffset + pageSize);
1379 needsRedraw = true;
1380 }
1381
1384 int pageSize = std::max(1, visibleLineCount - 1);
1385 scrollOffset = std::max(0, scrollOffset - pageSize);
1386 needsRedraw = true;
1387 }
1388
1389 bool Console::isMouseOverScrollbar(int mouseX, int mouseY) const {
1390 bool hit = mouseX >= scrollbarRect.x
1391 && mouseX < scrollbarRect.x + scrollbarRect.w
1392 && mouseY >= scrollbarRect.y
1393 && mouseY < scrollbarRect.y + scrollbarRect.h;
1394 return hit;
1395 }
1396
1403
1404 void Console::updateScrollDrag(int mouseY) {
1406 if (!scrollDragging) return;
1407 int total = lines.size();
1408 if (total <= visibleLineCount) return;
1409
1410 int totalLines = lines.size();
1411 if (totalLines <= visibleLineCount) return;
1412
1413
1414 int thumbMinH = 20;
1415 int trackH = scrollbarRect.h;
1416
1417
1418 int localY = mouseY - scrollbarRect.y;
1419 int maxLocal = trackH - thumbMinH;
1420 localY = std::max(0, std::min(maxLocal, localY));
1421
1422 float progress = 1.0f - (float)localY / (float)maxLocal;
1423 int maxOffset = std::max(0, total - visibleLineCount);
1424 scrollOffset = (int)(progress * maxOffset);
1425 scrollOffset = std::max(0, std::min(maxOffset, scrollOffset));
1426 needsRedraw = true;
1427 }
1428
1433
1434 void Console::handleMouseScroll(int mouseX, int mouseY, int wheelY) {
1436 if (wheelY > 0) {
1437 scrollUp();
1438 } else if (wheelY < 0) {
1439 scrollDown();
1440 }
1441 }
1442}
std::mutex queue_mutex
Definition console.hpp:96
bool pop(Command &cmd)
Pop the next command (blocking on native, non-blocking on Emscripten).
Definition console.cpp:80
std::queue< Command > queue
Definition console.hpp:94
void push(const Command &cmd)
Push a command onto the queue (thread-safe).
Definition console.cpp:70
std::atomic< bool > active
Definition console.hpp:101
std::condition_variable cv
Definition console.hpp:97
void shutdown()
Signal the worker to drain and stop.
Definition console.cpp:101
std::unordered_map< char, SDL_Surface * > characters
Glyph surfaces keyed by char.
Definition console.hpp:132
void load(const std::string &fnt, int size, const SDL_Color &c)
Load/reload the glyph cache from a font file.
Definition console.cpp:41
std::unique_ptr< mx::Font > font
Definition console.hpp:134
void clear()
Release all cached SDL_Surface objects.
Definition console.cpp:57
std::vector< TextLine > lines
Definition console.hpp:363
FadeState fadeState
Current fade animation state.
Definition console.hpp:295
void moveCursorLeft()
Move caret one character to the left.
Definition console.cpp:821
void show()
Make the console fully visible immediately.
Definition console.cpp:1063
void resetScroll()
Reset scroll to the bottom.
Definition console.cpp:195
unsigned char alpha
Definition console.hpp:354
void scrollPageDown()
Scroll down by one page.
Definition console.cpp:1382
std::string font
Definition console.hpp:332
SDL_Surface * drawText()
Render the console contents to an internal SDL_Surface.
Definition console.cpp:622
void procCmd(const std::string &cmd)
Tokenise and dispatch a command string.
Definition console.cpp:975
void moveCursorEnd()
Move caret to end of input line.
Definition console.cpp:849
void calculateLines()
Definition console.cpp:550
std::unique_ptr< std::thread > worker_thread
Definition console.hpp:372
std::recursive_mutex console_mutex
Definition console.hpp:328
SDL_Rect console_rect
Definition console.hpp:338
std::function< int(gl::GLWindow *window, const std::string &)> callbackEnter
Definition console.hpp:349
void updateCursorPosition()
Definition console.cpp:464
void create(int x, int y, int w, int h)
Set position and size of the console rectangle.
Definition console.cpp:142
unsigned int fadeDuration
Definition console.hpp:356
std::ostringstream & bufferData()
Access the internal output buffer directly as an ostream.
Definition console.cpp:916
void scrollToBottom()
Jump the scroll view to the most recent output.
Definition console.cpp:810
SDL_Rect scrollbarRect
Definition console.hpp:384
CommandQueue command_queue
Definition console.hpp:373
std::vector< std::string > commandHistory
Definition console.hpp:351
SDL_Color color
Definition console.hpp:334
gl::GLWindow * window
Owning GL window (weak reference).
Definition console.hpp:255
void print(const std::string &str)
Append text to the output buffer.
Definition console.cpp:205
void endScrollDrag()
Release the scrollbar thumb.
Definition console.cpp:1429
std::string originalPrompt
Definition console.hpp:367
std::queue< std::string > message_queue
Definition console.hpp:329
std::function< bool(gl::GLWindow *win, const std::vector< std::string > &)> callback
Definition console.hpp:348
void moveHistoryUp()
Recall the previous command from history.
Definition console.cpp:855
void scrollDown()
Scroll output down by one line.
Definition console.cpp:186
void checkForLineWrap()
Definition console.cpp:424
void moveCursorHome()
Move caret to start of input line.
Definition console.cpp:843
void thread_safe_print(const std::string &str)
Append text from any thread (uses the internal message queue).
Definition console.cpp:920
ConsoleChars c_chars
Definition console.hpp:330
void updateScrollDrag(int mouseY)
Continue a scrollbar thumb drag.
Definition console.cpp:1404
void beginScrollDrag(int mouseY)
Start a scrollbar thumb drag.
Definition console.cpp:1397
void reload()
Reload the glyph cache with the current font settings.
Definition console.cpp:159
void process_message_queue()
Process pending messages from the thread-safe print queue (call each frame).
Definition console.cpp:952
bool needsReflow
Set to trigger line-wrapping recalculation.
Definition console.hpp:294
void scrollPageUp()
Scroll up by one page.
Definition console.cpp:1374
std::string promptText
Prompt string displayed before the input line.
Definition console.hpp:252
void clear()
Clear the output buffer and reset scroll.
Definition console.cpp:171
std::atomic< bool > worker_active
Definition console.hpp:374
void handleMouseScroll(int mouseX, int mouseY, int wheelY)
Handle a mouse-wheel scroll event.
Definition console.cpp:1434
void setCallback(gl::GLWindow *window, std::function< bool(gl::GLWindow *win, const std::vector< std::string > &)> callback)
Register a command callback invoked when the user presses Enter.
Definition console.cpp:214
void updateInputScrollOffset()
Recalculate horizontal scroll for long input lines.
Definition console.cpp:219
void scrollUp()
Scroll output up by one line.
Definition console.cpp:175
int scrollDragStartOffset
Definition console.hpp:383
bool isMouseOverScrollbar(int mouseX, int mouseY) const
Test whether the mouse is over the scrollbar thumb.
Definition console.cpp:1389
bool cursorVisible
Whether the cursor is currently shown.
Definition console.hpp:254
void keypress(char c)
Handle a single character key press for the input line.
Definition console.cpp:279
void setTextAttrib(const int size, const SDL_Color &col)
Change font size and colour without reloading the widget.
Definition console.cpp:1071
Uint32 fadeStartTime
Definition console.hpp:355
void moveHistoryDown()
Move forward through command history.
Definition console.cpp:869
SDL_Surface * surface
Definition console.hpp:337
void worker_thread_func()
Definition console.cpp:927
std::ostringstream data
Definition console.hpp:335
void moveCursorRight()
Move caret one character to the right.
Definition console.cpp:835
void startFadeOut()
Begin a fade-out animation.
Definition console.cpp:1058
size_t inputCursorPos
Definition console.hpp:341
bool needsRedraw
Set to trigger an SDL_Surface redraw.
Definition console.hpp:293
std::string inputBuffer
Current content of the input line (mirrors internal state).
Definition console.hpp:292
std::string multilineBuffer
Definition console.hpp:366
void setInputCallback(std::function< int(gl::GLWindow *win, const std::string &)> callback)
Register a callback for raw input-line submission (Enter key).
Definition console.cpp:969
void load(const std::string &fnt, int size, const SDL_Color &col)
Load font and rebuild the glyph cache.
Definition console.cpp:163
std::string tempBuffer
Definition console.hpp:353
void startFadeIn()
Begin a fade-in animation.
Definition console.cpp:1042
void hide()
Make the console invisible immediately.
Definition console.cpp:1067
std::istringstream inputStream
Definition console.hpp:567
std::string font
Definition console.hpp:563
void updateTexture(GLuint tex, SDL_Surface *surf)
Definition console.cpp:1177
std::unique_ptr< gl::GLSprite > sprite
Definition console.hpp:558
int getWidth() const
Definition console.cpp:1335
void setPrompt(const std::string &prompt)
Change the prompt prefix string.
Definition console.cpp:1332
void draw(gl::GLWindow *win)
Upload SDL_Surface and blit the GL texture each frame.
Definition console.cpp:1184
void release()
Destroy the GL texture and sprite resources.
Definition console.cpp:893
void clearText()
Clear the entire output buffer.
Definition console.cpp:1094
void process_message_queue()
Flush the thread-safe message queue into the output buffer (call each frame).
Definition console.cpp:1102
int getHeight() const
Definition console.cpp:1336
void print(const std::string &data)
Append text to the console output.
Definition console.cpp:1214
void println(const std::string &data)
Append text followed by a newline.
Definition console.cpp:1220
void show()
Make console visible.
Definition console.cpp:1078
void setStretchHeight(int value)
Set an absolute height override for the stretch mode.
Definition console.cpp:1307
bool procDefaultCommands(const std::vector< std::string > &cmd)
Handle built-in commands (clear, quit, help, …).
Definition console.cpp:1338
std::istream & inputData()
Definition console.cpp:887
GLuint loadTextFromSurface(SDL_Surface *surf)
Definition console.cpp:1110
void event(gl::GLWindow *win, SDL_Event &e)
High-level event handler with window context.
Definition console.cpp:1224
void resize(gl::GLWindow *win, int w, int h)
Handle window resize — rebuilds GL texture at new dimensions.
Definition console.cpp:1311
std::unique_ptr< gl::ShaderProgram > shader
Definition console.hpp:559
void setTextAttrib(const int size, const SDL_Color &col)
Change font size/colour (propagates to inner Console).
Definition console.cpp:1156
void refresh()
Drain the command queue and process pending messages (call each frame).
Definition console.hpp:547
void setInputCallback(std::function< int(gl::GLWindow *window, const std::string &)> callback)
Register a raw-input callback (same semantics as Console::setInputCallback()).
Definition console.cpp:1106
void load(gl::GLWindow *win, const SDL_Rect &rc, const std::string &fnt, int size, const SDL_Color &col)
Load and position the console inside a specific rectangle.
Definition console.cpp:1127
void setCallback(gl::GLWindow *window, std::function< bool(gl::GLWindow *win, const std::vector< std::string > &)> callback)
Register a command callback (same semantics as Console::setCallback()).
Definition console.cpp:1328
void hide()
Hide console.
Definition console.cpp:1082
void thread_safe_print(const std::string &data)
Thread-safe variant of print().
Definition console.cpp:1098
A textured screen-space quad drawn with an OpenGL shader.
Definition gl.hpp:255
SDL2 + OpenGL/ES window with event loop and console support.
Definition gl.hpp:411
int w
Current window width.
Definition gl.hpp:563
int h
Current window height.
Definition gl.hpp:564
bool console_visible
Definition gl.hpp:575
void quit()
Request the event loop to stop.
Definition gl.cpp:328
mx::mxUtil util
Asset path and utility helpers.
Definition gl.hpp:561
General-purpose exception class for the mx library.
Definition exception.hpp:20
RAII wrapper around a TTF_Font handle.
Definition font.hpp:25
#define PROJECT_VERSION_MINOR
Minor version number of the project.
Definition config.hpp:28
#define PROJECT_VERSION_MAJOR
Enable OpenGL support.
Definition config.hpp:23
OpenGL-backed in-game terminal console for libmx2.
#define THREAD_GUARD(mutex)
Definition console.hpp:58
unsigned int GLuint
Definition console.hpp:38
OpenGL window, shader program, sprite, and text rendering classes.
std::vector< std::string > tokenize(const std::string &input)
Definition console.cpp:800
@ FADE_NONE
Definition console.hpp:138
SDL_Surface * flipSurface(SDL_Surface *surface)
Definition console.cpp:12
const char * vSource
Default vertex shader source.
Definition gl.cpp:853
const char * fSource
Default fragment shader source.
Definition gl.cpp:862
A single command entry with its text and completion callback.
Definition console.hpp:75
std::string text
Raw command string.
Definition console.hpp:76
std::function< void(const std::string &, std::ostream &)> callback
Handler invoked on the worker thread.
Definition console.hpp:77