[deploy] Merge pull request #62 from KiwanoEngine/dev

Merge dev branch
This commit is contained in:
Haibo 2020-07-17 16:19:47 +08:00 committed by GitHub
commit d1c90ed4b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 907 additions and 403 deletions

View File

@ -37,7 +37,7 @@ AudioModule::~AudioModule() {}
void AudioModule::SetupModule() void AudioModule::SetupModule()
{ {
KGE_SYS_LOG("Creating audio resources"); KGE_DEBUG_LOGF("Creating audio resources");
HRESULT hr = dlls::MediaFoundation::Get().MFStartup(MF_VERSION, MFSTARTUP_FULL); HRESULT hr = dlls::MediaFoundation::Get().MFStartup(MF_VERSION, MFSTARTUP_FULL);
@ -56,7 +56,7 @@ void AudioModule::SetupModule()
void AudioModule::DestroyModule() void AudioModule::DestroyModule()
{ {
KGE_SYS_LOG("Destroying audio resources"); KGE_DEBUG_LOGF("Destroying audio resources");
if (mastering_voice_) if (mastering_voice_)
{ {
@ -103,7 +103,7 @@ bool AudioModule::CreateSound(Sound& sound, const Transcoder::Buffer& buffer)
if (FAILED(hr)) if (FAILED(hr))
{ {
KGE_ERROR("Create IXAudio2SourceVoice failed with HRESULT of %08X", hr); KGE_ERRORF("Create IXAudio2SourceVoice failed with HRESULT of %08X", hr);
return false; return false;
} }
return true; return true;

View File

@ -65,7 +65,7 @@ bool Sound::Load(const String& file_path)
{ {
if (!FileSystem::GetInstance().IsFileExists(file_path)) if (!FileSystem::GetInstance().IsFileExists(file_path))
{ {
KGE_WARN("Media file '%s' not found", file_path.c_str()); KGE_WARNF("Media file '%s' not found", file_path.c_str());
return false; return false;
} }
@ -79,7 +79,7 @@ bool Sound::Load(const String& file_path)
HRESULT hr = transcoder_.LoadMediaFile(full_path); HRESULT hr = transcoder_.LoadMediaFile(full_path);
if (FAILED(hr)) if (FAILED(hr))
{ {
KGE_ERROR("Load media file failed with HRESULT of %08X", hr); KGE_ERRORF("Load media file failed with HRESULT of %08X", hr);
return false; return false;
} }
@ -103,7 +103,7 @@ bool Sound::Load(const Resource& res)
HRESULT hr = transcoder_.LoadMediaResource(res); HRESULT hr = transcoder_.LoadMediaResource(res);
if (FAILED(hr)) if (FAILED(hr))
{ {
KGE_ERROR("Load media resource failed with HRESULT of %08X", hr); KGE_ERRORF("Load media resource failed with HRESULT of %08X", hr);
return false; return false;
} }
@ -126,7 +126,7 @@ void Sound::Play(int loop_count)
{ {
if (!opened_) if (!opened_)
{ {
KGE_ERROR("Sound must be opened first!"); KGE_ERRORF("Sound must be opened first!");
return; return;
} }
@ -157,7 +157,7 @@ void Sound::Play(int loop_count)
if (FAILED(hr)) if (FAILED(hr))
{ {
KGE_ERROR("Submitting source buffer failed with HRESULT of %08X", hr); KGE_ERRORF("Submitting source buffer failed with HRESULT of %08X", hr);
} }
playing_ = SUCCEEDED(hr); playing_ = SUCCEEDED(hr);

View File

@ -105,7 +105,7 @@ HRESULT Transcoder::LoadMediaResource(const Resource& res)
if (stream == nullptr) if (stream == nullptr)
{ {
KGE_ERROR("SHCreateMemStream failed"); KGE_ERRORF("SHCreateMemStream failed");
return E_OUTOFMEMORY; return E_OUTOFMEMORY;
} }
@ -198,7 +198,7 @@ HRESULT Transcoder::ReadSource(IMFSourceReader* reader)
if (data == nullptr) if (data == nullptr)
{ {
KGE_ERROR("Low memory"); KGE_ERRORF("Low memory");
hr = E_OUTOFMEMORY; hr = E_OUTOFMEMORY;
} }
else else

View File

@ -298,7 +298,7 @@ void HttpModule::Perform(HttpRequestPtr request, HttpResponsePtr response)
ok = Curl::DeleteRequest(this, headers, url, &response_code, &response_data, &response_header, error_message); ok = Curl::DeleteRequest(this, headers, url, &response_code, &response_data, &response_header, error_message);
break; break;
default: default:
KGE_ERROR("HttpModule: unknown request type, only GET, POST, PUT or DELETE is supported"); KGE_ERRORF("HttpModule: unknown request type, only GET, POST, PUT or DELETE is supported");
return; return;
} }

View File

@ -535,7 +535,7 @@ void Actor::AddChild(ActorPtr child, int zorder)
{ {
if (parent == child) if (parent == child)
{ {
KGE_ERROR("A actor cannot be its own parent"); KGE_ERRORF("A actor cannot be its own parent");
return; return;
} }
} }

View File

@ -263,12 +263,20 @@ public:
void SetPositionY(float y); void SetPositionY(float y);
/// \~chinese /// \~chinese
/// @brief 移动坐标 /// @brief 移动坐标
void Move(const Vec2& v); void MoveTo(const Point& p);
/// \~chinese /// \~chinese
/// @brief 移动坐标 /// @brief 移动至坐标
void Move(float vx, float vy); void MoveTo(float x, float y);
/// \~chinese
/// @brief 移动相对坐标
void MoveBy(const Vec2& trans);
/// \~chinese
/// @brief 移动相对坐标
void MoveBy(float trans_x, float trans_y);
/// \~chinese /// \~chinese
/// @brief 设置缩放比例,默认为 (1.0, 1.0) /// @brief 设置缩放比例,默认为 (1.0, 1.0)
@ -725,14 +733,24 @@ inline void Actor::SetPositionY(float y)
this->SetPosition(Point(transform_.position.x, y)); this->SetPosition(Point(transform_.position.x, y));
} }
inline void Actor::Move(const Vec2& v) inline void Actor::MoveTo(const Point& p)
{ {
this->SetPosition(transform_.position.x + v.x, transform_.position.y + v.y); this->SetPosition(p);
} }
inline void Actor::Move(float vx, float vy) inline void Actor::MoveTo(float x, float y)
{ {
this->Move(Vec2(vx, vy)); this->SetPosition(Point(x, y));
}
inline void Actor::MoveBy(const Vec2& trans)
{
this->SetPosition(transform_.position.x + trans.x, transform_.position.y + trans.y);
}
inline void Actor::MoveBy(float trans_x, float trans_y)
{
this->MoveBy(Vec2(trans_x, trans_y));
} }
inline void Actor::SetScale(float scalex, float scaley) inline void Actor::SetScale(float scalex, float scaley)

View File

@ -43,12 +43,12 @@ Stage::~Stage() {}
void Stage::OnEnter() void Stage::OnEnter()
{ {
KGE_SYS_LOG("Stage entered"); KGE_DEBUG_LOGF("Stage entered");
} }
void Stage::OnExit() void Stage::OnExit()
{ {
KGE_SYS_LOG("Stage exited"); KGE_DEBUG_LOGF("Stage exited");
} }
void Stage::RenderBorder(RenderContext& ctx) void Stage::RenderBorder(RenderContext& ctx)

View File

@ -25,24 +25,24 @@
namespace kiwano namespace kiwano
{ {
ActionGroupPtr ActionGroup::Create(const Vector<ActionPtr>& actions, bool sync) ActionGroupPtr ActionGroup::Create(const Vector<ActionPtr>& actions, bool parallel)
{ {
ActionGroupPtr ptr = memory::New<ActionGroup>(); ActionGroupPtr ptr = memory::New<ActionGroup>();
if (ptr) if (ptr)
{ {
ptr->sync_ = sync; ptr->parallel_ = parallel;
ptr->AddActions(actions); ptr->AddActions(actions);
} }
return ptr; return ptr;
} }
ActionGroup::ActionGroup() ActionGroup::ActionGroup()
: sync_(false) : parallel_(false)
{ {
} }
ActionGroup::ActionGroup(bool sync) ActionGroup::ActionGroup(bool parallel)
: sync_(sync) : parallel_(parallel)
{ {
} }
@ -56,7 +56,7 @@ void ActionGroup::Init(Actor* target)
return; return;
} }
if (sync_) if (parallel_)
{ {
// init all actions // init all actions
for (current_ = actions_.GetFirst(); current_; current_ = current_->GetNext()) for (current_ = actions_.GetFirst(); current_; current_ = current_->GetNext())
@ -73,7 +73,7 @@ void ActionGroup::Init(Actor* target)
void ActionGroup::Update(Actor* target, Duration dt) void ActionGroup::Update(Actor* target, Duration dt)
{ {
if (!sync_) if (!parallel_)
{ {
if (current_) if (current_)
{ {
@ -133,7 +133,7 @@ ActionPtr ActionGroup::Clone() const
actions.push_back(action->Clone()); actions.push_back(action->Clone());
} }
} }
return DoClone(ActionGroup::Create(actions, sync_)); return DoClone(ActionGroup::Create(actions, parallel_));
} }
ActionPtr ActionGroup::Reverse() const ActionPtr ActionGroup::Reverse() const
@ -146,7 +146,7 @@ ActionPtr ActionGroup::Reverse() const
actions.push_back(action->Reverse()); actions.push_back(action->Reverse());
} }
} }
return DoClone(ActionGroup::Create(actions, sync_)); return DoClone(ActionGroup::Create(actions, parallel_));
} }
} // namespace kiwano } // namespace kiwano

View File

@ -40,12 +40,12 @@ public:
/// \~chinese /// \~chinese
/// @brief 创建动画组合 /// @brief 创建动画组合
/// @param actions 动画集合 /// @param actions 动画集合
/// @param sync 同步执行 /// @param parallel 同步执行
static ActionGroupPtr Create(const Vector<ActionPtr>& actions, bool sync = false); static ActionGroupPtr Create(const Vector<ActionPtr>& actions, bool parallel = false);
ActionGroup(); ActionGroup();
ActionGroup(bool sync); ActionGroup(bool parallel);
virtual ~ActionGroup(); virtual ~ActionGroup();
@ -77,7 +77,7 @@ protected:
void Update(Actor* target, Duration dt) override; void Update(Actor* target, Duration dt) override;
private: private:
bool sync_; bool parallel_;
ActionPtr current_; ActionPtr current_;
ActionList actions_; ActionList actions_;
}; };

