1585 lines
48 KiB
C++
1585 lines
48 KiB
C++
#include "pch.h"
|
|
#include "log.h"
|
|
#include "shader.h"
|
|
#include "shape.h"
|
|
#include "texture.h"
|
|
#include "image.h"
|
|
#include "app.h"
|
|
#include "canvas.h"
|
|
#include "keymap.h"
|
|
#include "hmd.h"
|
|
#include "../resource.h"
|
|
|
|
#include <shellscalingapi.h>
|
|
#include <WbemCli.h>
|
|
#include "wacom.h"
|
|
#include <deque>
|
|
#include <chrono>
|
|
#include "abr.h"
|
|
#include "settings.h"
|
|
|
|
#define WM_USER_CLOSE (WM_USER + 1)
|
|
|
|
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);
|
|
HINSTANCE hInst;
|
|
HWND hWnd;
|
|
HDC hDC;
|
|
HGLRC hRC;
|
|
wchar_t* className;
|
|
bool keys[256];
|
|
std::mutex gl_mutex;
|
|
std::mutex async_mutex;
|
|
std::thread::id gl_thread;
|
|
std::map<kKey, int> vkey_map;
|
|
|
|
std::thread hmd_renderer;
|
|
std::thread ui_renderer;
|
|
int vr_frames = 0;
|
|
int running = -1;
|
|
int vr_running = 0;
|
|
std::mutex ui_render_mutex;
|
|
std::condition_variable ui_render_cv;
|
|
|
|
std::deque<std::packaged_task<void()>> render_tasklist;
|
|
std::mutex render_task_mutex;
|
|
std::condition_variable render_cv;
|
|
std::thread render_thread;
|
|
std::thread::id render_thread_id;
|
|
bool render_running = false;
|
|
|
|
int gl_count = 0;
|
|
std::deque<std::packaged_task<void()>> tasklist;
|
|
std::mutex task_mutex;
|
|
std::deque<std::packaged_task<void()>> main_tasklist;
|
|
std::mutex main_task_mutex;
|
|
float timer_stylus = 0;
|
|
float timer_ink_touch = 0;
|
|
float timer_ink_pen = 0;
|
|
bool sandboxed = false;
|
|
|
|
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");
|
|
}
|
|
|
|
BOOL(*GetPointerInfo_fn)(UINT32 pointerId, POINTER_INFO* pointerInfo);
|
|
BOOL(*GetPointerType_fn)(UINT32 pointerId, POINTER_INPUT_TYPE* pointerType);
|
|
BOOL(*GetPointerTouchInfo_fn)(UINT32 pointerId, POINTER_TOUCH_INFO* touchInfo);
|
|
BOOL(*GetPointerPenInfo_fn)(UINT32 pointerId, POINTER_PEN_INFO* penInfo);
|
|
void init_ink_API()
|
|
{
|
|
HMODULE dll = LoadLibrary(L"User32.dll");
|
|
if (!dll)
|
|
{
|
|
LOG("cannot load User32.dll");
|
|
return;
|
|
}
|
|
LOG("loaded User32.dll");
|
|
GetPointerInfo_fn = (decltype(GetPointerInfo_fn))GetProcAddress(dll, "GetPointerInfo");
|
|
GetPointerType_fn = (decltype(GetPointerType_fn))GetProcAddress(dll, "GetPointerType");
|
|
GetPointerTouchInfo_fn = (decltype(GetPointerTouchInfo_fn))GetProcAddress(dll, "GetPointerTouchInfo");
|
|
GetPointerPenInfo_fn = (decltype(GetPointerPenInfo_fn))GetProcAddress(dll, "GetPointerPenInfo");
|
|
}
|
|
|
|
//Returns the last Win32 error, in string format. Returns an empty string if there is no error.
|
|
std::string GetLastErrorAsString()
|
|
{
|
|
//Get the error message, if any.
|
|
DWORD errorMessageID = ::GetLastError();
|
|
if (errorMessageID == 0)
|
|
return std::string(); //No error message has been recorded
|
|
|
|
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);
|
|
|
|
//Free the buffer.
|
|
LocalFree(messageBuffer);
|
|
|
|
return message;
|
|
}
|
|
|
|
void destroy_window()
|
|
{
|
|
std::lock_guard<std::mutex> lock(main_task_mutex);
|
|
main_tasklist.emplace_back([=] {
|
|
SendMessage(hWnd, WM_USER_CLOSE, 0, 0);
|
|
});
|
|
}
|
|
|
|
bool is_render_thread()
|
|
{
|
|
extern std::thread::id render_thread_id;
|
|
extern std::thread::id gl_thread;
|
|
return std::this_thread::get_id() == render_thread_id || std::this_thread::get_id() == gl_thread;
|
|
}
|
|
|
|
template<typename T, typename R = std::result_of<T()>::type>
|
|
std::future<R> render_task_async(T task)
|
|
{
|
|
#ifdef _WIN32
|
|
extern std::deque<std::packaged_task<R()>> render_tasklist;
|
|
extern std::mutex render_task_mutex;
|
|
extern std::condition_variable render_cv;
|
|
std::packaged_task<R()> pt(task);
|
|
std::future<R> f = pt.get_future();
|
|
if (is_render_thread())
|
|
{
|
|
pt();
|
|
}
|
|
else
|
|
{
|
|
{
|
|
std::lock_guard<std::mutex> lock(render_task_mutex);
|
|
render_tasklist.push_back(std::move(pt));
|
|
}
|
|
render_cv.notify_all();
|
|
}
|
|
return f;
|
|
#endif // _WIN32
|
|
}
|
|
|
|
template<typename T, typename R = std::result_of<T()>::type>
|
|
R render_task(T task)
|
|
{
|
|
#ifdef _WIN32
|
|
extern std::deque<std::packaged_task<R()>> render_tasklist;
|
|
extern std::mutex render_task_mutex;
|
|
extern std::condition_variable render_cv;
|
|
std::packaged_task<R()> pt(task);
|
|
std::future<R> f = pt.get_future();
|
|
if (is_render_thread())
|
|
{
|
|
pt();
|
|
}
|
|
else
|
|
{
|
|
{
|
|
std::lock_guard<std::mutex> lock(render_task_mutex);
|
|
render_tasklist.push_back(std::move(pt));
|
|
}
|
|
render_cv.notify_all();
|
|
}
|
|
return f.get();
|
|
#endif // _WIN32
|
|
}
|
|
|
|
void async_lock()
|
|
{
|
|
//std::lock_guard<std::mutex> _lock(async_mutex);
|
|
if (gl_count == 0 || gl_thread != std::this_thread::get_id())
|
|
{
|
|
gl_mutex.lock();
|
|
bool ret = wglMakeCurrent(hDC, hRC);
|
|
if (ret == false)
|
|
LOG("FAILED wglMakeCurrent: %s", GetLastErrorAsString().c_str());
|
|
gl_thread = std::this_thread::get_id();
|
|
//LOG("lock");
|
|
}
|
|
gl_count++;
|
|
//assert(ret == true);
|
|
}
|
|
|
|
bool async_lock_try()
|
|
{
|
|
//std::lock_guard<std::mutex> _lock(async_mutex);
|
|
if (gl_count == 0 || gl_thread != std::this_thread::get_id())
|
|
{
|
|
if (!gl_mutex.try_lock())
|
|
return false;
|
|
bool ret = wglMakeCurrent(hDC, hRC);
|
|
if (ret == false)
|
|
LOG("FAILED wglMakeCurrent: %s", GetLastErrorAsString().c_str());
|
|
gl_thread = std::this_thread::get_id();
|
|
//LOG("lock");
|
|
}
|
|
gl_count++;
|
|
//assert(ret == true);
|
|
return true;
|
|
}
|
|
|
|
void async_swap()
|
|
{
|
|
SwapBuffers(hDC);
|
|
//LOG("swap");
|
|
}
|
|
|
|
void async_unlock()
|
|
{
|
|
//std::lock_guard<std::mutex> _lock(async_mutex);
|
|
gl_count--;
|
|
if (gl_count == 0)
|
|
{
|
|
//LOG("unlock");
|
|
wglMakeCurrent(0, 0);
|
|
gl_mutex.unlock();
|
|
}
|
|
}
|
|
|
|
void win32_render_thread_notify()
|
|
{
|
|
ui_render_cv.notify_all();
|
|
}
|
|
|
|
void win32_show_cursor(bool visible)
|
|
{
|
|
std::lock_guard<std::mutex> lock(main_task_mutex);
|
|
main_tasklist.emplace_back([=] {
|
|
if (visible)
|
|
while (ShowCursor(true) < 0);
|
|
else
|
|
while (ShowCursor(false) >= 0);
|
|
});
|
|
}
|
|
|
|
std::string win32_clipboard_get_text()
|
|
{
|
|
std::string ret;
|
|
if (OpenClipboard(hWnd))
|
|
{
|
|
if (HANDLE h = GetClipboardData(CF_TEXT))
|
|
{
|
|
if (char* s = (char*)GlobalLock(h))
|
|
{
|
|
ret = s;
|
|
GlobalUnlock(h);
|
|
}
|
|
}
|
|
CloseClipboard();
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool win32_clipboard_set_text(const std::string& s)
|
|
{
|
|
bool success = false;
|
|
if (OpenClipboard(hWnd))
|
|
{
|
|
// owned by SetClipboardData
|
|
if (HGLOBAL h = GlobalAlloc(GMEM_MOVEABLE, s.size() + 1))
|
|
{
|
|
if (char* p = (char*)GlobalLock(h))
|
|
{
|
|
std::copy(s.begin(), s.end(), p);
|
|
p[s.size()] = 0; // string null-termination
|
|
GlobalUnlock(h);
|
|
success = true;
|
|
}
|
|
EmptyClipboard();
|
|
SetClipboardData(CF_TEXT, h);
|
|
}
|
|
CloseClipboard();
|
|
}
|
|
return success;
|
|
}
|
|
|
|
std::string win32_open_file(const char* filter)
|
|
{
|
|
OPENFILENAMEA ofn;
|
|
char fileName[MAX_PATH] = "";
|
|
ZeroMemory(&ofn, sizeof(ofn));
|
|
ofn.lStructSize = sizeof(OPENFILENAME);
|
|
ofn.hwndOwner = hWnd;
|
|
ofn.lpstrFilter = filter;
|
|
ofn.lpstrFile = fileName;
|
|
ofn.nMaxFile = MAX_PATH;
|
|
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_NOCHANGEDIR;
|
|
ofn.lpstrDefExt = "";
|
|
ofn.lpstrInitialDir = "";
|
|
if (GetOpenFileNameA(&ofn) != NULL)
|
|
{
|
|
return fileName;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
std::string win32_open_dir()
|
|
{
|
|
BROWSEINFOA bi;
|
|
char Buffer[MAX_PATH];
|
|
ZeroMemory(Buffer, MAX_PATH);
|
|
ZeroMemory(&bi, sizeof(bi));
|
|
bi.hwndOwner = hWnd;
|
|
bi.pszDisplayName = Buffer;
|
|
bi.lpszTitle = "Title";
|
|
bi.ulFlags = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNONLYFSDIRS | BIF_SHAREABLE;
|
|
LPCITEMIDLIST pFolder = SHBrowseForFolderA(&bi);
|
|
if (pFolder == NULL) return "";
|
|
if (!SHGetPathFromIDListA(pFolder, Buffer)) return "";
|
|
return Buffer;
|
|
}
|
|
|
|
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(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(L"WQL", 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(L"WQL", 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(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(L"WQL", 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");
|
|
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;
|
|
}
|
|
|
|
INT_PTR g_iLogHandle = -1;
|
|
static void SetupExceptionHandler()
|
|
{
|
|
// 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
|
|
// g_iLogHandle = BT_OpenLogFile(NULL, BTLF_TEXT);
|
|
// BT_SetLogSizeInEntries(g_iLogHandle, 100);
|
|
// BT_SetLogFlags(g_iLogHandle, BTLF_SHOWTIMESTAMP);
|
|
// BT_SetLogEchoMode(g_iLogHandle, BTLE_STDERR | BTLE_DBGOUT);
|
|
//
|
|
// PCTSTR pszLogFileName = BT_GetLogFileName(g_iLogHandle);
|
|
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)
|
|
{
|
|
auto path = App::I.data_path + "/recovery.ppi";
|
|
Canvas::I->project_save_thread(path);
|
|
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(hWnd, message, "File Recovery", MB_OK | MB_ICONWARNING);
|
|
}
|
|
LogRemote::I.file_close();
|
|
}, 0);
|
|
}
|
|
|
|
// create a reverse map from kKey to VK_XXX
|
|
void init_vk_map()
|
|
{
|
|
for (int k = 1; k < 256; k++) // ignore kKey::Unknown = 0
|
|
{
|
|
for (int vk = 0; vk < 256; vk++)
|
|
{
|
|
if (k == (int)convert_key(vk))
|
|
{
|
|
if (vkey_map.find((kKey)k) == vkey_map.end())
|
|
{
|
|
vkey_map.insert({ (kKey)k, vk });
|
|
}
|
|
else
|
|
{
|
|
LOG("KEY MAP COLLISION %d and %d maps to %d",
|
|
(int)vk, (int)vkey_map[(kKey)k], (int)k);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::mutex hmd_render_mutex;
|
|
std::condition_variable hmd_render_cv;
|
|
Vive* vive = nullptr;
|
|
bool win32_vr_start()
|
|
{
|
|
if (sandboxed)
|
|
return false;
|
|
|
|
vive = new Vive;
|
|
vive->on_draw = [](const glm::mat4& proj, const glm::mat4& view, const glm::mat4& pose) { App::I.vr_draw(proj, view, pose); };
|
|
if (!vive->Initialize())
|
|
{
|
|
delete vive;
|
|
vive = nullptr;
|
|
LOG("VR: failed to initialize vive");
|
|
return false;
|
|
}
|
|
|
|
hmd_renderer = std::thread([&] {
|
|
if (!vive)
|
|
return;
|
|
|
|
BT_SetTerminate();
|
|
LOG("start hmd render thread");
|
|
App::I.has_vr = true;
|
|
vr_running = true;
|
|
|
|
vive->on_analog_button = std::bind(&App::vr_analog, &App::I, std::placeholders::_1,
|
|
std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
|
|
vive->on_button = std::bind(&App::vr_digital, &App::I, std::placeholders::_1,
|
|
std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
|
|
|
|
const float target_tick_rate = 90;
|
|
auto t0 = GetTickCount64();
|
|
float one_sec_timer = 0;
|
|
int frames = 0;
|
|
while (vr_running && running == 1 && vive->Valid())
|
|
{
|
|
std::unique_lock<std::mutex> lock(hmd_render_mutex);
|
|
auto t1 = GetTickCount64();
|
|
float dt = (float)(t1 - t0) / 1000.0f;
|
|
|
|
one_sec_timer += dt;
|
|
if (one_sec_timer >= 1.f)
|
|
{
|
|
one_sec_timer = 0;
|
|
vr_frames = frames;
|
|
frames = 0;
|
|
}
|
|
frames++;
|
|
|
|
vive->Update();
|
|
App::I.vr_active = vive->m_active;
|
|
App::I.vr_controllers[0] = vive->m_controllers[0];
|
|
App::I.vr_head = vive->m_pose;
|
|
App::I.vr_update(dt);
|
|
|
|
if (vr_running && vive->m_active)
|
|
{
|
|
async_lock();
|
|
vive->Draw();
|
|
async_unlock();
|
|
}
|
|
|
|
const int framerate = (1.f / target_tick_rate) * 1000;
|
|
const int diff = framerate - (t1 - t0);
|
|
hmd_render_cv.wait_for(lock, std::chrono::milliseconds(diff));
|
|
t0 = t1;
|
|
}
|
|
App::I.vr_active = false;
|
|
App::I.has_vr = false;
|
|
vr_running = false;
|
|
if (async_lock_try())
|
|
{
|
|
vive->Terminate();
|
|
async_unlock();
|
|
}
|
|
LOG("hmd renderer terminated");
|
|
});
|
|
return true;
|
|
}
|
|
|
|
void win32_vr_stop()
|
|
{
|
|
if (vive)
|
|
{
|
|
vr_running = false;
|
|
if (hmd_renderer.joinable())
|
|
hmd_renderer.join();
|
|
vive->Terminate();
|
|
delete vive;
|
|
vive = nullptr;
|
|
}
|
|
}
|
|
|
|
void win32_save_window_state()
|
|
{
|
|
WINDOWPLACEMENT p;
|
|
GetWindowPlacement(hWnd, &p);
|
|
Settings::set("window-show-cmd", Serializer::Integer(p.showCmd));
|
|
Settings::set("window-rect", Serializer::IVec4({ p.rcNormalPosition.left,
|
|
p.rcNormalPosition.top, p.rcNormalPosition.right, p.rcNormalPosition.bottom }));
|
|
}
|
|
|
|
HBITMAP image_to_hbitmap(const Image& img)
|
|
{
|
|
BITMAPINFOHEADER bmih;
|
|
memset(&bmih, 0, sizeof(BITMAPINFOHEADER));
|
|
bmih.biWidth = img.width;
|
|
bmih.biHeight = img.height;
|
|
bmih.biBitCount = 32;
|
|
bmih.biCompression = BI_RGB;
|
|
bmih.biSize = sizeof(BITMAPINFOHEADER);
|
|
bmih.biPlanes = 1;
|
|
BITMAPINFO* bmi = (BITMAPINFO*)&bmih;
|
|
return CreateDIBitmap(GetDC(NULL), &bmih, CBM_INIT, img.data(), bmi, DIB_RGB_COLORS);
|
|
}
|
|
|
|
LRESULT CALLBACK splash_proc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (Msg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
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);
|
|
float z = (float)x / 96.f;
|
|
|
|
static char base_path[MAX_PATH];
|
|
GetCurrentDirectoryA(MAX_PATH, base_path);
|
|
std::string path = std::string(base_path) + "\\data\\splash.jpg";
|
|
//auto hbitmap = (HBITMAP)LoadImageA(NULL, path.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
|
|
Image img;
|
|
img.load_file(path);
|
|
img.flip();
|
|
auto hbitmap = image_to_hbitmap(img.resize(512 * z, 200 * z));
|
|
SendMessage(GetDlgItem(hWndDlg, IDC_STATIC_IMAGE), STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbitmap);
|
|
SetDlgItemText(hWndDlg, IDC_STATIC_VERSION, g_version_number_w);
|
|
|
|
RECT r;
|
|
GetClientRect(hWndDlg, &r);
|
|
MoveWindow(GetDlgItem(hWndDlg, IDC_STATIC_IMAGE), 0, 0, 512 * z, 200 * z, TRUE);
|
|
SetWindowPos(hWndDlg, HWND_TOP, 0, 0, 512 * z, 200 * z, SWP_NOMOVE);
|
|
|
|
HWND hWndVersion = GetDlgItem(hWndDlg, IDC_STATIC_VERSION);
|
|
RECT rv;
|
|
GetClientRect(hWndVersion, &rv);
|
|
MoveWindow(hWndVersion, 0, 200 * z - (rv.bottom - rv.top), r.right - r.left, rv.bottom - rv.top, TRUE);
|
|
|
|
return TRUE;
|
|
}
|
|
case WM_USER + 1:
|
|
PostQuitMessage(0);
|
|
return TRUE;
|
|
}
|
|
|
|
return DefWindowProc(hWndDlg, Msg, wParam, lParam);;
|
|
}
|
|
|
|
HWND splash_dialog;
|
|
void splash_thread_loop()
|
|
{
|
|
splash_dialog = CreateDialog(hInst, MAKEINTRESOURCE(IDD_SPLASH), NULL, reinterpret_cast<DLGPROC>(splash_proc));
|
|
|
|
MSG msg;
|
|
while (GetMessage(&msg, 0, 0, 0) > 0)
|
|
{
|
|
if (IsDialogMessage(splash_dialog, &msg))
|
|
{
|
|
DispatchMessage(&msg);
|
|
TranslateMessage(&msg);
|
|
}
|
|
}
|
|
|
|
DestroyWindow(splash_dialog);
|
|
}
|
|
|
|
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 render_thread_main()
|
|
{
|
|
uint32_t count = 0;
|
|
render_thread_id = std::this_thread::get_id();
|
|
render_running = true;
|
|
while (render_running == 1)
|
|
{
|
|
std::deque<std::packaged_task<void()>> working_list;
|
|
|
|
// move the task list locally to free the queue for other threads
|
|
{
|
|
std::unique_lock<std::mutex> lock(render_task_mutex);
|
|
render_cv.wait(lock, [] { return render_tasklist.empty() && render_running ? false : true; });
|
|
working_list = std::move(render_tasklist);
|
|
}
|
|
|
|
//{
|
|
// std::lock_guard<std::mutex> lock(task_mutex);
|
|
// working_list.insert(working_list.end(),
|
|
// std::make_move_iterator(tasklist.begin()),
|
|
// std::make_move_iterator(tasklist.end()));
|
|
// tasklist.clear();
|
|
//}
|
|
|
|
// execute the tasks
|
|
if (!working_list.empty())
|
|
{
|
|
async_lock();
|
|
while (!working_list.empty())
|
|
{
|
|
LOG("render task %d", count);
|
|
count++;
|
|
working_list.front()();
|
|
working_list.pop_front();
|
|
}
|
|
async_unlock();
|
|
}
|
|
}
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
WNDCLASS wc;
|
|
PIXELFORMATDESCRIPTOR pfd;
|
|
|
|
App::I.initLog();
|
|
|
|
init_shcore_API();
|
|
init_ink_API();
|
|
|
|
if(SetProcessDpiAwareness_fn)
|
|
SetProcessDpiAwareness_fn(PROCESS_PER_MONITOR_DPI_AWARE);
|
|
|
|
|
|
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);
|
|
}
|
|
else
|
|
{
|
|
fclose(fp_check);
|
|
LOG("data files ok");
|
|
}
|
|
|
|
std::thread dialog_thread(splash_thread_loop);
|
|
|
|
init_vk_map();
|
|
|
|
SetupExceptionHandler();
|
|
BT_SetTerminate();
|
|
|
|
read_WMI_info();
|
|
|
|
App::I.create();
|
|
|
|
// Inizialize data structures to zero
|
|
memset(&wc, 0, sizeof(wc));
|
|
memset(&keys, 0, sizeof(keys));
|
|
memset(&pfd, 0, sizeof(pfd));
|
|
|
|
// Create the main window
|
|
|
|
hInst = GetModuleHandle(NULL);
|
|
className = L"EngineMain";
|
|
|
|
wc.hInstance = hInst;
|
|
wc.lpfnWndProc = (WNDPROC)WndProc;
|
|
wc.lpszClassName = className;
|
|
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
|
|
RegisterClass(&wc);
|
|
|
|
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.zoom *= (float)x / 96.f;
|
|
|
|
int show_cmd = SW_NORMAL;
|
|
Settings::value<Serializer::Integer>("window-show-cmd", show_cmd);
|
|
DWORD wnd_style = WS_OVERLAPPEDWINDOW;
|
|
//if (show_cmd == SW_MAXIMIZE)
|
|
// wnd_style != WS_MAXIMIZE;
|
|
|
|
RECT clientRect = { 0, 0, (int)App::I.width * App::I.zoom, (int)App::I.height * App::I.zoom };
|
|
POINT clientPos = { CW_USEDEFAULT, CW_USEDEFAULT };
|
|
if (Settings::has("window-rect"))
|
|
{
|
|
auto wnd_rect = Settings::value<Serializer::IVec4>("window-rect");
|
|
App::I.width = wnd_rect.z - wnd_rect.x;
|
|
App::I.height = wnd_rect.w - wnd_rect.y;
|
|
clientRect = { wnd_rect.x, wnd_rect.y, wnd_rect.z, wnd_rect.w };
|
|
clientPos = { wnd_rect.x, wnd_rect.y };
|
|
}
|
|
else
|
|
{
|
|
AdjustWindowRect(&clientRect, wnd_style, false);
|
|
}
|
|
hWnd = CreateWindow(wc.lpszClassName, L"PanoPainter", wnd_style, clientPos.x, clientPos.y,
|
|
(float)(clientRect.right - clientRect.left),
|
|
(float)(clientRect.bottom - clientRect.top), 0, 0, hInst, 0);
|
|
|
|
// Setup GL Rendering Context
|
|
pfd.nSize = sizeof(pfd);
|
|
pfd.nVersion = 1;
|
|
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
|
|
pfd.iPixelType = PFD_TYPE_RGBA;
|
|
pfd.cColorBits = 32;
|
|
pfd.cDepthBits = 24;
|
|
pfd.iLayerType = PFD_MAIN_PLANE;
|
|
|
|
hDC = GetDC(hWnd);
|
|
int pxfmt = ChoosePixelFormat(hDC, &pfd);
|
|
SetPixelFormat(hDC, pxfmt, &pfd);
|
|
hRC = wglCreateContext(hDC); // Create OpenGL 2.1 or less
|
|
wglMakeCurrent(hDC, hRC);
|
|
|
|
// Initialize extensions
|
|
if (glewInit() != GLEW_OK)
|
|
return 0;
|
|
|
|
LOG("GL version: %s", glGetString(GL_VERSION));
|
|
LOG("GL vendor: %s", glGetString(GL_VENDOR));
|
|
LOG("GL renderer: %s", glGetString(GL_RENDERER));
|
|
|
|
static wchar_t window_title[512];
|
|
swprintf_s(window_title, L"PanoPainter %s (%s)", g_version_number_w,
|
|
str2wstr((char*)glGetString(GL_RENDERER)).c_str());
|
|
|
|
// If supported create a 3.1 context
|
|
if (wglewIsSupported("WGL_ARB_create_context"))
|
|
{
|
|
int contex_attribs[] =
|
|
{
|
|
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
|
|
WGL_CONTEXT_MINOR_VERSION_ARB, 1,
|
|
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
|
|
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
0
|
|
};
|
|
int pixel_attribs[] =
|
|
{
|
|
WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
|
|
WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
|
|
WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
|
|
WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB,
|
|
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
|
|
WGL_COLOR_BITS_ARB, 32,
|
|
WGL_DEPTH_BITS_ARB, 24,
|
|
//WGL_STENCIL_BITS_ARB, 8,
|
|
//WGL_SAMPLE_BUFFERS_ARB, 1, // Number of buffers (must be 1 at time of writing)
|
|
//WGL_SAMPLES_ARB, 4, // Number of samples
|
|
0
|
|
};
|
|
UINT numFormat;
|
|
|
|
wglMakeCurrent(NULL, NULL);
|
|
wglDeleteContext(hRC);
|
|
DestroyWindow(hWnd);
|
|
|
|
hWnd = CreateWindow(wc.lpszClassName, window_title, wnd_style, clientPos.x, clientPos.y,
|
|
(float)(clientRect.right - clientRect.left),
|
|
(float)(clientRect.bottom - clientRect.top), 0, 0, hInst, 0);
|
|
|
|
hDC = GetDC(hWnd);
|
|
wglChoosePixelFormatARB(hDC, pixel_attribs, nullptr, 1, &pxfmt, &numFormat);
|
|
SetPixelFormat(hDC, pxfmt, &pfd);
|
|
hRC = wglCreateContextAttribsARB(hDC, NULL, contex_attribs);
|
|
wglMakeCurrent(hDC, hRC);
|
|
}
|
|
else
|
|
{
|
|
LOG("WGL_ARB_create_context not supported");
|
|
// If not supported, go fuck yourself we are not gonna support your shitty device
|
|
return -1; // A negative number because you are a negative one
|
|
}
|
|
|
|
//wglSwapIntervalEXT(1);
|
|
|
|
bool start_in_vr = false;
|
|
if (argc > 1)
|
|
{
|
|
switch (const_hash(argv[1]))
|
|
{
|
|
case const_hash("convert"):
|
|
App::I.initShaders();
|
|
App::I.cmd_convert(argv[2], argv[3]);
|
|
return 0;
|
|
case const_hash("-vrmode"):
|
|
start_in_vr = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// link: https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-registertouchwindow
|
|
if (RegisterTouchWindow(hWnd, 0) == 0)
|
|
{
|
|
LOG("RegisterTouchWindow error: %s", GetLastErrorAsString().c_str());
|
|
}
|
|
|
|
wglMakeCurrent(NULL, NULL);
|
|
|
|
running = 1;
|
|
render_thread = std::thread(render_thread_main);
|
|
|
|
LOG("init app");
|
|
App::I.init();
|
|
|
|
if (!sandboxed)
|
|
{
|
|
LOG("init WinTab");
|
|
WacomTablet::I.init(hWnd);
|
|
}
|
|
else
|
|
{
|
|
LOG("SKIP init WinTab");
|
|
}
|
|
|
|
LOG("change icon");
|
|
SendMessage(hWnd, WM_SETICON, ICON_SMALL,
|
|
(LPARAM)LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(IDI_ICON1)));
|
|
|
|
ui_renderer = std::thread([&] {
|
|
BT_SetTerminate();
|
|
LOG("start render thread");
|
|
const float target_fps = 10;
|
|
const float target_tick_rate = 10;
|
|
unsigned long t0 = GetTickCount64();
|
|
unsigned long t1;
|
|
bool first_frame = true;
|
|
int frames = 0;
|
|
float one_sec = 0;
|
|
float render_timer = 0;
|
|
float frame_timer = 0;
|
|
while(running == 1)
|
|
{
|
|
t1 = GetTickCount64();
|
|
float dt = (float)(t1 - t0) / 1000.0f;
|
|
one_sec += dt;
|
|
render_timer += dt;
|
|
frame_timer += dt;
|
|
timer_stylus += dt;
|
|
timer_ink_touch += dt;
|
|
timer_ink_pen += dt;
|
|
|
|
if (one_sec > 1.f)
|
|
{
|
|
static wchar_t title_fps[512];
|
|
if (App::I.vr_active)
|
|
swprintf_s(title_fps, L"%s - %d fps - %d vr fps", window_title, frames, vr_frames);
|
|
else
|
|
swprintf_s(title_fps, L"%s - %d fps", window_title, frames);
|
|
|
|
std::lock_guard<std::mutex> lock(main_task_mutex);
|
|
main_tasklist.emplace_back([=] {
|
|
SetWindowText(hWnd, title_fps);
|
|
});
|
|
|
|
one_sec = 0;
|
|
frames = 0;
|
|
}
|
|
|
|
{
|
|
std::deque<std::packaged_task<void()>> working_list;
|
|
{
|
|
std::lock_guard<std::mutex> lock(task_mutex);
|
|
working_list = std::move(tasklist);
|
|
}
|
|
|
|
if (!working_list.empty())
|
|
{
|
|
while (!working_list.empty())
|
|
{
|
|
working_list.front()();
|
|
working_list.pop_front();
|
|
}
|
|
//LOG("clear");
|
|
//WacomTablet::I.m_stylus = false;
|
|
//WacomTablet::I.m_eraser = false;
|
|
}
|
|
}
|
|
|
|
if (first_frame)
|
|
{
|
|
first_frame = false;
|
|
WINDOWPLACEMENT wp;
|
|
GetWindowPlacement(hWnd, &wp);
|
|
wp.showCmd = show_cmd;
|
|
SetWindowPlacement(hWnd, &wp);
|
|
// GetClientRect(hWnd, &clientRect);
|
|
// App::I.width = clientRect.right - clientRect.left;
|
|
// App::I.height = clientRect.bottom - clientRect.top;
|
|
}
|
|
|
|
App::I.tick(dt);
|
|
|
|
std::unique_lock<std::mutex> lock(ui_render_mutex);
|
|
if (render_timer > 1.0f / target_fps)
|
|
{
|
|
App::I.redraw = true;
|
|
render_timer = 0;
|
|
}
|
|
if (timer_stylus > 0.1 && (WacomTablet::I.m_stylus || WacomTablet::I.m_eraser))
|
|
{
|
|
WacomTablet::I.m_stylus = false;
|
|
WacomTablet::I.m_eraser = false;
|
|
App::I.redraw = true;
|
|
}
|
|
if (timer_ink_pen > 0.1 && WacomTablet::I.m_ink_pen)
|
|
{
|
|
WacomTablet::I.m_ink_pen = false;
|
|
App::I.redraw = true;
|
|
}
|
|
if (timer_ink_touch > 0.1 && WacomTablet::I.m_ink_touch)
|
|
{
|
|
WacomTablet::I.m_ink_touch = false;
|
|
App::I.redraw = true;
|
|
}
|
|
|
|
if (App::I.redraw)
|
|
{
|
|
App::I.update(frame_timer);
|
|
render_task([frame_timer]
|
|
{
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
App::I.clear();
|
|
App::I.draw(frame_timer);
|
|
SwapBuffers(hDC);
|
|
});
|
|
frame_timer = 0;
|
|
frames++;
|
|
}
|
|
|
|
const int framerate = (1.f / target_tick_rate) * 1000;
|
|
const int diff = framerate - (t1 - t0);
|
|
//render_cv.wait_for(lock, std::chrono::milliseconds(diff));
|
|
//std::this_thread::sleep_for(std::chrono::milliseconds(30));
|
|
t0 = t1;
|
|
}
|
|
LOG("renderer terminated");
|
|
});
|
|
|
|
SetTimer(hWnd, 1, 500, NULL);
|
|
|
|
if (start_in_vr)
|
|
App::I.vr_start();
|
|
|
|
LOG("show main window");
|
|
SetForegroundWindow(hWnd);
|
|
//ShowWindow(hWnd, show_cmd);
|
|
|
|
SendMessage(splash_dialog, WM_USER + 1, 0, 0);
|
|
if (dialog_thread.joinable())
|
|
dialog_thread.join();
|
|
|
|
MSG msg;
|
|
LOG("start main loop");
|
|
while (running == 1)
|
|
{
|
|
// If there any message in the queue process it
|
|
auto present = App::I.animate || App::I.redraw ?
|
|
PeekMessage(&msg, 0, 0, 0, PM_REMOVE) : GetMessage(&msg, 0, 0, 0);
|
|
|
|
if (msg.message == WM_QUIT)
|
|
running = 0;
|
|
|
|
if (present)
|
|
{
|
|
DispatchMessage(&msg);
|
|
TranslateMessage(&msg);
|
|
}
|
|
|
|
// list of tasks for the main thread
|
|
{
|
|
std::deque<std::packaged_task<void()>> working_list;
|
|
{
|
|
std::lock_guard<std::mutex> lock(main_task_mutex);
|
|
working_list = std::move(main_tasklist);
|
|
}
|
|
|
|
if (!working_list.empty())
|
|
{
|
|
while (!working_list.empty())
|
|
{
|
|
working_list.front()();
|
|
working_list.pop_front();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!tasklist.empty())
|
|
ui_render_cv.notify_all();
|
|
}
|
|
// Clean up
|
|
WacomTablet::I.terminate();
|
|
render_cv.notify_all();
|
|
|
|
UnregisterClass(className, hInst);
|
|
LogRemote::I.stop();
|
|
}
|
|
|
|
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
|
|
{
|
|
static bool leftDown = false;
|
|
static DWORD lastTime;
|
|
static glm::vec2 lastPoint;
|
|
|
|
auto extra = GetMessageExtraInfo();
|
|
// if ((extra & 0xFFFFFF00) == 0xFF515700)
|
|
// LOG("source %s", extra & 0x80 ? "touch" : "pen");
|
|
|
|
switch (msg)
|
|
{
|
|
case WM_USER_CLOSE:
|
|
running = 0;
|
|
ui_render_cv.notify_all();
|
|
if (ui_renderer.joinable())
|
|
ui_renderer.join();
|
|
if (hmd_renderer.joinable())
|
|
hmd_renderer.join();
|
|
App::I.terminate();
|
|
PostQuitMessage(0);
|
|
render_running = false;
|
|
if (render_thread.joinable())
|
|
render_thread.join();
|
|
return 0;
|
|
case WM_PAINT:
|
|
App::I.redraw = true;
|
|
break;
|
|
case WM_CREATE:
|
|
BT_SetTerminate();
|
|
break;
|
|
case WM_CLOSE:
|
|
{
|
|
std::lock_guard<std::mutex> lock(task_mutex);
|
|
tasklist.emplace_back([=] {
|
|
if (App::I.request_close())
|
|
{
|
|
destroy_window();
|
|
}
|
|
});
|
|
return 1;
|
|
break;
|
|
}
|
|
case WM_SIZE:
|
|
{
|
|
auto w = (float)LOWORD(lp);
|
|
auto h = (float)HIWORD(lp);
|
|
if (h != 0 && running == 1)
|
|
{
|
|
std::lock_guard<std::mutex> lock(task_mutex);
|
|
tasklist.emplace_back([=]
|
|
{
|
|
App::I.resize(w, h);
|
|
App::I.redraw = true;
|
|
});
|
|
}
|
|
break;
|
|
}
|
|
case WM_ACTIVATE:
|
|
{
|
|
std::lock_guard<std::mutex> lock(task_mutex);
|
|
tasklist.emplace_back([=] {
|
|
int active = GET_WM_ACTIVATE_STATE(wp, lp);
|
|
WacomTablet::I.set_focus(active);
|
|
static BYTE keys[256];
|
|
if (GetKeyboardState(keys))
|
|
{
|
|
bool alt = keys[VK_MENU] & 0x80;
|
|
for (auto k : vkey_map)
|
|
{
|
|
// ignore alt + tab
|
|
if (alt && k.first == kKey::KeyTab)
|
|
continue;
|
|
bool down = keys[k.second] & 0x80;
|
|
if (App::I.keys[(int)k.first] && !down)
|
|
App::I.key_up(k.first);
|
|
else if(!App::I.keys[(int)k.first] && down)
|
|
App::I.key_down(k.first);
|
|
}
|
|
}
|
|
});
|
|
break;
|
|
}
|
|
// case WM_TOUCH:
|
|
// {
|
|
// std::lock_guard<std::mutex> lock(task_mutex);
|
|
// tasklist.emplace_back([=] {
|
|
// //LOG("touch");
|
|
// });
|
|
// break;
|
|
// }
|
|
case WT_PACKET:
|
|
{
|
|
App::I.set_stylus();
|
|
timer_stylus = 0;
|
|
std::lock_guard<std::mutex> lock(task_mutex);
|
|
tasklist.emplace_back([=] {
|
|
WacomTablet::I.handle_message(hWnd, msg, wp, lp);
|
|
});
|
|
break;
|
|
}
|
|
case WM_SYSKEYDOWN:
|
|
case WM_KEYDOWN:
|
|
if ((lp >> 30 & 1) == 0 && // ignore repeated
|
|
!(wp == VK_TAB && App::I.keys[(int)kKey::KeyAlt])) // ignore alt+tab
|
|
{
|
|
std::lock_guard<std::mutex> lock(task_mutex);
|
|
tasklist.emplace_back([wp] {
|
|
App::I.key_down(convert_key((int)wp));
|
|
});
|
|
}
|
|
break;
|
|
case WM_SYSKEYUP:
|
|
case WM_KEYUP:
|
|
if (!(wp == VK_TAB && App::I.keys[(int)kKey::KeyAlt])) // ignore alt+tab
|
|
{
|
|
std::lock_guard<std::mutex> lock(task_mutex);
|
|
tasklist.emplace_back([wp] {
|
|
App::I.key_up(convert_key((int)wp));
|
|
});
|
|
}
|
|
break;
|
|
case WM_CHAR:
|
|
{
|
|
std::lock_guard<std::mutex> lock(task_mutex);
|
|
tasklist.emplace_back([wp]{
|
|
App::I.key_char((int)wp);
|
|
});
|
|
}
|
|
break;
|
|
case WM_MOUSEMOVE:
|
|
{
|
|
/*
|
|
RECT r;
|
|
POINT curpos;
|
|
GetWindowRect(hWnd, &r);
|
|
glm::vec2 center = { r.left + (r.right - r.left) / 2, r.top + (r.bottom - r.top) / 2 };
|
|
GetCursorPos(&curpos);
|
|
SetCursorPos(center.x, center.y);
|
|
|
|
//glm::vec2 cur = { GET_X_LPARAM(lp), GET_Y_LPARAM(lp) };
|
|
glm::vec2 sz = { App::I.width, App::I.height };
|
|
glm::vec2 diff = { curpos.x - center.x, curpos.y - center.y };
|
|
lastPoint = glm::clamp(lastPoint + diff, { 0, 0 }, sz);
|
|
|
|
*/
|
|
lastPoint = { GET_X_LPARAM(lp), GET_Y_LPARAM(lp) };
|
|
|
|
auto pt = lastPoint;
|
|
std::lock_guard<std::mutex> lock(task_mutex);
|
|
tasklist.emplace_back([pt, extra, p = WacomTablet::I.get_pressure()]{
|
|
kEventSource pointer_source;
|
|
if (WacomTablet::I.m_ink_pen || WacomTablet::I.m_ink_touch)
|
|
{
|
|
pointer_source = WacomTablet::I.m_ink_pen ? kEventSource::Stylus : kEventSource::Touch;
|
|
}
|
|
else
|
|
{
|
|
pointer_source = WacomTablet::I.m_stylus ? kEventSource::Stylus : kEventSource::Mouse;
|
|
if ((extra & 0xFFFFFF00) == 0xFF515700)
|
|
pointer_source = kEventSource::Touch;
|
|
}
|
|
App::I.mouse_move((float)pt.x, (float)pt.y, p, pointer_source, WacomTablet::I.m_eraser);
|
|
});
|
|
}
|
|
break;
|
|
case WM_LBUTTONDOWN:
|
|
{
|
|
SetCapture(hWnd);
|
|
std::lock_guard<std::mutex> lock(task_mutex);
|
|
auto pt = lastPoint;
|
|
tasklist.emplace_back([pt, extra, hWnd, p = WacomTablet::I.get_pressure()]{
|
|
kEventSource pointer_source;
|
|
if (WacomTablet::I.m_ink_pen || WacomTablet::I.m_ink_touch)
|
|
{
|
|
pointer_source = WacomTablet::I.m_ink_pen ? kEventSource::Stylus : kEventSource::Touch;
|
|
}
|
|
else
|
|
{
|
|
pointer_source = WacomTablet::I.m_stylus ? kEventSource::Stylus : kEventSource::Mouse;
|
|
if ((extra & 0xFFFFFF00) == 0xFF515700)
|
|
pointer_source = kEventSource::Touch;
|
|
}
|
|
App::I.mouse_down(0, (float)pt.x, (float)pt.y, p, pointer_source, WacomTablet::I.m_eraser);
|
|
});
|
|
}
|
|
break;
|
|
case WM_LBUTTONUP:
|
|
{
|
|
ReleaseCapture();
|
|
std::lock_guard<std::mutex> lock(task_mutex);
|
|
auto pt = lastPoint;
|
|
tasklist.emplace_back([pt, extra] {
|
|
WacomTablet::I.reset_pressure();
|
|
kEventSource pointer_source;
|
|
if (WacomTablet::I.m_ink_pen || WacomTablet::I.m_ink_touch)
|
|
{
|
|
pointer_source = WacomTablet::I.m_ink_pen ? kEventSource::Stylus : kEventSource::Touch;
|
|
}
|
|
else
|
|
{
|
|
pointer_source = WacomTablet::I.m_stylus ? kEventSource::Stylus : kEventSource::Mouse;
|
|
if ((extra & 0xFFFFFF00) == 0xFF515700)
|
|
pointer_source = kEventSource::Touch;
|
|
}
|
|
App::I.mouse_up(0, (float)pt.x, (float)pt.y, pointer_source, WacomTablet::I.m_eraser);
|
|
});
|
|
}
|
|
break;
|
|
case WM_RBUTTONDOWN:
|
|
{
|
|
SetCapture(hWnd);
|
|
std::lock_guard<std::mutex> lock(task_mutex);
|
|
auto pt = lastPoint;
|
|
tasklist.emplace_back([pt, extra, hWnd] {
|
|
kEventSource pointer_source;
|
|
if (WacomTablet::I.m_ink_pen || WacomTablet::I.m_ink_touch)
|
|
{
|
|
pointer_source = WacomTablet::I.m_ink_pen ? kEventSource::Stylus : kEventSource::Touch;
|
|
}
|
|
else
|
|
{
|
|
pointer_source = WacomTablet::I.m_stylus ? kEventSource::Stylus : kEventSource::Mouse;
|
|
if ((extra & 0xFFFFFF00) == 0xFF515700)
|
|
pointer_source = kEventSource::Touch;
|
|
}
|
|
App::I.mouse_down(1, (float)pt.x, (float)pt.y, 1.f, pointer_source, 0);
|
|
});
|
|
}
|
|
break;
|
|
case WM_RBUTTONUP:
|
|
{
|
|
ReleaseCapture();
|
|
std::lock_guard<std::mutex> lock(task_mutex);
|
|
auto pt = lastPoint;
|
|
tasklist.emplace_back([pt, extra] {
|
|
kEventSource pointer_source;
|
|
if (WacomTablet::I.m_ink_pen || WacomTablet::I.m_ink_touch)
|
|
{
|
|
pointer_source = WacomTablet::I.m_ink_pen ? kEventSource::Stylus : kEventSource::Touch;
|
|
}
|
|
else
|
|
{
|
|
pointer_source = WacomTablet::I.m_stylus ? kEventSource::Stylus : kEventSource::Mouse;
|
|
if ((extra & 0xFFFFFF00) == 0xFF515700)
|
|
pointer_source = kEventSource::Touch;
|
|
}
|
|
App::I.mouse_up(1, (float)pt.x, (float)pt.y, pointer_source, 0);
|
|
});
|
|
}
|
|
break;
|
|
case WM_MOUSEWHEEL:
|
|
{
|
|
POINT pt;
|
|
pt.x = GET_X_LPARAM(lp);
|
|
pt.y = GET_Y_LPARAM(lp);
|
|
ScreenToClient(hWnd, &pt);
|
|
{
|
|
std::lock_guard<std::mutex> lock(task_mutex);
|
|
tasklist.emplace_back([pt, wp] {
|
|
App::I.mouse_scroll((float)pt.x, (float)pt.y,
|
|
(float)GET_WHEEL_DELTA_WPARAM(wp) / (float)WHEEL_DELTA);
|
|
});
|
|
}
|
|
break;
|
|
}
|
|
case WM_POINTERUPDATE:
|
|
{
|
|
POINTER_TOUCH_INFO touchInfo;
|
|
POINTER_PEN_INFO penInfo;
|
|
POINTER_INFO pointerInfo;
|
|
UINT32 pointerId = GET_POINTERID_WPARAM(wp);
|
|
POINTER_INPUT_TYPE pointerType = PT_POINTER;
|
|
|
|
if(!GetPointerInfo_fn)
|
|
break;
|
|
|
|
// Retrieve common pointer information
|
|
if (!GetPointerInfo_fn(pointerId, &pointerInfo))
|
|
{
|
|
// failure, call GetLastError()
|
|
}
|
|
else
|
|
{
|
|
// success, process pointerInfo
|
|
if (!GetPointerType_fn(pointerId, &pointerType))
|
|
{
|
|
// failure, call GetLastError()
|
|
// set PT_POINTER to fall to default case below
|
|
pointerType = PT_POINTER;
|
|
}
|
|
switch (pointerType)
|
|
{
|
|
case PT_TOUCH:
|
|
// Retrieve touch information
|
|
if (!GetPointerTouchInfo_fn(pointerId, &touchInfo))
|
|
{
|
|
// failure, call GetLastError()
|
|
}
|
|
else
|
|
{
|
|
// success, process touchInfo
|
|
// mark as handled to skip call to DefWindowProc
|
|
//fHandled = TRUE;
|
|
timer_ink_touch = 0;
|
|
WacomTablet::I.m_ink_touch = true;
|
|
WacomTablet::I.m_pen_pres = 1;
|
|
}
|
|
break;
|
|
case PT_PEN:
|
|
// Retrieve pen information
|
|
if (!GetPointerPenInfo_fn(pointerId, &penInfo))
|
|
{
|
|
// failure, call GetLastError()
|
|
}
|
|
else
|
|
{
|
|
// success, process penInfo
|
|
// mark as handled to skip call to DefWindowProc
|
|
//fHandled = TRUE;
|
|
//penInfo.pressure
|
|
timer_ink_pen = 0;
|
|
WacomTablet::I.m_ink_pen = true;
|
|
WacomTablet::I.m_pen_pres = (float)penInfo.pressure / 1024.f;
|
|
App::I.set_stylus();
|
|
}
|
|
break;
|
|
default:
|
|
if (!GetPointerInfo_fn(pointerId, &pointerInfo))
|
|
{
|
|
// failure.
|
|
}
|
|
else
|
|
{
|
|
// success, proceed with pointerInfo.
|
|
//fHandled = HandleGenericPointerInfo(&pointerInfo);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// avoid annoying alt system menu
|
|
if (wp == SC_KEYMENU && (lp >> 16) <= 0) return 0;
|
|
|
|
return DefWindowProc(hWnd, msg, wp, lp);
|
|
}
|
|
|
|
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
|
{
|
|
int argc = 0;
|
|
|
|
// convert args from char to wchar
|
|
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 main(argc, argv);
|
|
}
|