From 15740faf9fe9fe4be08965098bbf2947e096aeeb Mon Sep 17 00:00:00 2001
From: chai <chaifix@163.com>
Date: Wed, 14 Aug 2019 22:50:43 +0800
Subject: +Unity Runtime code

---
 Runtime/Misc/DeveloperConsole.cpp | 475 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 475 insertions(+)
 create mode 100644 Runtime/Misc/DeveloperConsole.cpp

(limited to 'Runtime/Misc/DeveloperConsole.cpp')

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
+}
-- 
cgit v1.1-26-g67d0