View File

@ -359,10 +359,10 @@ public:
/// \~chinese /// \~chinese
/// @brief 动画组合 /// @brief 动画组合
/// @param actions 动画集合 /// @param actions 动画集合
/// @param sync 同步执行 /// @param parallel 同步执行
static inline ActionHelper Group(const Vector<ActionPtr>& actions, bool sync = false) static inline ActionHelper Group(const Vector<ActionPtr>& actions, bool parallel = false)
{ {
return ActionHelper(ActionGroup::Create(actions, sync)); return ActionHelper(ActionGroup::Create(actions, parallel));
} }
}; };

View File

@ -195,7 +195,7 @@ public:
/// @brief 获取该动画的倒转 /// @brief 获取该动画的倒转
virtual ActionPtr Reverse() const override virtual ActionPtr Reverse() const override
{ {
KGE_ERROR("Reverse() not supported in ActionMoveTo"); KGE_ERRORF("Reverse() not supported in ActionMoveTo");
return nullptr; return nullptr;
} }
@ -299,7 +299,7 @@ public:
/// @brief 获取该动画的倒转 /// @brief 获取该动画的倒转
virtual ActionPtr Reverse() const override virtual ActionPtr Reverse() const override
{ {
KGE_ERROR("Reverse() not supported in ActionJumpTo"); KGE_ERRORF("Reverse() not supported in ActionJumpTo");
return nullptr; return nullptr;
} }
@ -398,7 +398,7 @@ public:
/// @brief 获取该动画的倒转 /// @brief 获取该动画的倒转
virtual ActionPtr Reverse() const override virtual ActionPtr Reverse() const override
{ {
KGE_ERROR("Reverse() not supported in ActionScaleTo"); KGE_ERRORF("Reverse() not supported in ActionScaleTo");
return nullptr; return nullptr;
} }
@ -439,7 +439,7 @@ public:
/// @brief 获取该动画的倒转 /// @brief 获取该动画的倒转
virtual ActionPtr Reverse() const override virtual ActionPtr Reverse() const override
{ {
KGE_ERROR("Reverse() not supported in ActionFadeTo"); KGE_ERRORF("Reverse() not supported in ActionFadeTo");
return nullptr; return nullptr;
} }
@ -544,7 +544,7 @@ public:
/// @brief 获取该动画的倒转 /// @brief 获取该动画的倒转
virtual ActionPtr Reverse() const override virtual ActionPtr Reverse() const override
{ {
KGE_ERROR("Reverse() not supported in ActionRotateTo"); KGE_ERRORF("Reverse() not supported in ActionRotateTo");
return nullptr; return nullptr;
} }
@ -589,7 +589,7 @@ public:
/// @brief 获取该动画的倒转 /// @brief 获取该动画的倒转
ActionPtr Reverse() const override ActionPtr Reverse() const override
{ {
KGE_ERROR("Reverse() not supported in ActionCustom"); KGE_ERRORF("Reverse() not supported in ActionCustom");
return nullptr; return nullptr;
} }

View File

@ -122,13 +122,13 @@ void ObjectBase::StopTracingLeaks()
void ObjectBase::DumpTracingObjects() void ObjectBase::DumpTracingObjects()
{ {
KGE_SYS_LOG("-------------------------- All Objects --------------------------"); KGE_DEBUG_LOGF("-------------------------- All Objects --------------------------");
for (const auto object : tracing_objects) for (const auto object : tracing_objects)
{ {
KGE_SYS_LOG("{ class=\"%s\" id=%d refcount=%d name=\"%s\" }", typeid(*object).name(), object->GetObjectID(), KGE_DEBUG_LOGF("{ class=\"%s\" id=%d refcount=%d name=\"%s\" }", typeid(*object).name(), object->GetObjectID(),
object->GetRefCount(), object->GetName().c_str()); object->GetRefCount(), object->GetName().c_str());
} }
KGE_SYS_LOG("------------------------- Total size: %d -------------------------", tracing_objects.size()); KGE_DEBUG_LOGF("------------------------- Total size: %d -------------------------", tracing_objects.size());
} }
Vector<ObjectBase*>& ObjectBase::GetTracingObjects() Vector<ObjectBase*>& ObjectBase::GetTracingObjects()

View File

@ -9,9 +9,9 @@
//#define KGE_ASSERT(EXPR) __noop // Disable asserts //#define KGE_ASSERT(EXPR) __noop // Disable asserts
//---- Define debug-output handler. Defaults to calling kiwano::logs::Messageln()/Warningln()/Errorln() //---- Define debug-output handler. Defaults to calling kiwano::logs::Messageln()/Warningln()/Errorln()
//#define KGE_SYS_LOG(FORMAT, ...) wprintf(FORMAT "\n", __VA_ARGS__) //#define KGE_LOGF(FORMAT, ...) printf(FORMAT "\n", __VA_ARGS__)
//#define KGE_WARN(FORMAT, ...) wprintf(FORMAT "\n", __VA_ARGS__) //#define KGE_WARNF(FORMAT, ...) printf(FORMAT "\n", __VA_ARGS__)
//#define KGE_ERROR(FORMAT, ...) wprintf(FORMAT "\n", __VA_ARGS__) //#define KGE_ERRORF(FORMAT, ...) printf(FORMAT "\n", __VA_ARGS__)
//---- Define attributes of all API symbols declarations for DLL //---- Define attributes of all API symbols declarations for DLL
//#define KGE_USE_DLL //#define KGE_USE_DLL

View File

