DeveloperConsole.cpp 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. // Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <AnKi/Core/DeveloperConsole.h>
  6. namespace anki {
  7. DeveloperConsole::~DeveloperConsole()
  8. {
  9. Logger::getSingleton().removeMessageHandler(this, loggerCallback);
  10. while(!m_logItems.isEmpty())
  11. {
  12. LogItem* item = &m_logItems.getFront();
  13. m_logItems.popFront();
  14. deleteInstance(UiMemoryPool::getSingleton(), item);
  15. }
  16. }
  17. Error DeveloperConsole::init()
  18. {
  19. zeroMemory(m_inputText);
  20. ANKI_CHECK(UiManager::getSingleton().newInstance(m_font, "EngineAssets/UbuntuMonoRegular.ttf", Array<U32, 1>{16}));
  21. // Add a new callback to the logger
  22. Logger::getSingleton().addMessageHandler(this, loggerCallback);
  23. return Error::kNone;
  24. }
  25. void DeveloperConsole::build(CanvasPtr ctx)
  26. {
  27. const Vec4 oldWindowColor = ImGui::GetStyle().Colors[ImGuiCol_WindowBg];
  28. ImGui::GetStyle().Colors[ImGuiCol_WindowBg].w = 0.3f;
  29. ctx->pushFont(m_font, 16);
  30. ImGui::Begin("Console", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoTitleBar);
  31. ImGui::SetWindowPos(Vec2(0.0f, 0.0f));
  32. ImGui::SetWindowSize(Vec2(F32(ctx->getWidth()), F32(ctx->getHeight()) * (2.0f / 3.0f)));
  33. // Push the items
  34. const F32 footerHeightToPreserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
  35. ImGui::BeginChild("ScrollingRegion", Vec2(0, -footerHeightToPreserve), false,
  36. ImGuiWindowFlags_HorizontalScrollbar); // Leave room for 1 separator + 1 InputText
  37. ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, Vec2(4.0f, 1.0f)); // Tighten spacing
  38. for(const LogItem& item : m_logItems)
  39. {
  40. switch(item.m_type)
  41. {
  42. case LoggerMessageType::kNormal:
  43. ImGui::PushStyleColor(ImGuiCol_Text, Vec4(0.0f, 1.0f, 0.0f, 1.0f));
  44. break;
  45. case LoggerMessageType::kError:
  46. case LoggerMessageType::kFatal:
  47. ImGui::PushStyleColor(ImGuiCol_Text, Vec4(1.0f, 0.0f, 0.0f, 1.0f));
  48. break;
  49. case LoggerMessageType::kWarning:
  50. ImGui::PushStyleColor(ImGuiCol_Text, Vec4(0.9f, 0.6f, 0.14f, 1.0f));
  51. break;
  52. default:
  53. ANKI_ASSERT(0);
  54. }
  55. constexpr Array<const Char*, U(LoggerMessageType::kCount)> kMsgText = {"I", "E", "W", "F"};
  56. ImGui::TextWrapped("[%s][%s] %s [%s:%d][%s][%s]", kMsgText[item.m_type], (item.m_subsystem) ? item.m_subsystem : "N/A ", item.m_msg.cstr(),
  57. item.m_file, item.m_line, item.m_func, item.m_threadName.cstr());
  58. ImGui::PopStyleColor();
  59. }
  60. const U32 timestamp = m_logItemsTimestamp.getNonAtomically();
  61. const Bool scrollToLast = m_logItemsTimestampConsumed < timestamp;
  62. if(scrollToLast)
  63. {
  64. ImGui::SetScrollHereY(1.0f);
  65. ++m_logItemsTimestampConsumed;
  66. }
  67. ImGui::PopStyleVar();
  68. ImGui::EndChild();
  69. // Commands
  70. ImGui::Separator();
  71. ImGui::PushItemWidth(-1.0f); // Use the whole size
  72. if(ImGui::InputText("##noname", &m_inputText[0], m_inputText.getSizeInBytes(), ImGuiInputTextFlags_EnterReturnsTrue, nullptr, nullptr))
  73. {
  74. const Error err = m_scriptEnv.evalString(&m_inputText[0]);
  75. if(!err)
  76. {
  77. ANKI_CORE_LOGI("Script ran without errors");
  78. }
  79. m_inputText[0] = '\0';
  80. }
  81. ImGui::PopItemWidth();
  82. ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget
  83. ImGui::End();
  84. ImGui::GetStyle().Colors[ImGuiCol_WindowBg] = oldWindowColor;
  85. ctx->popFont();
  86. }
  87. void DeveloperConsole::newLogItem(const LoggerMessageInfo& inf)
  88. {
  89. LogItem* newLogItem;
  90. // Pop first
  91. if(m_logItemCount + 1 > kMaxLogItems)
  92. {
  93. LogItem* first = &m_logItems.getFront();
  94. m_logItems.popFront();
  95. first->m_msg.destroy();
  96. first->m_threadName.destroy();
  97. // Re-use the log item
  98. newLogItem = first;
  99. --m_logItemCount;
  100. }
  101. else
  102. {
  103. newLogItem = newInstance<LogItem>(UiMemoryPool::getSingleton());
  104. }
  105. // Create the new item
  106. newLogItem->m_file = inf.m_file;
  107. newLogItem->m_func = inf.m_func;
  108. newLogItem->m_subsystem = inf.m_subsystem;
  109. newLogItem->m_threadName = inf.m_threadName;
  110. newLogItem->m_msg = inf.m_msg;
  111. newLogItem->m_line = inf.m_line;
  112. newLogItem->m_type = inf.m_type;
  113. // Push it back
  114. m_logItems.pushBack(newLogItem);
  115. ++m_logItemCount;
  116. m_logItemsTimestamp.fetchAdd(1);
  117. }
  118. } // end namespace anki