File Windows/ClientPlatformSpecific.cpp
File List > api > cppSDK > SDKClient > PlatformSpecific > Windows > ClientPlatformSpecific.cpp
Go to the documentation of this file
#include "ClientPlatformSpecific.hpp"
// Stop any Windows.h includes from declaring the min and max macros, because
// they conflict with std::min and std::max.
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
// CoreSdk_Shutdown, etc.
#include "ManusSDK.h"
// std::min
#include <algorithm>
// std::codecvt_utf8_utf16
#include <codecvt>
// std::filesystem
#include <filesystem>
// std::*fstream
#include <fstream>
// std::wstring_convert
#include <locale>
// SHGetKnownFolderPath
#include <ShlObj_core.h>
// spdlog
#include "spdlog/spdlog.h"
// wstringstream
#include <sstream>
// BOOL, DWORD, etc.
#include <Windows.h>
const std::string SDKClientPlatformSpecific::s_SlashForFilesystemPath = "\\";
bool g_PreviousKeyState[0x87] = { false };
static BOOL __stdcall ProcessConsoleShutdown(DWORD p_fdwCtrlType)
{
switch (p_fdwCtrlType)
{
case CTRL_CLOSE_EVENT:
case CTRL_SHUTDOWN_EVENT:
{
return CoreSdk_ShutDown();
}
default: return 0;
}
}
static std::string GetStringForError(int p_ErrorNumber)
{
std::string t_Result;
LPSTR t_ErrorMessage = NULL;
DWORD t_NumChars = FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
p_ErrorNumber,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
// Note the 'T' - this is NOT an LPSTR!
// Microsoft's documentation says: "An LPWSTR if UNICODE is
// defined, an LPSTR otherwise.",
// and: "The letter "T" in a type definition, for example, TCHAR or
// LPTSTR, designates a generic type that can be compiled for
// either Windows code pages or Unicode."
reinterpret_cast<LPTSTR>(&t_ErrorMessage),
0,
NULL);
if (t_NumChars == 0)
{
DWORD t_FormatError = GetLastError();
t_Result =
std::string("(could not get an error string for this error number). ") +
std::string("FormatMessage failed with error ") +
std::to_string(t_FormatError) +
std::string(".");
}
else
{
t_Result = std::string(static_cast<const char*>(t_ErrorMessage));
}
LocalFree(t_ErrorMessage);
return t_Result;
}
static bool DoesWindowHaveFocus(void)
{
HWND t_ConsoleWindow = GetConsoleWindow();
HWND t_HCurWnd = GetForegroundWindow();
return t_ConsoleWindow == t_HCurWnd;
}
std::string UTF16WstringToUTF8String(const std::wstring& p_Wstring)
{
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> t_Converter;
std::string t_Narrow = t_Converter.to_bytes(p_Wstring);
return t_Narrow;
}
std::wstring UTF8StringtoUTF16Wstring(const std::string& p_String)
{
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> t_Converter;
std::wstring t_Wide = t_Converter.from_bytes(p_String);
return t_Wide;
}
bool SDKClientPlatformSpecific::PlatformSpecificInitialization(void)
{
// If the system's locale is set correctly, and a compatible font is used,
// this makes it possible to display Unicode characters encoded using UTF-8
// in the command prompt.
if (SetConsoleOutputCP(CP_UTF8) == 0)
{
spdlog::error(
"Failed to set the console output to UTF-8. The error number was {}.",
GetLastError());
return false;
}
if (SetConsoleCtrlHandler(ProcessConsoleShutdown, true) == false)
{
spdlog::error("Failed to initialize console shutdown events.");
return false;
}
return true;
}
bool SDKClientPlatformSpecific::PlatformSpecificShutdown(void)
{
return true;
}
void SDKClientPlatformSpecific::UpdateInput(void)
{
// Nothing to update here for Windows.
}
bool SDKClientPlatformSpecific::CopyString(
char* const p_Target,
const size_t p_MaxLengthThatWillFitInTarget,
const std::string& p_Source)
{
// strcpy_s Is basically a Microsoft-only function.
// https://stackoverflow.com/questions/4570147/safe-string-functions-in-mac-os-x-and-linux
const errno_t t_CopyResult = strcpy_s(
p_Target,
p_MaxLengthThatWillFitInTarget,
p_Source.c_str());
if (t_CopyResult != 0)
{
spdlog::error(
"Copying the string {} resulted in the error {}."
, p_Source.c_str()
, t_CopyResult);
return false;
}
return true;
}
bool SDKClientPlatformSpecific::ResizeWindow(
const short int p_ConsoleWidth,
const short int p_ConsoleHeight,
const short int p_ConsoleScrollback)
{
bool t_Absolute = true;
HANDLE t_Console = GetStdHandle(STD_OUTPUT_HANDLE);
COORD t_BufferSize =
{
p_ConsoleWidth,
p_ConsoleScrollback // Height includes the number of lines that can be
// viewed by scrolling up.
};
auto t_MaxConsoleSize = GetLargestConsoleWindowSize(t_Console);
const short int t_WindowHeight =
std::min(t_BufferSize.Y,
std::min(p_ConsoleHeight, t_MaxConsoleSize.Y));
_SMALL_RECT t_ConsoleRect =
{
0,
0,
std::min(t_BufferSize.X, t_MaxConsoleSize.X) - 1,
t_WindowHeight - 1
};
// Resize the buffer, but not the window.
// If the window is not resized as well, a scrollbar can be used to see
// text that won't fit.
if (!SetConsoleScreenBufferSize(t_Console, t_BufferSize))
{
DWORD t_Error = GetLastError();
spdlog::error(
"Setting the console screen buffer size failed with error {}: {}",
t_Error,
GetStringForError(t_Error));
return false;
}
// Resize the window itself.
if (!SetConsoleWindowInfo(t_Console, t_Absolute, &t_ConsoleRect))
{
DWORD t_Error = GetLastError();
spdlog::error(
"Setting the console window size failed with error {}: {}",
t_Error,
GetStringForError(t_Error));
return false;
}
return true;
}
void SDKClientPlatformSpecific::ApplyConsolePosition(
const int p_ConsoleCurrentOffset)
{
COORD t_Pos = { 0, static_cast<SHORT>(p_ConsoleCurrentOffset) };
HANDLE t_Output = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(t_Output, t_Pos);
}
void SDKClientPlatformSpecific::ClearConsole(void)
{
COORD t_TopLeft = { 0, 0 };
HANDLE t_Console = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO t_Screen;
DWORD t_Written;
GetConsoleScreenBufferInfo(t_Console, &t_Screen);
FillConsoleOutputCharacterA(
t_Console,
' ',
t_Screen.dwSize.X * t_Screen.dwSize.Y,
t_TopLeft,
&t_Written);
FillConsoleOutputAttribute(
t_Console,
FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE,
t_Screen.dwSize.X * t_Screen.dwSize.Y,
t_TopLeft,
&t_Written);
SetConsoleCursorPosition(t_Console, t_TopLeft);
}
bool SDKClientPlatformSpecific::GetKey(const int p_Key)
{
if (DoesWindowHaveFocus()) // we got focus? then check key state.
{
bool t_Res = GetAsyncKeyState(p_Key) & 0x8000;
g_PreviousKeyState[p_Key] = t_Res;
return t_Res;
}
return false;
}
bool SDKClientPlatformSpecific::GetKeyDown(const int p_Key)
{
if (DoesWindowHaveFocus()) // we got focus? then check key state.
{
bool t_Res = false;
bool t_State = GetAsyncKeyState(p_Key) & 0x8000;
if (t_State == true && g_PreviousKeyState[p_Key] == false)
t_Res = true;
g_PreviousKeyState[p_Key] = t_State;
return t_Res;
}
return false;
}
bool SDKClientPlatformSpecific::GetKeyUp(const int p_Key)
{
if (DoesWindowHaveFocus()) // we got focus? then check key state.
{
bool t_Res = false;
bool t_State = GetAsyncKeyState(p_Key) & 0x8000;
if (t_State == false && g_PreviousKeyState[p_Key] == true)
t_Res = true;
g_PreviousKeyState[p_Key] = t_State;
return t_Res;
}
return false;
}
std::string SDKClientPlatformSpecific::GetDocumentsDirectoryPath_UTF8(void)
{
std::wstringstream t_Wss;
// try to get the documents local path
wchar_t* t_Path = NULL;
HRESULT t_Result = SHGetKnownFolderPath(
FOLDERID_Documents,
0,
NULL,
&t_Path);
// If this fails, the system is either incredibly security locked, or very
// bad.
if (SUCCEEDED(t_Result))
{
std::wstringstream t_Wss;
t_Wss << t_Path;
// Due to the way SHGetKnownFolderPath works. we need to clean this up.
CoTaskMemFree(t_Path);
std::string t_NarrowDocumentsDir =
UTF16WstringToUTF8String(t_Wss.str());
return t_NarrowDocumentsDir;
}
else
{
spdlog::warn("Could not get the directory path for the documents.");
}
return std::string("");
}
std::ifstream SDKClientPlatformSpecific::GetInputFileStream(
std::string p_Path_UTF8)
{
std::wstring t_WidePath = UTF8StringtoUTF16Wstring(p_Path_UTF8);
return std::ifstream(t_WidePath, std::ifstream::binary);
}
std::ofstream SDKClientPlatformSpecific::GetOutputFileStream(
std::string p_Path_UTF8)
{
std::wstring t_WidePath = UTF8StringtoUTF16Wstring(p_Path_UTF8);
return std::ofstream(t_WidePath, std::ofstream::binary);
}
bool SDKClientPlatformSpecific::DoesFolderOrFileExist(std::string p_Path_UTF8)
{
std::wstring t_WidePath = UTF8StringtoUTF16Wstring(p_Path_UTF8);
return std::filesystem::exists(t_WidePath);
}
void SDKClientPlatformSpecific::CreateFolderIfItDoesNotExist(
std::string p_Path_UTF8)
{
std::wstring t_WidePath = UTF8StringtoUTF16Wstring(p_Path_UTF8);
if (!DoesFolderOrFileExist(p_Path_UTF8))
{
std::filesystem::create_directory(t_WidePath);
}
}