C++ SDK for MANUS Core 2.0
Loading...
Searching...
No Matches
ClientPlatformSpecific.cpp
Go to the documentation of this file.
2
3// Stop any Windows.h includes from declaring the min and max macros, because
4// they conflict with std::min and std::max.
5#define NOMINMAX
6#define WIN32_LEAN_AND_MEAN
7
8// CoreSdk_Shutdown, etc.
9#include "ManusSDK.h"
10
11// std::min
12#include <algorithm>
13// std::codecvt_utf8_utf16
14#include <codecvt>
15// std::filesystem
16#include <filesystem>
17// std::*fstream
18#include <fstream>
19// std::wstring_convert
20#include <locale>
21// SHGetKnownFolderPath
22#include <ShlObj_core.h>
23// spdlog
24#include "spdlog/spdlog.h"
25// wstringstream
26#include <sstream>
27// BOOL, DWORD, etc.
28#include <Windows.h>
29
31
33bool g_PreviousKeyState[0x87] = { false };
34
38static BOOL __stdcall ProcessConsoleShutdown(DWORD p_fdwCtrlType)
39{
40 switch (p_fdwCtrlType)
41 {
42 case CTRL_CLOSE_EVENT:
43 case CTRL_SHUTDOWN_EVENT:
44 {
45 return CoreSdk_ShutDown();
46 }
47 default: return 0;
48 }
49}
50
53static std::string GetStringForError(int p_ErrorNumber)
54{
55 std::string t_Result;
56
57 LPSTR t_ErrorMessage = NULL;
58
59 DWORD t_NumChars = FormatMessage(
60 FORMAT_MESSAGE_FROM_SYSTEM
61 | FORMAT_MESSAGE_ALLOCATE_BUFFER
62 | FORMAT_MESSAGE_IGNORE_INSERTS,
63 NULL,
64 p_ErrorNumber,
65 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
66 // Note the 'T' - this is NOT an LPSTR!
67 // Microsoft's documentation says: "An LPWSTR if UNICODE is
68 // defined, an LPSTR otherwise.",
69 // and: "The letter "T" in a type definition, for example, TCHAR or
70 // LPTSTR, designates a generic type that can be compiled for
71 // either Windows code pages or Unicode."
72 reinterpret_cast<LPTSTR>(&t_ErrorMessage),
73 0,
74 NULL);
75
76 if (t_NumChars == 0)
77 {
78 DWORD t_FormatError = GetLastError();
79
80 t_Result =
81 std::string("(could not get an error string for this error number). ") +
82 std::string("FormatMessage failed with error ") +
83 std::to_string(t_FormatError) +
84 std::string(".");
85 }
86 else
87 {
88 t_Result = std::string(static_cast<const char*>(t_ErrorMessage));
89 }
90
91 LocalFree(t_ErrorMessage);
92
93 return t_Result;
94}
95
96static bool DoesWindowHaveFocus(void)
97{
98 HWND t_ConsoleWindow = GetConsoleWindow();
99 HWND t_HCurWnd = GetForegroundWindow();
100
101 return t_ConsoleWindow == t_HCurWnd;
102}
103
104std::string UTF16WstringToUTF8String(const std::wstring& p_Wstring)
105{
106 std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> t_Converter;
107 std::string t_Narrow = t_Converter.to_bytes(p_Wstring);
108
109 return t_Narrow;
110}
111
112std::wstring UTF8StringtoUTF16Wstring(const std::string& p_String)
113{
114 std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> t_Converter;
115 std::wstring t_Wide = t_Converter.from_bytes(p_String);
116
117 return t_Wide;
118}
119
121{
122 // If the system's locale is set correctly, and a compatible font is used,
123 // this makes it possible to display Unicode characters encoded using UTF-8
124 // in the command prompt.
125 if (SetConsoleOutputCP(CP_UTF8) == 0)
126 {
127 spdlog::error(
128 "Failed to set the console output to UTF-8. The error number was {}.",
129 GetLastError());
130
131 return false;
132 }
133
134 if (SetConsoleCtrlHandler(ProcessConsoleShutdown, true) == false)
135 {
136 spdlog::error("Failed to initialize console shutdown events.");
137
138 return false;
139 }
140
141 return true;
142}
143
145{
146 return true;
147}
148
150{
151 // Nothing to update here for Windows.
152}
153
155 char* const p_Target,
156 const size_t p_MaxLengthThatWillFitInTarget,
157 const std::string& p_Source)
158{
159 // strcpy_s Is basically a Microsoft-only function.
160 // https://stackoverflow.com/questions/4570147/safe-string-functions-in-mac-os-x-and-linux
161 const errno_t t_CopyResult = strcpy_s(
162 p_Target,
163 p_MaxLengthThatWillFitInTarget,
164 p_Source.c_str());
165 if (t_CopyResult != 0)
166 {
167 spdlog::error(
168 "Copying the string {} resulted in the error {}."
169 , p_Source.c_str()
170 , t_CopyResult);
171
172 return false;
173 }
174
175 return true;
176}
177
179 const short int p_ConsoleWidth,
180 const short int p_ConsoleHeight,
181 const short int p_ConsoleScrollback)
182{
183 bool t_Absolute = true;
184 HANDLE t_Console = GetStdHandle(STD_OUTPUT_HANDLE);
185
186 COORD t_BufferSize =
187 {
188 p_ConsoleWidth,
189 p_ConsoleScrollback // Height includes the number of lines that can be
190 // viewed by scrolling up.
191 };
192
193 auto t_MaxConsoleSize = GetLargestConsoleWindowSize(t_Console);
194 const short int t_WindowHeight =
195 std::min(t_BufferSize.Y,
196 std::min(p_ConsoleHeight, t_MaxConsoleSize.Y));
197 _SMALL_RECT t_ConsoleRect =
198 {
199 0,
200 0,
201 std::min(t_BufferSize.X, t_MaxConsoleSize.X) - 1,
202 t_WindowHeight - 1
203 };
204
205 // Resize the buffer, but not the window.
206 // If the window is not resized as well, a scrollbar can be used to see
207 // text that won't fit.
208 if (!SetConsoleScreenBufferSize(t_Console, t_BufferSize))
209 {
210 DWORD t_Error = GetLastError();
211 spdlog::error(
212 "Setting the console screen buffer size failed with error {}: {}",
213 t_Error,
214 GetStringForError(t_Error));
215 return false;
216 }
217
218 // Resize the window itself.
219 if (!SetConsoleWindowInfo(t_Console, t_Absolute, &t_ConsoleRect))
220 {
221 DWORD t_Error = GetLastError();
222 spdlog::error(
223 "Setting the console window size failed with error {}: {}",
224 t_Error,
225 GetStringForError(t_Error));
226 return false;
227 }
228
229 return true;
230}
231
233 const int p_ConsoleCurrentOffset)
234{
235 COORD t_Pos = { 0, static_cast<SHORT>(p_ConsoleCurrentOffset) };
236 HANDLE t_Output = GetStdHandle(STD_OUTPUT_HANDLE);
237 SetConsoleCursorPosition(t_Output, t_Pos);
238}
239
241{
242 COORD t_TopLeft = { 0, 0 };
243 HANDLE t_Console = GetStdHandle(STD_OUTPUT_HANDLE);
244 CONSOLE_SCREEN_BUFFER_INFO t_Screen;
245 DWORD t_Written;
246
247 GetConsoleScreenBufferInfo(t_Console, &t_Screen);
248 FillConsoleOutputCharacterA(
249 t_Console,
250 ' ',
251 t_Screen.dwSize.X * t_Screen.dwSize.Y,
252 t_TopLeft,
253 &t_Written);
254 FillConsoleOutputAttribute(
255 t_Console,
256 FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE,
257 t_Screen.dwSize.X * t_Screen.dwSize.Y,
258 t_TopLeft,
259 &t_Written);
260 SetConsoleCursorPosition(t_Console, t_TopLeft);
261}
262
263bool SDKClientPlatformSpecific::GetKey(const int p_Key)
264{
265 if (DoesWindowHaveFocus()) // we got focus? then check key state.
266 {
267 bool t_Res = GetAsyncKeyState(p_Key) & 0x8000;
268 g_PreviousKeyState[p_Key] = t_Res;
269
270 return t_Res;
271 }
272
273 return false;
274}
275
276bool SDKClientPlatformSpecific::GetKeyDown(const int p_Key)
277{
278 if (DoesWindowHaveFocus()) // we got focus? then check key state.
279 {
280 bool t_Res = false;
281 bool t_State = GetAsyncKeyState(p_Key) & 0x8000;
282 if (t_State == true && g_PreviousKeyState[p_Key] == false)
283 t_Res = true;
284 g_PreviousKeyState[p_Key] = t_State;
285
286 return t_Res;
287 }
288
289 return false;
290}
291
292bool SDKClientPlatformSpecific::GetKeyUp(const int p_Key)
293{
294 if (DoesWindowHaveFocus()) // we got focus? then check key state.
295 {
296 bool t_Res = false;
297 bool t_State = GetAsyncKeyState(p_Key) & 0x8000;
298 if (t_State == false && g_PreviousKeyState[p_Key] == true)
299 t_Res = true;
300 g_PreviousKeyState[p_Key] = t_State;
301
302 return t_Res;
303 }
304
305 return false;
306}
307
309{
310 std::wstringstream t_Wss;
311 // try to get the documents local path
312
313 wchar_t* t_Path = NULL;
314 HRESULT t_Result = SHGetKnownFolderPath(
315 FOLDERID_Documents,
316 0,
317 NULL,
318 &t_Path);
319 // If this fails, the system is either incredibly security locked, or very
320 // bad.
321 if (SUCCEEDED(t_Result))
322 {
323 std::wstringstream t_Wss;
324 t_Wss << t_Path;
325 // Due to the way SHGetKnownFolderPath works. we need to clean this up.
326 CoTaskMemFree(t_Path);
327
328 std::string t_NarrowDocumentsDir =
329 UTF16WstringToUTF8String(t_Wss.str());
330
331 return t_NarrowDocumentsDir;
332 }
333 else
334 {
335 spdlog::warn("Could not get the directory path for the documents.");
336 }
337
338 return std::string("");
339}
340
342 std::string p_Path_UTF8)
343{
344 std::wstring t_WidePath = UTF8StringtoUTF16Wstring(p_Path_UTF8);
345
346 return std::ifstream(t_WidePath, std::ifstream::binary);
347}
348
350 std::string p_Path_UTF8)
351{
352 std::wstring t_WidePath = UTF8StringtoUTF16Wstring(p_Path_UTF8);
353
354 return std::ofstream(t_WidePath, std::ofstream::binary);
355}
356
357bool SDKClientPlatformSpecific::DoesFolderOrFileExist(std::string p_Path_UTF8)
358{
359 std::wstring t_WidePath = UTF8StringtoUTF16Wstring(p_Path_UTF8);
360
361 return std::filesystem::exists(t_WidePath);
362}
363
365 std::string p_Path_UTF8)
366{
367 std::wstring t_WidePath = UTF8StringtoUTF16Wstring(p_Path_UTF8);
368
369 if (!DoesFolderOrFileExist(p_Path_UTF8))
370 {
371 std::filesystem::create_directory(t_WidePath);
372 }
373}
std::wstring UTF8StringtoUTF16Wstring(const std::string &p_String)
bool g_PreviousKeyState[0x87]
All keys till F24 key.
std::string UTF16WstringToUTF8String(const std::wstring &p_Wstring)
CORESDK_API SDKReturnCode CoreSdk_ShutDown()
Shut down the wrapper. This needs to be called last.
void ApplyConsolePosition(const int p_ConsoleCurrentOffset)
Set the current console position. This handles the platform-specific part of advancing the console po...
static void ClearConsole(void)
Clear the console window.
void UpdateInput(void)
Update the current keyboard state.
bool PlatformSpecificInitialization(void)
Initialise things only needed for this platform.
bool GetKeyDown(const int p_Key)
Returns true first time it is called when key is pressed.
bool GetKeyUp(const int p_Key)
Returns true first time it is called when key is released.
static bool CopyString(char *const p_Target, const size_t p_MaxLengthThatWillFitInTarget, const std::string &p_Source)
Copy the given string into the given target.
bool ResizeWindow(const short int p_ConsoleWidth, const short int p_ConsoleHeight, const short int p_ConsoleScrollback)
Resize the window, so the log messages will fit. Make sure not to enter illegal sizes in here or SetC...
bool DoesFolderOrFileExist(std::string p_Path_UTF8)
Check if the given folder or file exists. The folder path given should be in UTF-8 format.
bool PlatformSpecificShutdown(void)
Shut down things only needed for this platform.
bool GetKey(const int p_Key)
Gets key's current state. True is pressed false is not pressed.
std::ofstream GetOutputFileStream(std::string p_Path_UTF8)
Get an output stream for the given file. The file's path should be in UTF-8 format.
std::string GetDocumentsDirectoryPath_UTF8(void)
Get the path to the user's Documents folder. The string should be in UTF-8 format.
void CreateFolderIfItDoesNotExist(std::string p_Path_UTF8)
Create the given folder if it does not exist. The folder path given should be in UTF-8 format.
static const std::string s_SlashForFilesystemPath
The slash character that is used in the filesystem.
std::ifstream GetInputFileStream(std::string p_Path_UTF8)
Get an input stream for the given file. The file's path should be in UTF-8 format.