@ -181,7 +181,7 @@ DbgHelp g_DbgHelp;
void PrintErrorCode(LPCSTR lpszFunction) void PrintErrorCode(LPCSTR lpszFunction)
{ {
KGE_ERROR("%s failed with HRESULT of %08X", lpszFunction, HRESULT_FROM_WIN32(GetLastError())); KGE_ERRORF("%s failed with HRESULT of %08X", lpszFunction, HRESULT_FROM_WIN32(GetLastError()));
} }
void PrintCallStackOnContext(PCONTEXT pContext) void PrintCallStackOnContext(PCONTEXT pContext)
@ -227,7 +227,7 @@ void PrintCallStackOnContext(PCONTEXT pContext)
constexpr int STACKWALK_MAX_NAMELEN = 1024; constexpr int STACKWALK_MAX_NAMELEN = 1024;
BYTE symbolBuffer[sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN]; BYTE symbolBuffer[sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN];
KGE_ERROR("========== Stack trace =========="); KGE_ERRORF("========== Stack trace ==========");
while (true) while (true)
{ {
@ -263,11 +263,11 @@ void PrintCallStackOnContext(PCONTEXT pContext)
DWORD dwLineDisplacement; DWORD dwLineDisplacement;
if (g_DbgHelp.SymGetLineFromAddr64(hProcess, sf.AddrPC.Offset, &dwLineDisplacement, &lineInfo)) if (g_DbgHelp.SymGetLineFromAddr64(hProcess, sf.AddrPC.Offset, &dwLineDisplacement, &lineInfo))
{ {
KGE_ERROR("%s (%d): %s", lineInfo.FileName, lineInfo.LineNumber, pSymbol->Name); KGE_ERRORF("%s (%d): %s", lineInfo.FileName, lineInfo.LineNumber, pSymbol->Name);
} }
else else
{ {
KGE_ERROR("(filename not available): %s", pSymbol->Name); KGE_ERRORF("(filename not available): %s", pSymbol->Name);
} }
if (sf.AddrReturn.Offset == 0) if (sf.AddrReturn.Offset == 0)

View File

@ -27,7 +27,7 @@
#define KGE_THROW(MESSAGE) \ #define KGE_THROW(MESSAGE) \
do \ do \
{ \ { \
KGE_ERROR("An exception occurred: %s", MESSAGE); \ KGE_ERRORF("An exception occurred: %s", MESSAGE); \
kiwano::StackTracer().Print(); \ kiwano::StackTracer().Print(); \
throw kiwano::RuntimeError(MESSAGE); \ throw kiwano::RuntimeError(MESSAGE); \
} while (0) } while (0)
@ -35,7 +35,7 @@
#define KGE_THROW_SYSTEM_ERROR(ERRCODE, MESSAGE) \ #define KGE_THROW_SYSTEM_ERROR(ERRCODE, MESSAGE) \
do \ do \
{ \ { \
KGE_ERROR("An exception occurred (%#x): %s", ERRCODE, MESSAGE); \ KGE_ERRORF("An exception occurred (%#x): %s", ERRCODE, MESSAGE); \
kiwano::StackTracer().Print(); \ kiwano::StackTracer().Print(); \
throw kiwano::SystemError(std::error_code(kiwano::error_enum(ERRCODE)), MESSAGE); \ throw kiwano::SystemError(std::error_code(kiwano::error_enum(ERRCODE)), MESSAGE); \
} while (0) } while (0)

View File

@ -48,28 +48,28 @@ Resource::Data Resource::GetData() const
HRSRC res_info = FindResourceA(nullptr, MAKEINTRESOURCEA(id_), type_.data()); HRSRC res_info = FindResourceA(nullptr, MAKEINTRESOURCEA(id_), type_.data());
if (res_info == nullptr) if (res_info == nullptr)
{ {
KGE_ERROR("FindResource failed"); KGE_ERRORF("FindResource failed");
break; break;
} }
HGLOBAL res_data = LoadResource(nullptr, res_info); HGLOBAL res_data = LoadResource(nullptr, res_info);
if (res_data == nullptr) if (res_data == nullptr)
{ {
KGE_ERROR("LoadResource failed"); KGE_ERRORF("LoadResource failed");
break; break;
} }
DWORD size = SizeofResource(nullptr, res_info); DWORD size = SizeofResource(nullptr, res_info);
if (size == 0) if (size == 0)
{ {
KGE_ERROR("SizeofResource failed"); KGE_ERRORF("SizeofResource failed");
break; break;
} }
LPVOID buffer = LockResource(res_data); LPVOID buffer = LockResource(res_data);
if (buffer == nullptr) if (buffer == nullptr)
{ {
KGE_ERROR("LockResource failed"); KGE_ERRORF("LockResource failed");
break; break;
} }

View File

@ -30,39 +30,53 @@ namespace strings
#if defined(KGE_PLATFORM_WINDOWS) #if defined(KGE_PLATFORM_WINDOWS)
String Format(const char* format, ...) String Format(const char* format, ...)
{
va_list args = nullptr;
va_start(args, format);
String result = FormatArgs(format, args);
va_end(args);
return result;
}
WideString Format(const wchar_t* format, ...)
{
va_list args = nullptr;
va_start(args, format);
WideString result = FormatArgs(format, args);
va_end(args);
return result;
}
String FormatArgs(const char* format, va_list args)
{ {
String result; String result;
if (format) if (format)
{ {
va_list args = nullptr;
va_start(args, format);
const auto len = static_cast<size_t>(::_vscprintf(format, args) + 1); const auto len = static_cast<size_t>(::_vscprintf(format, args) + 1);
if (len) if (len)
{ {
result.resize(len - 1); result.resize(len - 1);
::_vsnprintf_s(&result[0], len, len, format, args); ::_vsnprintf_s(&result[0], len, len, format, args);
} }
va_end(args);
} }
return result; return result;
} }
WideString Format(const wchar_t* format, ...) WideString FormatArgs(const wchar_t* format, va_list args)
{ {
WideString result; WideString result;
if (format) if (format)
{ {
va_list args = nullptr;
va_start(args, format);
const auto len = static_cast<size_t>(::_vscwprintf(format, args) + 1); const auto len = static_cast<size_t>(::_vscwprintf(format, args) + 1);
if (len) if (len)
{ {
result.resize(len - 1); result.resize(len - 1);
::_vsnwprintf_s(&result[0], len, len, format, args); ::_vsnwprintf_s(&result[0], len, len, format, args);
} }
va_end(args);
} }
return result; return result;
} }

View File

@ -50,6 +50,14 @@ String Format(const char* format, ...);
/// @brief ¸ñʽ»¯×Ö·û´® /// @brief ¸ñʽ»¯×Ö·û´®
WideString Format(const wchar_t* format, ...); WideString Format(const wchar_t* format, ...);
/// \~chinese
/// @brief ¸ñʽ»¯×Ö·û´®
String FormatArgs(const char* format, va_list args);
/// \~chinese
/// @brief ¸ñʽ»¯×Ö·û´®
WideString FormatArgs(const wchar_t* format, va_list args);
/// \~chinese /// \~chinese
/// @brief ¿í×Ö·û´®×ªÕ­×Ö·û´® /// @brief ¿í×Ö·û´®×ªÕ­×Ö·û´®
String WideToNarrow(const WideString& str); String WideToNarrow(const WideString& str);

View File

@ -606,7 +606,7 @@ LRESULT WindowWin32Impl::MessageProc(HWND hwnd, UINT32 msg, WPARAM wparam, LPARA
{ {
if (SIZE_MAXHIDE == wparam || SIZE_MINIMIZED == wparam) if (SIZE_MAXHIDE == wparam || SIZE_MINIMIZED == wparam)
{ {
KGE_SYS_LOG("Window minimized"); KGE_DEBUG_LOGF("Window minimized");
is_minimized_ = true; is_minimized_ = true;
// Pause game when window is minimized // Pause game when window is minimized
@ -614,7 +614,7 @@ LRESULT WindowWin32Impl::MessageProc(HWND hwnd, UINT32 msg, WPARAM wparam, LPARA
} }
else if (SIZE_MAXIMIZED == wparam) else if (SIZE_MAXIMIZED == wparam)
{ {
KGE_SYS_LOG("Window maximized"); KGE_DEBUG_LOGF("Window maximized");
if (is_minimized_) if (is_minimized_)
{ {
@ -626,7 +626,7 @@ LRESULT WindowWin32Impl::MessageProc(HWND hwnd, UINT32 msg, WPARAM wparam, LPARA
{ {
if (is_minimized_) if (is_minimized_)
{ {
KGE_SYS_LOG("Window restored"); KGE_DEBUG_LOGF("Window restored");
// the window was restored and was previously minimized // the window was restored and was previously minimized
is_minimized_ = false; is_minimized_ = false;
@ -646,7 +646,7 @@ LRESULT WindowWin32Impl::MessageProc(HWND hwnd, UINT32 msg, WPARAM wparam, LPARA
evt->height = this->GetHeight(); evt->height = this->GetHeight();
this->PushEvent(evt); this->PushEvent(evt);
KGE_SYS_LOG("Window resized to (%d, %d)", this->width_, this->height_); KGE_DEBUG_LOGF("Window resized to (%d, %d)", this->width_, this->height_);
} }
} }
} }
@ -672,7 +672,7 @@ LRESULT WindowWin32Impl::MessageProc(HWND hwnd, UINT32 msg, WPARAM wparam, LPARA
uint32_t client_height = uint32_t(client_rect.bottom - client_rect.top); uint32_t client_height = uint32_t(client_rect.bottom - client_rect.top);
if (client_width != this->GetWidth() || client_height != this->GetHeight()) if (client_width != this->GetWidth() || client_height != this->GetHeight())
{ {
KGE_SYS_LOG("Window resized to (%d, %d)", client_width, client_height); KGE_DEBUG_LOGF("Window resized to (%d, %d)", client_width, client_height);
this->width_ = client_width; this->width_ = client_width;
this->height_ = client_height; this->height_ = client_height;
@ -744,7 +744,7 @@ LRESULT WindowWin32Impl::MessageProc(HWND hwnd, UINT32 msg, WPARAM wparam, LPARA
case WM_SETTEXT: case WM_SETTEXT:
{ {
KGE_SYS_LOG("Window title changed"); KGE_DEBUG_LOGF("Window title changed");
this->title_ = strings::WideToNarrow(reinterpret_cast<LPCWSTR>(lparam)); this->title_ = strings::WideToNarrow(reinterpret_cast<LPCWSTR>(lparam));
@ -756,13 +756,13 @@ LRESULT WindowWin32Impl::MessageProc(HWND hwnd, UINT32 msg, WPARAM wparam, LPARA
case WM_SETICON: case WM_SETICON:
{ {
KGE_SYS_LOG("Window icon changed"); KGE_DEBUG_LOGF("Window icon changed");
} }
break; break;
case WM_DISPLAYCHANGE: case WM_DISPLAYCHANGE:
{ {
KGE_SYS_LOG("The display resolution has changed"); KGE_DEBUG_LOGF("The display resolution has changed");
} }
break; break;
@ -774,7 +774,7 @@ LRESULT WindowWin32Impl::MessageProc(HWND hwnd, UINT32 msg, WPARAM wparam, LPARA
case WM_CLOSE: case WM_CLOSE:
{ {
KGE_SYS_LOG("Window is closing"); KGE_DEBUG_LOGF("Window is closing");
WindowClosedEventPtr evt = new WindowClosedEvent; WindowClosedEventPtr evt = new WindowClosedEvent;
this->PushEvent(evt); this->PushEvent(evt);
@ -785,7 +785,7 @@ LRESULT WindowWin32Impl::MessageProc(HWND hwnd, UINT32 msg, WPARAM wparam, LPARA
case WM_DESTROY: case WM_DESTROY:
{ {
KGE_SYS_LOG("Window was destroyed"); KGE_DEBUG_LOGF("Window was destroyed");
::PostQuitMessage(0); ::PostQuitMessage(0);
return 0; return 0;

View File

@ -155,7 +155,7 @@ void RenderContextImpl::DrawTextLayout(const TextLayout& layout, const Point& of
} }
else else
{ {
KGE_ERROR("Failed to draw text layout with HRESULT of %08X", hr); KGE_ERRORF("Failed to draw text layout with HRESULT of %08X", hr);
} }
} }
} }

View File

@ -51,7 +51,7 @@ RendererImpl::RendererImpl()
void RendererImpl::MakeContextForWindow(WindowPtr window) void RendererImpl::MakeContextForWindow(WindowPtr window)
{ {
KGE_SYS_LOG("Creating device resources"); KGE_DEBUG_LOGF("Creating device resources");
KGE_THROW_IF_FAILED(::CoInitialize(nullptr), "CoInitialize failed"); KGE_THROW_IF_FAILED(::CoInitialize(nullptr), "CoInitialize failed");
@ -143,7 +143,7 @@ void RendererImpl::MakeContextForWindow(WindowPtr window)
void RendererImpl::Destroy() void RendererImpl::Destroy()
{ {
KGE_SYS_LOG("Destroying device resources"); KGE_DEBUG_LOGF("Destroying device resources");
if (d2d_res_) if (d2d_res_)
{ {
@ -194,7 +194,7 @@ void RendererImpl::CreateTexture(Texture& texture, const String& file_path)
if (!FileSystem::GetInstance().IsFileExists(file_path)) if (!FileSystem::GetInstance().IsFileExists(file_path))
{ {
KGE_WARN("Texture file '%s' not found!", file_path.c_str()); KGE_WARNF("Texture file '%s' not found!", file_path.c_str());
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
} }
@ -291,7 +291,7 @@ void RendererImpl::CreateTexture(Texture& texture, const Resource& resource)
if (FAILED(hr)) if (FAILED(hr))
{ {
KGE_WARN("Load texture failed with HRESULT of %08X!", hr); KGE_WARNF("Load texture failed with HRESULT of %08X!", hr);
} }
} }
@ -305,7 +305,7 @@ void RendererImpl::CreateGifImage(GifImage& gif, const String& file_path)
if (!FileSystem::GetInstance().IsFileExists(file_path)) if (!FileSystem::GetInstance().IsFileExists(file_path))
{ {
KGE_WARN("Gif texture file '%s' not found!", file_path.c_str()); KGE_WARNF("Gif texture file '%s' not found!", file_path.c_str());
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
} }
@ -324,7 +324,7 @@ void RendererImpl::CreateGifImage(GifImage& gif, const String& file_path)
if (FAILED(hr)) if (FAILED(hr))
{ {
KGE_WARN("Load GIF texture failed with HRESULT of %08X!", hr); KGE_WARNF("Load GIF texture failed with HRESULT of %08X!", hr);
} }
} }
@ -356,7 +356,7 @@ void RendererImpl::CreateGifImage(GifImage& gif, const Resource& resource)
if (FAILED(hr)) if (FAILED(hr))
{ {
KGE_WARN("Load GIF texture failed with HRESULT of %08X!", hr); KGE_WARNF("Load GIF texture failed with HRESULT of %08X!", hr);
} }
} }
@ -520,7 +520,7 @@ void RendererImpl::CreateGifImageFrame(GifImage::Frame& frame, const GifImage& g
if (FAILED(hr)) if (FAILED(hr))
{ {
KGE_WARN("Load GIF frame failed with HRESULT of %08X!", hr); KGE_WARNF("Load GIF frame failed with HRESULT of %08X!", hr);
} }
} }
@ -536,7 +536,7 @@ void RendererImpl::CreateFontCollection(Font& font, const String& file_path)
{ {
if (!FileSystem::GetInstance().IsFileExists(file_path)) if (!FileSystem::GetInstance().IsFileExists(file_path))
{ {
KGE_WARN("Font file '%s' not found!", file_path.c_str()); KGE_WARNF("Font file '%s' not found!", file_path.c_str());
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
} }
} }

View File

@ -19,10 +19,563 @@
// THE SOFTWARE. // THE SOFTWARE.
#include <ctime> #include <ctime>
#include <ios>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <kiwano/utils/Logger.h> #include <kiwano/utils/Logger.h>
namespace kiwano
{
//
// LogFormater
//
String LogFormater::GetLevelLabel(LogLevel level) const
{
switch (level)
{
case kiwano::LogLevel::Debug:
return "[Debug]";
case kiwano::LogLevel::Info:
return "[Info]";
case kiwano::LogLevel::Notice:
return "[Notice]";
case kiwano::LogLevel::Warning:
return "[Warning]";
case kiwano::LogLevel::Error:
return "[Error]";
default:
break;
}
return String();
}
class TextFormater : public LogFormater
{
public:
void Format(std::iostream& out, LogLevel level, Time time, std::streambuf* raw_msg) override
{
// get timestamp
time_t unix = std::time(nullptr);
std::tm tmbuf;
localtime_s(&tmbuf, &unix);
// build message
out << GetLevelLabel(level) << std::put_time(&tmbuf, " %H:%M:%S ");
if (raw_msg->sgetc() != std::char_traits<char>::eof())
out << raw_msg;
out << "\n";
}
};
//
// LogProvider
//
LogProvider::~LogProvider() {}
#if defined(KGE_PLATFORM_WINDOWS)
void SetWindowConsoleColor(std::ostream& os, int foreground, int background)
{
static WORD defaultAttributes = 0;
// get terminal handle
HANDLE hTerminal = INVALID_HANDLE_VALUE;
if (&os == &std::cout)
hTerminal = GetStdHandle(STD_OUTPUT_HANDLE);
else if (&os == &std::cerr)
hTerminal = GetStdHandle(STD_ERROR_HANDLE);
if (hTerminal == INVALID_HANDLE_VALUE)
return;
// save default terminal attributes if it unsaved
if (!defaultAttributes)
{
CONSOLE_SCREEN_BUFFER_INFO info;
if (!GetConsoleScreenBufferInfo(hTerminal, &info))
return;
defaultAttributes = info.wAttributes;
}
// restore all default settings
if (foreground == -1 && background == -1)
{
SetConsoleTextAttribute(hTerminal, defaultAttributes);
return;
}
// get current settings
CONSOLE_SCREEN_BUFFER_INFO info;
if (!GetConsoleScreenBufferInfo(hTerminal, &info))
return;
if (foreground != -1)
{
info.wAttributes &= ~(info.wAttributes & 0x0F);
info.wAttributes |= static_cast<WORD>(foreground);
}
if (background != -1)
{
info.wAttributes &= ~(info.wAttributes & 0xF0);
info.wAttributes |= static_cast<WORD>(background);
}
SetConsoleTextAttribute(hTerminal, info.wAttributes);
}
#endif
template <int color>
std::ostream& ConsoleColorBrush(std::ostream& os)
{
#if defined(KGE_PLATFORM_WINDOWS)
if (color > 0)
{
switch (color)
{
case 31: // red
SetWindowConsoleColor(os, FOREGROUND_RED | FOREGROUND_INTENSITY, -1);
break;
case 32: // green
SetWindowConsoleColor(os, FOREGROUND_GREEN | FOREGROUND_INTENSITY, -1);
break;
case 33: // yellow
SetWindowConsoleColor(os, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, -1);
break;
case 34: // blue
SetWindowConsoleColor(os, FOREGROUND_BLUE | FOREGROUND_INTENSITY, -1);
break;
case 36: // cyan
SetWindowConsoleColor(os, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY, -1);
break;
default:
break;
}
}
else if (color < 0)
{
SetWindowConsoleColor(os, -1, -1);
}
#else
if (color > 0)
{
os << "\033[1;" << color << "m";
}
else if (color < 0)
{
os << "\033[0m";
}
#endif
return os;
}
LogProviderPtr ConsoleLogProvider::Create()
{
LogProviderPtr ptr = new ConsoleLogProvider;
return ptr;
}
ConsoleLogProvider::~ConsoleLogProvider()
{
Flush();
}
void ConsoleLogProvider::Init()
{
}
void ConsoleLogProvider::WriteMessage(LogLevel level, LogBuffer* msg)
{
if (level != LogLevel::Error)
std::cout << GetColor(level) << msg << std::flush << ConsoleColorBrush<-1>;
else
std::cerr << GetColor(level) << msg << ConsoleColorBrush<-1>;
#if defined(KGE_PLATFORM_WINDOWS)
::OutputDebugStringA(msg->GetRaw());
#endif
}
void ConsoleLogProvider::Flush()
{
std::cout.flush();
std::cout.clear();
}
ConsoleLogProvider::ConsoleColor ConsoleLogProvider::GetColor(LogLevel level)
{
std::initializer_list<ConsoleColor> colors = {
ConsoleColorBrush<34>, // Debug Blue
ConsoleColorBrush<0>, // Info Default
ConsoleColorBrush<32>, // Notice Green
ConsoleColorBrush<33>, // Warn Yellow
ConsoleColorBrush<31>, // Error Red
};
if (size_t(level) < colors.size())
return *std::next(colors.begin(), ptrdiff_t(level));
return ConsoleColorBrush<0>;
}
LogProviderPtr FileLogProvider::Create(const String& filepath, std::ios_base::openmode mode)
{
SmartPtr<FileLogProvider> ptr = new FileLogProvider;
if (ptr)
{
ptr->ofs_.open(filepath, mode);
}
return ptr;
}
FileLogProvider::~FileLogProvider()
{
if (ofs_.is_open())
ofs_.close();
}
void FileLogProvider::Init() {}
void FileLogProvider::WriteMessage(LogLevel level, LogBuffer* msg)
{
if (ofs_)
{
ofs_ << msg << std::flush;
}
}
void FileLogProvider::Flush()
{
if (ofs_)
{
ofs_.flush();
ofs_.clear();
}
}
//
// LogBuffer
//
LogBuffer::LogBuffer(size_t buffer_size)
: buf_(buffer_size)
, seek_high_(nullptr)
{
Reset();
}
void LogBuffer::Resize(size_t size)
{
if (buf_.size() < size)
{
buf_.resize(size);
}
}
void LogBuffer::Reset()
{
const auto begin = buf_.data();
const auto size = buf_.size();
this->setp(begin, begin + size);
this->setg(begin, begin, begin);
seek_high_ = nullptr;
}
const char* LogBuffer::GetRaw() const
{
const auto pptr = this->pptr();
if (!pptr)
return "";
const auto data = buf_.data();
const auto size = buf_.size();
if (pptr == data)
return "";
if (pptr > data && pptr < data + size)
{
*pptr = '\0';
return data;
}
return "";
}
LogBuffer::int_type LogBuffer::overflow(int_type ch)
{
if (traits_type::eq_int_type(ch, traits_type::eof()))
return traits_type::not_eof(ch); // EOF, return success
const auto pptr = this->pptr();
if (pptr)
return traits_type::eof();
const auto epptr = this->epptr();
if (pptr < epptr)
{
seek_high_ = pptr + 1;
return ch;
}
const auto old_ptr = buf_.data();
const auto old_size = pptr - old_ptr;
size_t new_size = 0;
if (old_size < INT_MAX / 2)
new_size = old_size << 1;
else if (old_size < INT_MAX)
new_size = INT_MAX;
else
return traits_type::eof(); // buffer can't grow, fail
// grow
buf_.resize(new_size);
const auto new_ptr = buf_.data();
const auto new_pnext = new_ptr + old_size;
seek_high_ = new_pnext + 1;
this->setp(new_ptr, new_pnext, new_ptr + new_size);
this->setg(new_ptr, new_ptr + (this->gptr() - old_ptr), seek_high_);
return ch;
}
LogBuffer::int_type LogBuffer::underflow()
{
const auto gptr = this->gptr();
if (!gptr)
return traits_type::eof();
if (gptr < this->egptr())
return traits_type::to_int_type(*gptr);
const auto pptr = this->pptr();
if (!pptr)
return traits_type::eof();
const auto high = std::max(seek_high_, pptr);
if (high <= gptr)
return traits_type::eof();
seek_high_ = high;
this->setg(this->eback(), gptr, high);
return traits_type::to_int_type(*gptr);
}
LogBuffer::pos_type LogBuffer::seekpos(pos_type pos, std::ios_base::openmode mode)
{
const auto offset = static_cast<std::streamoff>(pos);
const auto old_gptr = this->gptr();
const auto olg_pptr = this->pptr();
if (olg_pptr && seek_high_ < olg_pptr)
{
seek_high_ = olg_pptr;
}
const auto seek_low = this->eback();
const auto seek_dist = seek_high_ - seek_low;
if (static_cast<unsigned long long>(offset) > static_cast<unsigned long long>(seek_dist))
{
return pos_type(off_type(-1));
}
if (offset != 0 && (((mode & std::ios_base::in) && !old_gptr) || ((mode & std::ios_base::out) && !olg_pptr)))
{
return pos_type(off_type(-1));
}
const auto new_ptr = seek_low + offset;
if ((mode & std::ios_base::in) && old_gptr)
{
this->setg(seek_low, new_ptr, seek_high_);
}
if ((mode & std::ios_base::out) && olg_pptr)
{
this->setp(seek_low, new_ptr, this->epptr());
}
return pos_type(offset);
}
LogBuffer::pos_type LogBuffer::seekoff(off_type offset, std::ios_base::seekdir way, std::ios_base::openmode mode)
{
const auto old_gptr = this->gptr();
const auto old_pptr = this->pptr();
if (old_pptr && seek_high_ < old_pptr)
{
seek_high_ = old_pptr;
}
const auto seek_low = this->eback();
const auto seek_dist = seek_high_ - seek_low;
off_type new_offset;
switch (way)
{
case std::ios_base::beg:
new_offset = 0;
break;
case std::ios_base::end:
new_offset = seek_dist;
break;
case std::ios_base::cur:
{
constexpr auto both = std::ios_base::in | std::ios_base::out;
if ((mode & both) != both)
{
if (mode & std::ios_base::in)
{
if (old_gptr || !seek_low)
{
new_offset = old_gptr - seek_low;
break;
}
}
else if ((mode & std::ios_base::out) && (old_pptr || !seek_low))
{
new_offset = old_pptr - seek_low;
break;
}
}
}
// fallthrough
default:
return pos_type(off_type(-1));
}
if (std::streamsize(offset) + new_offset > std::streamsize(seek_dist))
{
return pos_type(off_type(-1));
}
offset += new_offset;
if (offset != 0 && (((mode & std::ios_base::in) && !old_gptr) || ((mode & std::ios_base::out) && !old_pptr)))
{
return pos_type(off_type(-1));
}
const auto new_ptr = seek_low + offset;
if ((mode & std::ios_base::in) && old_gptr)
{
this->setg(seek_low, new_ptr, seek_high_);
}
if ((mode & std::ios_base::out) && old_pptr)
{
this->setp(seek_low, new_ptr, this->epptr());
}
return pos_type(offset);
}
//
// Logger
//
Logger::Logger()
: enabled_(true)
, level_(LogLevel::Debug)
, buffer_(1024)
{
LogFormaterPtr formater = new TextFormater;
SetFormater(formater);
LogProviderPtr provider = ConsoleLogProvider::Create();
AddProvider(provider);
}
Logger::~Logger()
{
}
void Logger::Logf(LogLevel level, const char* format, ...)
{
if (!enabled_)
return;
if (level < level_)
return;
// build message
va_list args = nullptr;
va_start(args, format);
StringStream sstream;
sstream << strings::FormatArgs(format, args);
va_end(args);
// write message
Write(level, sstream.rdbuf());
}
void Logger::Flush()
{
if (!enabled_)
return;
for (auto provider : providers_)
{
provider->Flush();
}
}
void Logger::SetLevel(LogLevel level)
{
level_ = level;
}
void Logger::AddProvider(LogProviderPtr provider)
{
if (provider)
{
provider->Init();
providers_.push_back(provider);
}
}
LogFormaterPtr Logger::GetFormater()
{
return formater_;
}
void Logger::ResizeBuffer(size_t buffer_size)
{
buffer_.Resize(buffer_size);
}
void Logger::Write(LogLevel level, std::streambuf* raw_msg)
{
std::lock_guard<std::mutex> lock(mutex_);
// reset buffer
buffer_.Reset();
// format message
std::iostream stream(&buffer_);
if (formater_)
{
formater_->Format(stream, level, Time::Now(), raw_msg);
}
else
{
stream << raw_msg << "\n";
}
// write message
for (auto provider : providers_)
{
buffer_.pubseekpos(0, std::ios_base::in);
provider->WriteMessage(level, &buffer_);
}
}
#if defined(KGE_PLATFORM_WINDOWS)
//
// Console log
//
namespace namespace
{ {
std::streambuf *cin_buffer, *cout_buffer, *cerr_buffer; std::streambuf *cin_buffer, *cout_buffer, *cerr_buffer;
@ -121,204 +674,11 @@ HWND GetAllocatedConsole()
} }
} // namespace } // namespace
namespace kiwano #endif
{
namespace console_colors
{
const WORD blue = FOREGROUND_BLUE | FOREGROUND_INTENSITY;
const WORD green = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
const WORD red = FOREGROUND_RED | FOREGROUND_INTENSITY;
const WORD yellow = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY;
const WORD white = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY;
const WORD blue_bg = white | BACKGROUND_BLUE | BACKGROUND_INTENSITY;
const WORD green_bg = white | BACKGROUND_GREEN | BACKGROUND_INTENSITY;
const WORD red_bg = white | BACKGROUND_RED | BACKGROUND_INTENSITY;
const WORD yellow_bg = BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY;
const WORD white_bg = BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY;
const WORD Reset = white;
} // namespace console_colors
#define DECLARE_HANDLE_COLOR(NAME, HANDLE_NAME, COLOR) \
inline OutputStream&(NAME)(OutputStream & _out) \
{ \
::SetConsoleTextAttribute(::GetStdHandle(HANDLE_NAME), console_colors::##COLOR); \
return _out; \
}
#define DECLARE_COLOR(COLOR) \
DECLARE_HANDLE_COLOR(stdout_##COLOR, STD_OUTPUT_HANDLE, COLOR) \
DECLARE_HANDLE_COLOR(stderr_##COLOR, STD_ERROR_HANDLE, COLOR)
#define DECLARE_BG_COLOR(COLOR) \
DECLARE_HANDLE_COLOR(stdout_##COLOR##_bg, STD_OUTPUT_HANDLE, COLOR##_bg) \
DECLARE_HANDLE_COLOR(stderr_##COLOR##_bg, STD_ERROR_HANDLE, COLOR##_bg)
namespace console_colors
{
DECLARE_COLOR(red);
DECLARE_COLOR(green);
DECLARE_COLOR(yellow);
DECLARE_COLOR(blue);
DECLARE_COLOR(white);
DECLARE_COLOR(Reset);
DECLARE_BG_COLOR(red);
DECLARE_BG_COLOR(green);
DECLARE_BG_COLOR(yellow);
DECLARE_BG_COLOR(blue);
DECLARE_BG_COLOR(white);
} // namespace console_colors
Logger::Logger()
: enabled_(true)
, default_stdout_color_(0)
, default_stderr_color_(0)
, output_stream_(std::cout.rdbuf())
, error_stream_(std::cerr.rdbuf())
{
ResetStreamToStdStream();
}
Logger::~Logger()
{
FreeAllocatedConsole();
}
void Logger::Printf(Level level, const char* format, ...)
{
if (!enabled_)
return;
StringStream sstream;
Prepare(level, sstream);
// Format message
if (format)
{
va_list args = nullptr;
va_start(args, format);
static char temp_buffer[1024 * 3 + 1];
const auto len = ::_vscprintf(format, args) + 1;
::_vsnprintf_s(temp_buffer, len, len, format, args);
sstream << ' ' << temp_buffer << "\n";
va_end(args);
}
Output(level, sstream);
}
void Logger::Prepare(Level level, StringStream& sstream)
{
String prefix;
switch (level)
{
case Level::Info:
prefix = "[INFO] ";
break;
case Level::System:
prefix = "[SYSTEM] ";
break;
case Level::Warning:
prefix = "[WARN] ";
break;
case Level::Error:
prefix = "[ERROR] ";
break;
}
// Timestamp
time_t unix = std::time(nullptr);
std::tm tmbuf;
localtime_s(&tmbuf, &unix);
sstream << prefix << std::put_time(&tmbuf, "%H:%M:%S ");
}
void Logger::Output(Level level, StringStream& sstream)
{
using ConsoleColor = Function<OutputStream&(OutputStream&)>;
OutputStream* ostream = nullptr;
ConsoleColor color = nullptr;
switch (level)
{
case Level::Info:
ostream = &output_stream_;
color = Closure(this, &Logger::DefaultOutputColor);
break;
case Level::System:
ostream = &output_stream_;
color = console_colors::stdout_blue;
break;
case Level::Warning:
ostream = &output_stream_;
color = console_colors::stdout_yellow_bg;
break;
case Level::Error:
ostream = &error_stream_;
color = console_colors::stderr_red_bg;
break;
}
// Printing
if (ostream)
{
auto output = sstream.str();
color(*ostream) << output << std::flush;
::OutputDebugStringA(output.c_str());
ResetConsoleColor();
}
}
void Logger::ResetStreamToStdStream()
{
bool has_console = ::GetConsoleWindow() != nullptr;
if (has_console)
{
default_stdout_color_ = default_stderr_color_ =
FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY;
CONSOLE_SCREEN_BUFFER_INFO stdout_info;
if (::GetConsoleScreenBufferInfo(::GetStdHandle(STD_OUTPUT_HANDLE), &stdout_info))
{
default_stdout_color_ = stdout_info.wAttributes;
}
CONSOLE_SCREEN_BUFFER_INFO stderr_info;
if (::GetConsoleScreenBufferInfo(::GetStdHandle(STD_ERROR_HANDLE), &stderr_info))
{
default_stderr_color_ = stderr_info.wAttributes;
}
// replace the C++ global locale with the user-preferred locale
(void)std::locale::global(std::locale());
(void)std::cout.imbue(std::locale());
(void)std::cerr.imbue(std::locale());
RedirectOutputStream(std::cout.rdbuf());
RedirectErrorStream(std::cerr.rdbuf());
}
}
std::streambuf* Logger::RedirectOutputStream(std::streambuf* buf)
{
return output_stream_.rdbuf(buf);
}
std::streambuf* Logger::RedirectErrorStream(std::streambuf* buf)
{
return error_stream_.rdbuf(buf);
}
void Logger::ShowConsole(bool show) void Logger::ShowConsole(bool show)
{ {
#if defined(KGE_PLATFORM_WINDOWS)
HWND current_console = ::GetConsoleWindow(); HWND current_console = ::GetConsoleWindow();
if (show) if (show)
{ {
@ -328,14 +688,13 @@ void Logger::ShowConsole(bool show)
} }
else else
{ {
HWND console = ::AllocateConsole(); HWND console = AllocateConsole();
if (!console) if (console)
{ {
KGE_WARN("AllocConsole failed"); // replace the C++ global locale with the user-preferred locale
} (void)std::locale::global(std::locale());
else (void)std::cout.imbue(std::locale());
{ (void)std::cerr.imbue(std::locale());
ResetStreamToStdStream();
// disable the close button of console // disable the close button of console
HMENU hmenu = ::GetSystemMenu(console, FALSE); HMENU hmenu = ::GetSystemMenu(console, FALSE);
@ -357,6 +716,10 @@ void Logger::ShowConsole(bool show)
} }
} }
} }
#else
// NOT IMPLEMENT
#endif
} }
} // namespace kiwano } // namespace kiwano

View File

@ -19,83 +19,212 @@
// THE SOFTWARE. // THE SOFTWARE.
#pragma once #pragma once
#include <kiwano/core/Common.h> #include <kiwano/core/Time.h>
#include <kiwano/base/ObjectBase.h>
#include <mutex>
#include <iomanip> #include <iomanip>
#include <streambuf> #include <streambuf>
#include <fstream>
#ifndef KGE_SYS_LOG #ifndef KGE_DEBUG_LOG
#ifdef KGE_DEBUG #ifdef KGE_DEBUG
#define KGE_SYS_LOG(FORMAT, ...) \ #define KGE_DEBUG_LOG(...) ::kiwano::Logger::GetInstance().Log(::kiwano::LogLevel::Debug, __VA_ARGS__)
::kiwano::Logger::GetInstance().Printf(::kiwano::Logger::Level::System, FORMAT, __VA_ARGS__)
#else #else
#define KGE_SYS_LOG __noop #define KGE_DEBUG_LOG __noop
#endif #endif
#endif #endif
#ifndef KGE_WARN
#define KGE_WARN(FORMAT, ...) ::kiwano::Logger::GetInstance().Printf(::kiwano::Logger::Level::Warning, FORMAT, __VA_ARGS__)
#endif
#ifndef KGE_ERROR
#define KGE_ERROR(FORMAT, ...) ::kiwano::Logger::GetInstance().Printf(::kiwano::Logger::Level::Error, FORMAT, __VA_ARGS__)
#endif
#ifndef KGE_LOG #ifndef KGE_LOG
#define KGE_LOG(...) ::kiwano::Logger::GetInstance().Println(::kiwano::Logger::Level::Info, __VA_ARGS__) #define KGE_LOG(...) ::kiwano::Logger::GetInstance().Log(::kiwano::LogLevel::Info, __VA_ARGS__)
#endif
#ifndef KGE_NOTICE
#define KGE_NOTICE(...) ::kiwano::Logger::GetInstance().Log(::kiwano::LogLevel::Notice, __VA_ARGS__)
#endif
#ifndef KGE_WARN
#define KGE_WARN(...) ::kiwano::Logger::GetInstance().Log(::kiwano::LogLevel::Warning, __VA_ARGS__)
#endif
#ifndef KGE_ERROR
#define KGE_ERROR(...) ::kiwano::Logger::GetInstance().Log(::kiwano::LogLevel::Error, __VA_ARGS__)
#endif
#ifndef KGE_DEBUG_LOGF
#ifdef KGE_DEBUG
#define KGE_DEBUG_LOGF(FORMAT, ...) ::kiwano::Logger::GetInstance().Logf(::kiwano::LogLevel::Debug, FORMAT, __VA_ARGS__)
#else
#define KGE_DEBUG_LOGF __noop
#endif
#endif #endif
#ifndef KGE_LOGF #ifndef KGE_LOGF
#define KGE_LOGF(FORMAT, ...) ::kiwano::Logger::GetInstance().Printf(::kiwano::Logger::Level::Info, FORMAT, __VA_ARGS__) #define KGE_LOGF(FORMAT, ...) ::kiwano::Logger::GetInstance().Logf(::kiwano::LogLevel::Info, FORMAT, __VA_ARGS__)
#endif #endif
#ifndef KGE_LOG_STREAM #ifndef KGE_NOTICEF
#define KGE_LOG_STREAM() ::kiwano::Logger::GetInstance().GetOutputStream() #define KGE_NOTICEF(FORMAT, ...) ::kiwano::Logger::GetInstance().Logf(::kiwano::LogLevel::Notice, FORMAT, __VA_ARGS__)
#endif #endif
#ifndef KGE_ERROR_STREAM #ifndef KGE_WARNF
#define KGE_ERROR_STREAM() ::kiwano::Logger::GetInstance().GetErrorStream() #define KGE_WARNF(FORMAT, ...) ::kiwano::Logger::GetInstance().Logf(::kiwano::LogLevel::Warning, FORMAT, __VA_ARGS__)
#endif
#ifndef KGE_ERRORF
#define KGE_ERRORF(FORMAT, ...) ::kiwano::Logger::GetInstance().Logf(::kiwano::LogLevel::Error, FORMAT, __VA_ARGS__)
#endif #endif
namespace kiwano namespace kiwano
{ {
KGE_DECLARE_SMART_PTR(LogFormater);
KGE_DECLARE_SMART_PTR(LogProvider);
KGE_DECLARE_SMART_PTR(Logger);
/** /**
* \~chinese * \~chinese
* @brief * @brief
*/
enum class LogLevel
{
Debug, ///< 调试
Info, ///< 信息
Notice, ///< 注意
Warning, ///< 警告
Error, ///< 错误
};
/**
* \~chinese
* @brief
*/
class KGE_API LogFormater : public ObjectBase
{
public:
virtual void Format(std::iostream& out, LogLevel level, Time time, std::streambuf* raw_msg) = 0;
String GetLevelLabel(LogLevel level) const;
};
/**
* \~chinese
* @brief
*/
class LogBuffer : public std::streambuf
{
public:
LogBuffer(size_t buffer_size);
void Resize(size_t size);
void Reset();
const char* GetRaw() const;
LogBuffer(const LogBuffer&) = delete;
LogBuffer& operator=(const LogBuffer&) = delete;
protected:
int_type overflow(int_type ch) override;
int_type underflow() override;
pos_type seekpos(pos_type sp, std::ios_base::openmode which) override;
pos_type seekoff(off_type off, std::ios_base::seekdir dir,
std::ios_base::openmode which = std::ios_base::in) override;
private:
Vector<char_type> buf_;
char_type* seek_high_;
};
/**
* \~chinese
* @brief
*/
class KGE_API LogProvider : public ObjectBase
{
public:
virtual ~LogProvider();
virtual void Init() = 0;
virtual void WriteMessage(LogLevel level, LogBuffer* msg) = 0;
virtual void Flush() = 0;
};
/**
* \~chinese
* @brief
*/
class KGE_API ConsoleLogProvider : public LogProvider
{
public:
static LogProviderPtr Create();
virtual ~ConsoleLogProvider();
void Init() override;
void WriteMessage(LogLevel level, LogBuffer* msg) override;
void Flush() override;
private:
typedef std::ostream&(*ConsoleColor)(std::ostream&);
ConsoleColor GetColor(LogLevel level);
};
/**
* \~chinese
* @brief
*/
class KGE_API FileLogProvider : public LogProvider
{
public:
static LogProviderPtr Create(const String& filepath, std::ios_base::openmode mode = std::ios_base::out);
virtual ~FileLogProvider();
void Init() override;
void WriteMessage(LogLevel level, LogBuffer* msg) override;
void Flush() override;
private:
std::ofstream ofs_;
};
/**
* \~chinese
* @brief
*/ */
class KGE_API Logger : public Singleton<Logger> class KGE_API Logger : public Singleton<Logger>
{ {
friend Singleton<Logger>; friend Singleton<Logger>;
public: public:
/// \~chinese
/// @brief 日志级别
enum class Level
{
Info, ///< 信息
System, ///< 系统
Warning, ///< 警告
Error ///< 错误
};
/// \~chinese /// \~chinese
/// @brief 打印日志 /// @brief 打印日志
/// @param level 日志级别 /// @param level 日志级别
/// @param format 格式字符串 /// @param format 格式字符串
void Printf(Level level, const char* format, ...); void Logf(LogLevel level, const char* format, ...);
/// \~chinese /// \~chinese
/// @brief 打印日志 /// @brief 打印日志
/// @param level 日志级别 /// @param level 日志级别
/// @param args 参数 /// @param args 参数
template <typename... _Args> template <typename... _Args>
void Print(Level level, _Args&&... args); void Log(LogLevel level, _Args&&... args);
/// \~chinese /// \~chinese
/// @brief 打印一行日志 /// @brief 刷新日志缓冲
/// @param level 日志级别 void Flush();
/// @param args 参数
template <typename... _Args>
void Println(Level level, _Args&&... args);
/// \~chinese /// \~chinese
/// @brief 启用日志 /// @brief 启用日志
@ -106,48 +235,51 @@ public:
void Disable(); void Disable();
/// \~chinese /// \~chinese
/// @brief 获取输出流 /// @brief 设置日志等级
OutputStream& GetOutputStream(); void SetLevel(LogLevel level);
/// \~chinese /// \~chinese
/// @brief 获取错误输出流 /// @brief 添加日志提供者
OutputStream& GetErrorStream(); /// @param provider 日志提供者
void AddProvider(LogProviderPtr provider);
/// \~chinese /// \~chinese
/// @brief 重定向输出流 /// @brief 设置日志格式
std::streambuf* RedirectOutputStream(std::streambuf* buf); /// @param formater 日志格式化
void SetFormater(LogFormaterPtr formater);
/// \~chinese /// \~chinese
/// @brief 重定向错误输出流 /// @brief 获取日志格式
std::streambuf* RedirectErrorStream(std::streambuf* buf); /// @return 日志格式
LogFormaterPtr GetFormater();
/// \~chinese
/// @brief 重设缓冲区大小
/// @param buffer_size 缓冲区大小
void ResizeBuffer(size_t buffer_size);
/// \~chinese
/// @brief 写入缓冲区
/// @param level 日志等级
/// @param raw_msg 日志内容
void Write(LogLevel level, std::streambuf* raw_msg);
/// \~chinese /// \~chinese
/// @brief 显示或关闭控制台 /// @brief 显示或关闭控制台
/// @note 此操作会重定向输出流到标准输出流
void ShowConsole(bool show); void ShowConsole(bool show);
~Logger(); virtual ~Logger();
private: private:
Logger(); Logger();
void Prepare(Level level, StringStream& sstream);
void Output(Level level, StringStream& sstream);
void ResetStreamToStdStream();
void ResetConsoleColor() const;
OutputStream& DefaultOutputColor(OutputStream& out);
private: private:
bool enabled_; bool enabled_;
WORD default_stdout_color_; LogLevel level_;
WORD default_stderr_color_; LogFormaterPtr formater_;
LogBuffer buffer_;
OutputStream output_stream_; Vector<LogProviderPtr> providers_;
OutputStream error_stream_; std::mutex mutex_;
}; };
inline void Logger::Enable() inline void Logger::Enable()
@ -160,57 +292,26 @@ inline void Logger::Disable()
enabled_ = false; enabled_ = false;
} }
inline void Logger::SetFormater(LogFormaterPtr formater)
{
formater_ = formater;
}
template <typename... _Args> template <typename... _Args>
void Logger::Print(Level level, _Args&&... args) inline void Logger::Log(LogLevel level, _Args&&... args)
{ {
if (!enabled_) if (!enabled_)
return; return;
StringStream sstream; if (level < level_)
Prepare(level, sstream);
// Format message
(void)std::initializer_list<int>{ ((sstream << ' ' << args), 0)... };
Output(level, sstream);
}
template <typename... _Args>
void Logger::Println(Level level, _Args&&... args)
{
if (!enabled_)
return; return;
// build message
StringStream sstream; StringStream sstream;
Prepare(level, sstream);
// Format message
(void)std::initializer_list<int>{ ((sstream << ' ' << args), 0)... }; (void)std::initializer_list<int>{ ((sstream << ' ' << args), 0)... };
sstream << "\r\n"; // write message
this->Write(level, sstream.rdbuf());
Output(level, sstream);
} }
inline void Logger::ResetConsoleColor() const
{
::SetConsoleTextAttribute(::GetStdHandle(STD_OUTPUT_HANDLE), default_stdout_color_);
::SetConsoleTextAttribute(::GetStdHandle(STD_ERROR_HANDLE), default_stderr_color_);
}
inline OutputStream& Logger::DefaultOutputColor(OutputStream& out)
{
::SetConsoleTextAttribute(::GetStdHandle(STD_OUTPUT_HANDLE), default_stdout_color_);
return out;
}
inline OutputStream& Logger::GetOutputStream()
{
return output_stream_;
}
inline OutputStream& Logger::GetErrorStream()
{
return error_stream_;
}
} // namespace kiwano } // namespace kiwano

View File

@ -60,7 +60,7 @@ bool ResourceCache::LoadFromJsonFile(const String& file_path)
{ {
if (!FileSystem::GetInstance().IsFileExists(file_path)) if (!FileSystem::GetInstance().IsFileExists(file_path))
{ {
KGE_ERROR("%s failed: File not found.", __FUNCTION__); KGE_ERRORF("%s failed: File not found.", __FUNCTION__);
return false; return false;
} }
@ -78,12 +78,12 @@ bool ResourceCache::LoadFromJsonFile(const String& file_path)
} }
catch (std::wifstream::failure& e) catch (std::wifstream::failure& e)
{ {
KGE_ERROR("%s failed: Cannot open file. (%s)", __FUNCTION__, e.what()); KGE_ERRORF("%s failed: Cannot open file. (%s)", __FUNCTION__, e.what());
return false; return false;
} }
catch (Json::exception& e) catch (Json::exception& e)
{ {
KGE_ERROR("%s failed: Cannot parse to JSON. (%s)", __FUNCTION__, e.what()); KGE_ERRORF("%s failed: Cannot parse to JSON. (%s)", __FUNCTION__, e.what());
return false; return false;
} }
return LoadFromJson(json_data); return LoadFromJson(json_data);
@ -106,12 +106,12 @@ bool ResourceCache::LoadFromJson(const Json& json_data)
} }
else else
{ {
KGE_ERROR("%s failed: unknown resource data version", __FUNCTION__); KGE_ERRORF("%s failed: unknown resource data version", __FUNCTION__);
} }
} }
catch (Json::exception& e) catch (Json::exception& e)
{ {
KGE_ERROR("%s failed: JSON data is invalid. (%s)", __FUNCTION__, e.what()); KGE_ERRORF("%s failed: JSON data is invalid. (%s)", __FUNCTION__, e.what());
return false; return false;
} }
return false; return false;
@ -121,7 +121,7 @@ bool ResourceCache::LoadFromXmlFile(const String& file_path)
{ {
if (!FileSystem::GetInstance().IsFileExists(file_path)) if (!FileSystem::GetInstance().IsFileExists(file_path))
{ {
KGE_ERROR("%s failed: File not found.", __FUNCTION__); KGE_ERRORF("%s failed: File not found.", __FUNCTION__);
return false; return false;
} }
@ -136,7 +136,7 @@ bool ResourceCache::LoadFromXmlFile(const String& file_path)
} }
else else
{ {
KGE_ERROR("%s failed: XML [%s] parsed with errors: %s", __FUNCTION__, full_path.c_str(), result.description()); KGE_ERRORF("%s failed: XML [%s] parsed with errors: %s", __FUNCTION__, full_path.c_str(), result.description());
return false; return false;
} }
} }
@ -160,7 +160,7 @@ bool ResourceCache::LoadFromXml(const XmlDocument& doc)
} }
else else
{ {
KGE_ERROR("%s failed: unknown resource data version", __FUNCTION__); KGE_ERRORF("%s failed: unknown resource data version", __FUNCTION__);
} }
} }
return false; return false;