summaryrefslogtreecommitdiff
path: root/Runtime/Misc/DeveloperConsole.cpp
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2019-08-14 22:50:43 +0800
committerchai <chaifix@163.com>2019-08-14 22:50:43 +0800
commit15740faf9fe9fe4be08965098bbf2947e096aeeb (patch)
treea730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/Misc/DeveloperConsole.cpp
+Unity Runtime codeHEADmaster
Diffstat (limited to 'Runtime/Misc/DeveloperConsole.cpp')
-rw-r--r--Runtime/Misc/DeveloperConsole.cpp475
1 files changed, 475 insertions, 0 deletions
diff --git a/Runtime/Misc/DeveloperConsole.cpp b/Runtime/Misc/DeveloperConsole.cpp
new file mode 100644
index 0000000..13def26
--- /dev/null
+++ b/Runtime/Misc/DeveloperConsole.cpp
@@ -0,0 +1,475 @@
+#include "UnityPrefix.h"
+#include "Runtime/Misc/DeveloperConsole.h"
+
+#if UNITY_HAS_DEVELOPER_CONSOLE
+
+#include "Runtime/IMGUI/GUILabel.h"
+#include "Runtime/IMGUI/GUIButton.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Graphics/ScreenManager.h"
+
+#include "Runtime/Threads/ThreadUtility.h"
+
+#include "Runtime/Misc/ReproductionLog.h"
+
+
+static DeveloperConsole* s_DeveloperConsole = 0;
+
+// This function performs the normal loggin operation with the initialized developer console
+void DeveloperConsole_HandleLogFunction (const std::string& condition, const std::string& stackTrace, int type)
+{
+ // Bas Smit and Elvis Alistar suggested to disable the developer console when we are in reproduction mode.
+ // [17:00:40] Bas Smit: I dont see the dev console adding value to the rig
+ // [17:00:57] Elvis Alistar: it actual makes the results very difficult to interpret
+ // [17:01:07] Elvis Alistar: as most of the runs will be different
+#if SUPPORT_REPRODUCE_LOG
+ if (GetReproduceMode() != kPlaybackReproduceLog)
+#endif
+ {
+ UnityMemoryBarrier();
+ if (s_DeveloperConsole)
+ s_DeveloperConsole->HandleLog(condition, stackTrace, static_cast<LogType>(type));
+ }
+}
+
+
+// If nothing is created, then CleanupDeveloperConsole () is a no-op
+void CleanupDeveloperConsole ()
+{
+ RegisterLogCallback(NULL, false);
+
+ DeveloperConsole* tmp = 0;
+ std::swap(s_DeveloperConsole, tmp); // "non-locking destruction"; it is carried out a little bit later
+
+ UnityMemoryBarrier();
+
+ delete tmp;
+}
+
+void InitializeDeveloperConsole ()
+{
+ Assert( NULL == s_DeveloperConsole);
+ s_DeveloperConsole = new DeveloperConsole();
+ RegisterLogCallback(DeveloperConsole_HandleLogFunction, true);
+}
+
+DeveloperConsole& GetDeveloperConsole()
+{
+ Assert( NULL != s_DeveloperConsole);
+ return *s_DeveloperConsole;
+}
+DeveloperConsole* GetDeveloperConsolePtr()
+{
+ return s_DeveloperConsole;
+}
+
+
+DeveloperConsole::DeveloperConsole()
+ : m_ConsoleClosed(false)
+ , m_LogBuffer()
+
+ // State
+ , m_GUIState()
+ , m_Content()
+
+ // Used styles
+ , m_LabelStyle(0)
+ , m_ButtonStyle(0)
+ , m_BoxStyle(0)
+
+ // Titles for GUI elements
+ , m_DevelopmentConsoleBoxTitle("Development Console")
+#if UNITY_WEBPLAYER_AND_STANDALONE
+ , m_OpenLogFileButtonTitle("Open Log File")
+#endif
+ , m_ClearButtonTitle("Clear")
+ , m_CloseButtonTitle("Close")
+{}
+
+DeveloperConsole::~DeveloperConsole()
+{
+ delete m_LabelStyle;
+ delete m_ButtonStyle;
+ delete m_BoxStyle;
+}
+
+LogBufferEntry::LogBufferEntry(const std::string& condition, const std::string& stripped_stacktrace, LogType log)
+ : type(log)
+{
+ // Concatenate condition and the stacktrace;
+ // the only difference between expanded and non-expanded console entry is,
+ // actually, just the length of the rendered string
+ std::string strace(condition);
+ cond_len = strace.length(); // Preserve the length of the condition
+
+ strace += "\n> ";
+ strace += stripped_stacktrace;
+
+ // Initialize the debug string
+ UTF8ToUTF16String(strace.c_str(), debug_text);
+}
+
+void DeveloperConsole::HandleLog(const std::string& condition, const std::string& strippedStacktrace, LogType type)
+{
+ if (type > kLogLevel && type != LogType_Exception)
+ return;
+
+#if SUPPORT_THREADS
+ Mutex::AutoLock lock(m_NewEntriesMutex);
+#endif
+
+ m_NewEntries.push_back( LogBufferEntry(condition, strippedStacktrace, type));
+ if (m_NewEntries.size() > kMaxNumberOfLogMessages)
+ {
+ m_NewEntries.pop_front(); // Forget the first message
+ }
+}
+
+ObjectGUIState& DeveloperConsole::GetObjectGUIState()
+{
+ return m_GUIState;
+}
+
+bool DeveloperConsole::IsVisible () const
+{
+#if SUPPORT_THREADS
+ Mutex::AutoLock consoleLock(m_ConsoleMutex); // Required for m_LogBuffer
+ Mutex::AutoLock newEntriesLock(m_NewEntriesMutex); // Required for m_NewEntries
+#endif
+ if (m_ConsoleClosed)
+ return false;
+ return !m_LogBuffer.empty() || !m_NewEntries.empty();
+}
+
+void DeveloperConsole::SetOpen(bool opened)
+{
+ m_ConsoleClosed = !opened;
+}
+void DeveloperConsole::Clear()
+{
+#if SUPPORT_THREADS
+ Mutex::AutoLock lock(m_ConsoleMutex);
+#endif
+ m_LogBuffer.clear();
+}
+bool DeveloperConsole::HasLogMessages()
+{
+#if SUPPORT_THREADS
+ Mutex::AutoLock lock(m_ConsoleMutex);
+#endif
+ return !m_LogBuffer.empty();
+}
+
+// Initializes an existing instance of a UTF16String in a fast way (avoiding double-copy pattern)
+inline static void FastUTF16StringInit(const UTF16String& src, UTF16String& dst)
+{
+ dst.TakeOverPreAllocatedUTF16Bytes(src.text, src.length);
+ dst.owns = false;
+}
+
+inline static void SetupLogEntryGUIContent(GUIContent& content, const LogBufferEntry& entry)
+{
+ FastUTF16StringInit(entry.debug_text, content.m_Text);
+ if (entry.cond_len > 0)
+ {
+ // if the entry is not expanded, we want to show only the part
+ // that does not include the stacktrace
+ content.m_Text.length = entry.cond_len;
+ }
+}
+
+bool DeveloperConsole::DoGUI()
+{
+#if SUPPORT_THREADS
+ Mutex::AutoLock lock(m_ConsoleMutex);
+#endif
+
+ AppendNewEntries();
+
+ // New entries were copied into into m_LogBuffer, so just check that
+ if (m_ConsoleClosed || m_LogBuffer.empty())
+ return false;
+
+ if (0 == m_LabelStyle) // if this is not initialized,
+ { // then all styles need to be fetched
+ InitGUIStyles();
+ }
+
+ Assert( NULL != m_LabelStyle);
+ Assert( NULL != m_ButtonStyle);
+ Assert( NULL != m_BoxStyle);
+
+ GUIState &guiState = GetGUIState ();
+ guiState.m_CanvasGUIState.m_GUIClipState.BeginOnGUI (*guiState.m_CurrentEvent);
+ guiState.BeginOnGUI(GetObjectGUIState());
+
+ // GUI code must go here
+ const float screen_width = GetScreenManager().GetWidth();
+ const float screen_height = GetScreenManager().GetHeight();
+
+ const float half_screen_width = 0.5f * screen_width;
+
+ float box_height = DrawBox(guiState, screen_height, half_screen_width);
+
+ Rectf position;
+ float label_pos = 0.f;
+ GUIStyle& labelStyle = *m_LabelStyle;
+
+ // We output messages in this order: the newest at the bottom and the oldest at the top
+ for( std::list<LogBufferEntry>::reverse_iterator rit = m_LogBuffer.rbegin();
+ rit != m_LogBuffer.rend(); ++rit)
+ {
+ SetupLogEntryGUIContent(m_Content, *rit);
+
+ switch (rit->type)
+ {
+ case LogType_Error:
+ case LogType_Assert:
+ case LogType_Exception:
+ labelStyle.m_Normal.textColor = ColorRGBAf (1.0f, 0.0f, 0.0f, 1.0f); // Color.red;
+ break;
+
+ case LogType_Warning:
+ labelStyle.m_Normal.textColor = ColorRGBAf (1.0f, 0.0f, 0.0f, 1.0f); // Color.red;
+ break;
+
+ default:
+ labelStyle.m_Normal.textColor = ColorRGBAf (1.0f, 1.0f, 1.0f, 1.0f); // Color.white;
+ break;
+ }
+
+ label_pos += rit->label_height; // Label height has been previously computed in DrawBox
+
+ position.Set(0, screen_height - label_pos, half_screen_width, box_height);
+ if (IMGUI::GUIButton(guiState, position, m_Content, labelStyle))
+ {
+ rit->cond_len = -rit->cond_len;
+ }
+ }
+
+#if UNITY_WEBPLAYER_AND_STANDALONE
+ position.Set(half_screen_width, screen_height - 75, 70, 20);
+ if (DrawButton(guiState, position, m_OpenLogFileButtonTitle))
+ {
+ DeveloperConsole_OpenConsoleFile();
+ }
+#endif // UNITY_WEBPLAYER_AND_STANDALONE
+
+ position.Set(half_screen_width, screen_height - 50, 70, 20);
+ if (DrawButton(guiState, position, m_ClearButtonTitle))
+ {
+ m_LogBuffer.clear();
+ }
+
+ position.Set(half_screen_width, screen_height - 25, 70, 20);
+ if (DrawButton(guiState, position, m_CloseButtonTitle))
+ {
+ m_ConsoleClosed = true;
+ }
+
+ guiState.EndOnGUI ();
+ guiState.m_CanvasGUIState.m_GUIClipState.EndOnGUI (*guiState.m_CurrentEvent);
+
+ return guiState.m_CurrentEvent->type == InputEvent::kUsed;
+}
+
+void DeveloperConsole::AppendNewEntries()
+{
+#if SUPPORT_THREADS
+ // We already have a lock on m_ConsoleMutex for writing to m_LogBuffer
+ // We need a lock on new entries potentially added from other threads
+ Mutex::AutoLock lock(m_NewEntriesMutex);
+#endif
+
+ while (!m_NewEntries.empty())
+ {
+ m_LogBuffer.push_back(m_NewEntries.front());
+ m_NewEntries.pop_front();
+ if (m_LogBuffer.size() > kMaxNumberOfLogMessages)
+ {
+ m_LogBuffer.pop_front(); // Forget the first message
+ }
+ m_ConsoleClosed = false; // New log message, the console pops back
+ }
+}
+
+bool DeveloperConsole::DrawButton(GUIState& guiState, const Rectf& position, const UTF16String& label)
+{
+ FastUTF16StringInit(label, m_Content.m_Text);
+ return IMGUI::GUIButton(guiState, position, m_Content, *m_ButtonStyle);
+}
+
+float DeveloperConsole::DrawBox(GUIState& guiState, float height, float width)
+{
+ const GUIStyle& labelStyle = *m_LabelStyle;
+
+ // Compute how much space log messages themselves would take
+ float box_body_height = 0.f;
+ for( std::list<LogBufferEntry>::iterator it = m_LogBuffer.begin();
+ it != m_LogBuffer.end(); ++it)
+ {
+ SetupLogEntryGUIContent(m_Content, *it);
+ it->label_height = labelStyle.CalcHeight(m_Content, width);
+ box_body_height += it->label_height;
+ }
+
+ // Accomodate the box title
+ GUIStyle& boxStyle = *m_BoxStyle;
+
+ FastUTF16StringInit(m_DevelopmentConsoleBoxTitle, m_Content.m_Text);
+ float box_title_height = boxStyle.CalcHeight(m_Content, width);
+
+ // Summa summarum: the height of the developer console is its body plus its title
+ float dev_console_height = box_body_height + box_title_height;
+
+ Rectf position(0, height - dev_console_height, width, dev_console_height);
+ IMGUI::GUILabel(guiState, position, m_Content, boxStyle);
+ return box_body_height;
+}
+
+
+// Hardcoded styles (the values copied from the Editor)
+
+static GUIStyle* CreateLabelIconGUIStyle ()
+{
+ GUIStyle* style = new GUIStyle ();
+
+ style->m_Normal.textColor = ColorRGBAf (1.0f, 1.0f, 1.0f, 1.0f);//ColorRGBAf (0.0f, 0.0f, 0.0f, 1.0f);
+ style->m_Hover.textColor = ColorRGBAf (0.0f, 0.0f, 0.0f, 1.0f);
+ style->m_OnNormal.textColor = ColorRGBAf (0.0f, 0.0f, 0.0f, 1.0f);
+
+ style->m_Padding.left = 3; // so that the messages do not look glued to the left of the screen
+ style->m_Padding.right = 0;
+ style->m_Padding.top = 3;
+ style->m_Padding.bottom = 3;
+
+ style->m_RichText = true;
+ style->m_Alignment = kUpperLeft;
+ style->m_Clipping = kOverflow;
+
+ style->m_ImagePosition = kTextOnly;
+ style->m_WordWrap = true;
+
+ return style;
+}
+
+static Texture2D* GetResourceTexture(const char* texture_name)
+{
+ return reinterpret_cast<Texture2D*>(
+ GetBuiltinResourceManager ().GetResource (ClassID(Texture2D), texture_name));
+}
+
+static GUIStyle* CreateButtonGUIStyle ()
+{
+ GUIStyle* style = new GUIStyle ();
+
+ const float normal_color = 230.0f / 255.0f;
+ style->m_Normal.textColor = ColorRGBAf (normal_color, normal_color, normal_color, 1.0f);
+ style->m_Normal.background = GetResourceTexture("GameSkin/button.png");
+
+ style->m_Hover.textColor = ColorRGBAf (1.0f, 1.0f, 1.0f, 1.0f);
+ style->m_Hover.background = GetResourceTexture("GameSkin/button hover.png");
+
+ style->m_Active.textColor = style->m_Normal.textColor;
+ style->m_Active.background = GetResourceTexture("GameSkin/button active.png");
+
+ style->m_Focused.textColor = style->m_Hover.textColor;
+
+ style->m_OnNormal.textColor = style->m_Normal.textColor;
+ style->m_OnNormal.background = GetResourceTexture("GameSkin/button on.png");
+
+ style->m_OnHover.textColor = style->m_Hover.textColor;
+ style->m_OnHover.background = GetResourceTexture("GameSkin/button on hover.png");
+
+ style->m_OnActive.textColor = style->m_Normal.textColor;
+ style->m_OnActive.background = GetResourceTexture("GameSkin/button active.png");
+
+ style->m_Border.left = 6;
+ style->m_Border.right = 6;
+ style->m_Border.top = 6;
+ style->m_Border.bottom = 4;
+
+ style->m_Margin.left = 4;
+ style->m_Margin.right = 4;
+ style->m_Margin.top = 4;
+ style->m_Margin.bottom = 4;
+
+ style->m_Padding.left = 6;
+ style->m_Padding.right = 6;
+ style->m_Padding.top = 3;
+ style->m_Padding.bottom = 3;
+
+ style->m_RichText = false;
+ style->m_FontSize = 10; // make button captions a little bit smaller
+ style->m_Alignment = kMiddleCenter;
+
+ style->m_StretchWidth = true;
+ style->m_StretchHeight = false;
+
+ return style;
+}
+
+static GUIStyle* CreateBoxGUIStyle ()
+{
+ GUIStyle* style = new GUIStyle ();
+
+ const float normal_color = 204.0f / 255.0f;
+ style->m_Normal.textColor = ColorRGBAf (normal_color, normal_color, normal_color, 1.0f);
+ style->m_Normal.background = GetResourceTexture("GameSkin/box.png");
+
+ style->m_Border.left = 6;
+ style->m_Border.right = 6;
+ style->m_Border.top = 6;
+ style->m_Border.bottom = 6;
+
+ style->m_Margin.left = 4;
+ style->m_Margin.right = 4;
+ style->m_Margin.top = 4;
+ style->m_Margin.bottom = 4;
+
+ style->m_Padding.left = 4;
+ style->m_Padding.right = 4;
+ style->m_Padding.top = 4;
+ style->m_Padding.bottom = 4;
+
+ style->m_RichText = false;
+ style->m_FontStyle = 1; // The box title is bold
+ style->m_Alignment = kUpperCenter;
+
+ style->m_StretchWidth = true;
+ style->m_StretchHeight = false;
+
+ return style;
+}
+
+void DeveloperConsole::InitGUIStyles()
+{
+ m_LabelStyle = CreateLabelIconGUIStyle();
+ m_ButtonStyle = CreateButtonGUIStyle();
+ m_BoxStyle = CreateBoxGUIStyle();
+}
+
+#endif // UNITY_HAS_DEVELOPER_CONSOLE
+
+
+#if UNITY_WIN
+#include <ShellAPI.h>
+#include "PlatformDependent/Win/PathUnicodeConversion.h"
+#endif
+
+void DeveloperConsole_OpenConsoleFile ()
+{
+ std::string path = GetConsoleLogPath();
+#if UNITY_OSX
+ system (("open \""+path+"\"").c_str());
+#elif UNITY_WIN && !UNITY_WINRT
+ std::wstring widePath;
+ ConvertUnityPathName(path, widePath);
+
+ int res = (int)::ShellExecuteW( NULL, L"open", widePath.c_str(), NULL, NULL, SW_SHOWNORMAL);
+ if (res <= 32)
+ res = (int)::ShellExecuteW( NULL, L"edit", widePath.c_str(), NULL, NULL, SW_SHOWNORMAL);
+#else
+ ErrorString ("Opening Console File is not supported on this platform.");
+#endif
+}