595 lines
18 KiB
C++
595 lines
18 KiB
C++
#include "pch.h"
|
|
|
|
#include "platform_windows/windows_bootstrap_helpers.h"
|
|
#include "platform_windows/windows_window_shell.h"
|
|
|
|
#include "app.h"
|
|
#include "canvas.h"
|
|
#include "legacy_gl_runtime_dispatch.h"
|
|
#include "legacy_preference_storage.h"
|
|
#include "log.h"
|
|
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <ctime>
|
|
#include <iomanip>
|
|
#include <WbemCli.h>
|
|
#include <sstream>
|
|
#include <shellscalingapi.h>
|
|
#include <string>
|
|
|
|
#if __has_include(<renderdoc_app.h>)
|
|
#include <renderdoc_app.h>
|
|
#define USE_RENDERDOC
|
|
#endif
|
|
|
|
namespace pp::platform::windows {
|
|
void set_async_render_context(HDC hdc, HGLRC hrc);
|
|
}
|
|
|
|
namespace pp::platform::windows {
|
|
|
|
#ifdef USE_RENDERDOC
|
|
RENDERDOC_API_1_4_0* rdoc_api = NULL;
|
|
|
|
bool win32_renderdoc_init()
|
|
{
|
|
// At init, on windows
|
|
if (HMODULE mod = GetModuleHandleA("renderdoc.dll"))
|
|
{
|
|
pRENDERDOC_GetAPI RENDERDOC_GetAPI =
|
|
(pRENDERDOC_GetAPI)GetProcAddress(mod, "RENDERDOC_GetAPI");
|
|
return RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_1_2, (void**)&rdoc_api);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void win32_renderdoc_frame_start()
|
|
{
|
|
if (rdoc_api)
|
|
rdoc_api->StartFrameCapture(NULL, NULL);
|
|
}
|
|
|
|
void win32_renderdoc_frame_end()
|
|
{
|
|
if (rdoc_api)
|
|
rdoc_api->EndFrameCapture(NULL, NULL);
|
|
}
|
|
#else
|
|
void win32_renderdoc_frame_start() { }
|
|
void win32_renderdoc_frame_end() { }
|
|
#endif
|
|
|
|
HRESULT(*GetDpiForMonitor_fn)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT* dpiX, UINT* dpiY);
|
|
HRESULT(*SetProcessDpiAwareness_fn)(PROCESS_DPI_AWARENESS value);
|
|
|
|
void init_shcore_API()
|
|
{
|
|
HMODULE dll = LoadLibrary(L"Shcore.dll");
|
|
if (!dll)
|
|
{
|
|
LOG("cannot load Shcore.dll");
|
|
return;
|
|
}
|
|
LOG("loaded Shcore.dll");
|
|
GetDpiForMonitor_fn = (decltype(GetDpiForMonitor_fn))GetProcAddress(dll, "GetDpiForMonitor");
|
|
SetProcessDpiAwareness_fn = (decltype(SetProcessDpiAwareness_fn))GetProcAddress(dll, "SetProcessDpiAwareness");
|
|
}
|
|
|
|
// Returns the last Win32 error, in string format. Returns an empty string if there is no error.
|
|
std::string GetLastErrorAsString()
|
|
{
|
|
DWORD errorMessageID = ::GetLastError();
|
|
if (errorMessageID == 0)
|
|
return std::string();
|
|
|
|
LPSTR messageBuffer = nullptr;
|
|
size_t size = FormatMessageA(
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
|
|
|
|
std::string message(messageBuffer, size);
|
|
LocalFree(messageBuffer);
|
|
return message;
|
|
}
|
|
|
|
void ensure_runtime_data_directory()
|
|
{
|
|
FILE* fp_check = fopen("data\\layout.xml", "rb");
|
|
if (!fp_check)
|
|
{
|
|
LOG("data files not found");
|
|
static char path[MAX_PATH];
|
|
GetModuleFileNameA(NULL, path, MAX_PATH);
|
|
LOG("current dir %s", path);
|
|
PathRemoveFileSpecA(path);
|
|
SetCurrentDirectoryA(path);
|
|
LOG("change dir to %s", path);
|
|
return;
|
|
}
|
|
|
|
fclose(fp_check);
|
|
LOG("data files ok");
|
|
}
|
|
|
|
int run_winmain_entry(int (*entry_point)(int, char**))
|
|
{
|
|
int argc = 0;
|
|
|
|
auto wargs = CommandLineToArgvW(GetCommandLine(), &argc);
|
|
auto argv = new char*[argc + 1];
|
|
for (int i = 0; i < argc; i++)
|
|
{
|
|
auto len = wcslen(wargs[i]) + 1;
|
|
argv[i] = new char[len];
|
|
wcstombs_s(nullptr, argv[i], len, wargs[i], len);
|
|
}
|
|
|
|
LocalFree(wargs);
|
|
|
|
return entry_point(argc, argv);
|
|
}
|
|
|
|
void setup_exception_handler()
|
|
{
|
|
// Setup exception handler
|
|
BT_SetAppName(_T("PanoPainter"));
|
|
BT_SetAppVersion(g_version_w);
|
|
//BT_SetSupportEMail(_T("your@email.com"));
|
|
BT_SetFlags(BTF_DETAILEDMODE | BTF_ATTACHREPORT | BTF_SCREENCAPTURE);
|
|
|
|
// = BugTrapServer ===========================================
|
|
//BT_SetSupportServer(_T("omigamedev.ddns.net"), 8088);
|
|
BT_SetSupportEMail(_T("info@panopainter.com"));
|
|
// - or -
|
|
//BT_SetSupportServer(_T("127.0.0.1"), 9999);
|
|
|
|
// = BugTrapWebServer ========================================
|
|
//BT_SetSupportServer(_T("http://localhost/BugTrapWebServer/RequestHandler.aspx"), BUGTRAP_HTTP_PORT);
|
|
//BT_SetSupportServer(_T("http://omigamedev.ddns.net:8088/source/Server/BugTrapWebServer/RequestHandler.aspx"), BUGTRAP_HTTP_PORT);
|
|
BT_SetSupportServer(_T("http://panopainter.com/bug/"), BUGTRAP_HTTP_PORT);
|
|
|
|
// required for VS 2005 & 2008
|
|
BT_InstallSehFilter();
|
|
|
|
// Add custom log file using default name
|
|
TCHAR wpath[MAX_PATH];
|
|
//GetFullPathNameW(L"panopainter-log.txt", 1024, wpath, nullptr);
|
|
auto log_file = App::I->data_path + "/panopainter-log.txt";
|
|
std::mbstowcs(wpath, log_file.c_str(), log_file.size());
|
|
BT_AddLogFile(wpath);
|
|
|
|
BT_SetPreErrHandler([](INT_PTR){
|
|
if (Canvas::I && Canvas::I->m_unsaved)
|
|
{
|
|
auto t = std::time(nullptr);
|
|
auto tm = *std::localtime(&t);
|
|
std::ostringstream oss;
|
|
oss << std::put_time(&tm, "%d-%m-%Y %H-%M-%S");
|
|
|
|
auto path = App::I->data_path + "/" + App::I->doc_name + "-recovery (" + oss.str() + ").ppi";
|
|
Canvas::I->project_save_thread(path, false);
|
|
static char abspath[MAX_PATH];
|
|
GetFullPathNameA(path.c_str(), MAX_PATH, abspath, NULL);
|
|
static char message[4096];
|
|
snprintf(message, sizeof(message), "File recovered in: %s", abspath);
|
|
MessageBoxA(retained_state().hWnd, message, "File Recovery", MB_OK | MB_ICONWARNING);
|
|
}
|
|
LogRemote::I.file_close();
|
|
}, 0);
|
|
|
|
BT_SetTerminate();
|
|
}
|
|
|
|
int read_WMI_info()
|
|
{
|
|
// see: http://win32easy.blogspot.co.uk/2011/03/wmi-in-c-query-everyting-from-your-os.html
|
|
|
|
HRESULT hRes = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
|
if (FAILED(hRes))
|
|
{
|
|
LOG("Unable to launch COM: %x", hRes);
|
|
return 1;
|
|
}
|
|
|
|
if ((FAILED(hRes = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_CONNECT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, 0))))
|
|
{
|
|
LOG("Unable to initialize security: %x", hRes);
|
|
return 1;
|
|
}
|
|
|
|
IWbemLocator* pLocator = NULL;
|
|
if (FAILED(hRes = CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_ALL, IID_PPV_ARGS(&pLocator))))
|
|
{
|
|
LOG("Unable to create a WbemLocator: %x", hRes);
|
|
return 1;
|
|
}
|
|
|
|
IWbemServices* pService = NULL;
|
|
if (FAILED(hRes = pLocator->ConnectServer(BSTR(L"root\\CIMV2"), NULL, NULL, NULL, WBEM_FLAG_CONNECT_USE_MAX_WAIT, NULL, NULL, &pService)))
|
|
{
|
|
pLocator->Release();
|
|
LOG("Unable to connect to \"CIMV2\": %x", hRes);
|
|
return 1;
|
|
}
|
|
|
|
auto log_field = [](const wchar_t* section, IWbemClassObject* clsObj, const wchar_t* field) {
|
|
VARIANT vRet;
|
|
CIMTYPE pType;
|
|
VariantInit(&vRet);
|
|
if (SUCCEEDED(clsObj->Get(field, 0, &vRet, &pType, NULL)))
|
|
{
|
|
if (pType == CIM_STRING && pType != CIM_EMPTY && pType != CIM_ILLEGAL)
|
|
{
|
|
LOGW(L"%s %s: %s", section, field, vRet.bstrVal);
|
|
}
|
|
else if (pType == CIM_UINT32 && pType != CIM_EMPTY && pType != CIM_ILLEGAL)
|
|
{
|
|
LOGW(L"%s %s: %d", section, field, vRet.uintVal);
|
|
}
|
|
|
|
VariantClear(&vRet);
|
|
}
|
|
};
|
|
|
|
auto get_int = [](IWbemClassObject* clsObj, const wchar_t* field) {
|
|
VARIANT vRet;
|
|
CIMTYPE pType;
|
|
VariantInit(&vRet);
|
|
int ret = 0;
|
|
if (SUCCEEDED(clsObj->Get(field, 0, &vRet, &pType, NULL)))
|
|
{
|
|
if (pType == CIM_UINT32 && pType != CIM_EMPTY && pType != CIM_ILLEGAL)
|
|
ret = vRet.uintVal;
|
|
VariantClear(&vRet);
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
|
|
// GET DEVICE INFO
|
|
{
|
|
IEnumWbemClassObject* pEnumerator = NULL;
|
|
if (FAILED(hRes = pService->ExecQuery(BSTR(L"WQL"), BSTR(L"SELECT * FROM Win32_ComputerSystem"), WBEM_FLAG_FORWARD_ONLY, NULL, &pEnumerator)))
|
|
{
|
|
pLocator->Release();
|
|
pService->Release();
|
|
LOG("Unable to retrive desktop monitors: %x", hRes);
|
|
return 1;
|
|
}
|
|
|
|
IWbemClassObject* clsObj = NULL;
|
|
int numElems;
|
|
while ((hRes = pEnumerator->Next(WBEM_INFINITE, 1, &clsObj, (ULONG*)&numElems)) != WBEM_S_FALSE)
|
|
{
|
|
if (FAILED(hRes))
|
|
break;
|
|
|
|
log_field(L"Machine", clsObj, L"Name");
|
|
log_field(L"Machine", clsObj, L"Model");
|
|
log_field(L"Machine", clsObj, L"Manufacturer");
|
|
|
|
clsObj->Release();
|
|
}
|
|
pEnumerator->Release();
|
|
}
|
|
|
|
// GET OS INFO
|
|
{
|
|
IEnumWbemClassObject* pEnumerator = NULL;
|
|
if (FAILED(hRes = pService->ExecQuery(BSTR(L"WQL"), BSTR(L"SELECT * FROM Win32_OperatingSystem"), WBEM_FLAG_FORWARD_ONLY, NULL, &pEnumerator)))
|
|
{
|
|
pLocator->Release();
|
|
pService->Release();
|
|
LOG("Unable to retrive desktop monitors: %x", hRes);
|
|
return 1;
|
|
}
|
|
|
|
IWbemClassObject* clsObj = NULL;
|
|
int numElems;
|
|
while ((hRes = pEnumerator->Next(WBEM_INFINITE, 1, &clsObj, (ULONG*)&numElems)) != WBEM_S_FALSE)
|
|
{
|
|
if (FAILED(hRes))
|
|
break;
|
|
|
|
log_field(L"OS", clsObj, L"Name");
|
|
log_field(L"OS", clsObj, L"Version");
|
|
log_field(L"OS", clsObj, L"Locale");
|
|
log_field(L"OS", clsObj, L"OSProductSuite");
|
|
log_field(L"OS", clsObj, L"Manufacturer");
|
|
log_field(L"OS", clsObj, L"Description");
|
|
|
|
clsObj->Release();
|
|
}
|
|
pEnumerator->Release();
|
|
}
|
|
|
|
|
|
pService->Release();
|
|
pService = NULL;
|
|
if (FAILED(hRes = pLocator->ConnectServer(BSTR(L"root\\Microsoft\\Windows\\DeviceGuard"), NULL, NULL, NULL, WBEM_FLAG_CONNECT_USE_MAX_WAIT, NULL, NULL, &pService)))
|
|
{
|
|
pLocator->Release();
|
|
LOG("Unable to connect to \"DeviceGuard\": %x", hRes);
|
|
return 1;
|
|
}
|
|
|
|
// GET DEVICE GUARD
|
|
{
|
|
IEnumWbemClassObject* pEnumerator = NULL;
|
|
if (FAILED(hRes = pService->ExecQuery(BSTR(L"WQL"), BSTR(L"SELECT * FROM Win32_DeviceGuard"), WBEM_FLAG_FORWARD_ONLY, NULL, &pEnumerator)))
|
|
{
|
|
pLocator->Release();
|
|
pService->Release();
|
|
LOG("Unable to retrive desktop monitors: %x", hRes);
|
|
return 1;
|
|
}
|
|
|
|
IWbemClassObject* clsObj = NULL;
|
|
int numElems;
|
|
while ((hRes = pEnumerator->Next(WBEM_INFINITE, 1, &clsObj, (ULONG*)&numElems)) != WBEM_S_FALSE)
|
|
{
|
|
if (FAILED(hRes))
|
|
break;
|
|
|
|
if (get_int(clsObj, L"CodeIntegrityPolicyEnforcementStatus") > 0)
|
|
{
|
|
LOG("SANDBOX DETECTED");
|
|
retained_state().sandboxed = true;
|
|
}
|
|
|
|
SAFEARRAY *psaNames = NULL;
|
|
if (SUCCEEDED(clsObj->GetNames(0, WBEM_FLAG_ALWAYS | WBEM_FLAG_NONSYSTEM_ONLY, 0, &psaNames)))
|
|
{
|
|
// Get the number of properties.
|
|
long lLower, lUpper;
|
|
BSTR PropName = NULL;
|
|
SafeArrayGetLBound(psaNames, 1, &lLower);
|
|
SafeArrayGetUBound(psaNames, 1, &lUpper);
|
|
|
|
for (long i = lLower; i <= lUpper; i++)
|
|
{
|
|
// Get this property.
|
|
SafeArrayGetElement(psaNames, &i, &PropName);
|
|
|
|
LOGW(L"Prop: %s", PropName);
|
|
log_field(L"DeviceGuard", clsObj, PropName);
|
|
|
|
SysFreeString(PropName);
|
|
}
|
|
|
|
SafeArrayDestroy(psaNames);
|
|
}
|
|
|
|
clsObj->Release();
|
|
}
|
|
pEnumerator->Release();
|
|
}
|
|
|
|
|
|
pLocator->Release();
|
|
CoUninitialize();
|
|
|
|
return 0;
|
|
}
|
|
|
|
MainWindowStartupState initialize_main_window_startup_state()
|
|
{
|
|
auto startup = MainWindowStartupState {};
|
|
const auto hInst = GetModuleHandle(NULL);
|
|
const auto className = L"EngineMain";
|
|
|
|
startup.window_class.hInstance = hInst;
|
|
startup.window_class.lpfnWndProc = (WNDPROC)main_window_proc;
|
|
startup.window_class.lpszClassName = className;
|
|
startup.window_class.hbrBackground = (HBRUSH)COLOR_WINDOW;
|
|
startup.window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
|
|
RegisterClass(&startup.window_class);
|
|
|
|
auto monitor = MonitorFromWindow(0, MONITOR_DEFAULTTOPRIMARY);
|
|
auto x = unsigned{ 96 };
|
|
auto y = unsigned{ 96 };
|
|
if (GetDpiForMonitor_fn)
|
|
GetDpiForMonitor_fn(monitor, MDT_EFFECTIVE_DPI, &x, &y);
|
|
App::I->display_density = (float)x / 96.f;
|
|
|
|
const auto window_preferences = pp::panopainter::read_legacy_window_preferences(SW_NORMAL);
|
|
if (window_preferences.has_ui_scale)
|
|
App::I->zoom = window_preferences.ui_scale;
|
|
else
|
|
App::I->zoom = (float)x / 96.f;
|
|
|
|
startup.show_command = window_preferences.show_command;
|
|
startup.client_rect = {
|
|
0,
|
|
0,
|
|
static_cast<LONG>(App::I->width * App::I->zoom),
|
|
static_cast<LONG>(App::I->height * App::I->zoom),
|
|
};
|
|
if (window_preferences.has_window_rect)
|
|
{
|
|
auto wnd_rect = window_preferences.window_rect;
|
|
App::I->width = wnd_rect.z - wnd_rect.x;
|
|
App::I->height = wnd_rect.w - wnd_rect.y;
|
|
startup.client_rect = { wnd_rect.x, wnd_rect.y, wnd_rect.z, wnd_rect.w };
|
|
startup.client_pos = { wnd_rect.x, wnd_rect.y };
|
|
}
|
|
else
|
|
{
|
|
AdjustWindowRect(&startup.client_rect, startup.window_style, false);
|
|
}
|
|
|
|
return startup;
|
|
}
|
|
|
|
void create_main_window(const MainWindowStartupState& startup, HWND& hWnd, HINSTANCE hInst, const wchar_t* window_title)
|
|
{
|
|
const int window_width = startup.client_rect.right - startup.client_rect.left;
|
|
const int window_height = startup.client_rect.bottom - startup.client_rect.top;
|
|
hWnd = CreateWindow(
|
|
startup.window_class.lpszClassName,
|
|
window_title,
|
|
startup.window_style,
|
|
startup.client_pos.x,
|
|
startup.client_pos.y,
|
|
window_width,
|
|
window_height,
|
|
0,
|
|
0,
|
|
hInst,
|
|
0);
|
|
}
|
|
|
|
void initialize_pixel_format_descriptor(PIXELFORMATDESCRIPTOR& pixel_format)
|
|
{
|
|
pixel_format.nSize = sizeof(pixel_format);
|
|
pixel_format.nVersion = 1;
|
|
pixel_format.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
|
|
pixel_format.iPixelType = PFD_TYPE_RGBA;
|
|
pixel_format.cColorBits = 32;
|
|
pixel_format.cDepthBits = 24;
|
|
pixel_format.iLayerType = PFD_MAIN_PLANE;
|
|
}
|
|
|
|
bool load_glad_entry_points(HDC device_context)
|
|
{
|
|
if (!gladLoadGL())
|
|
{
|
|
LOG("gladLoadGL() failed");
|
|
return false;
|
|
}
|
|
if (!gladLoadWGL(device_context))
|
|
{
|
|
LOG("gladLoadWGL() failed");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
pp::renderer::gl::OpenGlRuntimeInfo log_runtime_info()
|
|
{
|
|
auto runtime_info = pp::renderer::gl::OpenGlRuntimeInfo {};
|
|
const auto runtime_info_result = pp::renderer::gl::query_opengl_runtime_info(
|
|
pp::legacy::gl_runtime::runtime_info_dispatch());
|
|
if (runtime_info_result.ok())
|
|
{
|
|
runtime_info = runtime_info_result.value();
|
|
LOG("GL version: %s", runtime_info.version);
|
|
LOG("GL vendor: %s", runtime_info.vendor);
|
|
LOG("GL renderer: %s", runtime_info.renderer);
|
|
}
|
|
else
|
|
{
|
|
LOG("OpenGL runtime info query failed: %s", runtime_info_result.status().message);
|
|
}
|
|
|
|
return runtime_info;
|
|
}
|
|
|
|
bool upgrade_to_core_gl_context(const MainWindowStartupState& startup, HWND& hWnd, HINSTANCE hInst, const wchar_t* window_title, OpenGlWindowContext& context)
|
|
{
|
|
if (!GLAD_WGL_ARB_create_context)
|
|
{
|
|
LOG("WGL_ARB_create_context not supported");
|
|
// If not supported, go fuck yourself we are not gonna support your shitty device
|
|
return false;
|
|
}
|
|
|
|
const auto wgl_config = pp::renderer::gl::windows_wgl_core_context_3_3_config();
|
|
UINT num_format = 0;
|
|
|
|
wglMakeCurrent(NULL, NULL);
|
|
wglDeleteContext(context.render_context);
|
|
DestroyWindow(hWnd);
|
|
|
|
create_main_window(startup, hWnd, hInst, window_title);
|
|
|
|
context.device_context = GetDC(hWnd);
|
|
int pixel_format = 0;
|
|
wglChoosePixelFormatARB(
|
|
context.device_context,
|
|
wgl_config.pixel_format_attributes.data(),
|
|
nullptr,
|
|
1,
|
|
&pixel_format,
|
|
&num_format);
|
|
SetPixelFormat(context.device_context, pixel_format, &startup.pixel_format);
|
|
context.render_context = wglCreateContextAttribsARB(
|
|
context.device_context,
|
|
NULL,
|
|
wgl_config.context_attributes.data());
|
|
wglMakeCurrent(context.device_context, context.render_context);
|
|
set_async_render_context(context.device_context, context.render_context);
|
|
return true;
|
|
}
|
|
|
|
MainStartupResult initialize_main_window_and_gl(MainWindowStartupState& startup, HWND& hWnd, HINSTANCE hInst, wchar_t* window_title, OpenGlWindowContext& context)
|
|
{
|
|
create_main_window(startup, hWnd, hInst, L"PanoPainter");
|
|
initialize_pixel_format_descriptor(startup.pixel_format);
|
|
|
|
context.device_context = GetDC(hWnd);
|
|
const int pixel_format = ChoosePixelFormat(context.device_context, &startup.pixel_format);
|
|
SetPixelFormat(context.device_context, pixel_format, &startup.pixel_format);
|
|
context.render_context = wglCreateContext(context.device_context);
|
|
wglMakeCurrent(context.device_context, context.render_context);
|
|
set_async_render_context(context.device_context, context.render_context);
|
|
|
|
if (!load_glad_entry_points(context.device_context))
|
|
return MainStartupResult::GladLoadFailure;
|
|
|
|
context.runtime_info = log_runtime_info();
|
|
|
|
#ifdef USE_RENDERDOC
|
|
if (!win32_renderdoc_init())
|
|
LOG("Renderdoc not started");
|
|
#endif
|
|
|
|
const auto renderer_name = std::string(context.runtime_info.renderer != nullptr ? context.runtime_info.renderer : "");
|
|
swprintf_s(
|
|
window_title,
|
|
512,
|
|
L"PanoPainter %s (%s)",
|
|
g_version_number_w,
|
|
str2wstr(renderer_name).c_str());
|
|
|
|
if (!upgrade_to_core_gl_context(startup, hWnd, hInst, window_title, context))
|
|
return MainStartupResult::MissingCoreContextSupport;
|
|
|
|
return MainStartupResult::Ok;
|
|
}
|
|
|
|
BOOL UnadjustWindowRectEx(LPRECT prc, DWORD dwStyle, BOOL fMenu, DWORD dwExStyle)
|
|
{
|
|
RECT rc;
|
|
SetRectEmpty(&rc);
|
|
BOOL fRc = AdjustWindowRectEx(&rc, dwStyle, fMenu, dwExStyle);
|
|
if (fRc) {
|
|
prc->left -= rc.left;
|
|
prc->top -= rc.top;
|
|
prc->right -= rc.right;
|
|
prc->bottom -= rc.bottom;
|
|
}
|
|
return fRc;
|
|
}
|
|
|
|
void _pre_call_callback(const char* name, void* funcptr, int len_args, ...)
|
|
{
|
|
assert(App::I->is_render_thread());
|
|
}
|
|
|
|
void _post_call_callback(const char* name, void* funcptr, int len_args, ...)
|
|
{
|
|
GLenum error_code;
|
|
error_code = glad_glGetError();
|
|
|
|
if (error_code != pp::renderer::gl::no_error_code())
|
|
{
|
|
LOG("ERROR %d in %s\n", error_code, name);
|
|
}
|
|
}
|
|
|
|
}
|