Files
panopainter/src/main.cpp

816 lines
24 KiB
C++

#include "pch.h"
#include "log.h"
#include "shader.h"
#include "shape.h"
#include "texture.h"
#include "image.h"
#include "app.h"
#include "keymap.h"
#include "../resource.h"
#include <shellscalingapi.h>
#include <WbemCli.h>
#include "wacom.h"
#include <deque>
#include <chrono>
#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;
int gl_count = 0;
std::deque<std::packaged_task<void()>> tasklist;
std::mutex task_mutex;
//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()
{
SendMessage(hWnd, WM_USER_CLOSE, 0, 0);
}
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();
}
}
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;
}
struct async_locker
{
async_locker() { async_lock(); }
~async_locker() { async_unlock(); }
};
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;
VariantInit(&vRet);
if (SUCCEEDED(clsObj->Get(field, 0, &vRet, NULL, NULL)) && vRet.vt == VT_BSTR)
{
LOGW(L"%s %s: %s", section, field, vRet.bstrVal);
VariantClear(&vRet);
}
};
// 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();
pLocator->Release();
CoUninitialize();
return 0;
}
INT_PTR g_iLogHandle = -1;
static void SetupExceptionHandler()
{
// Setup exception handler
BT_SetAppName(_T("PanoPainter"));
//BT_SetSupportEMail(_T("your@email.com"));
BT_SetFlags(BTF_DETAILEDMODE | BTF_ATTACHREPORT | BTF_SCREENCAPTURE);
// = BugTrapServer ===========================================
BT_SetSupportServer(_T("omigamedev.ddns.net"), 8088);
// - 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);
// 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[1024];
GetFullPathNameW(L"panopainter-log.txt", 1024, wpath, nullptr);
BT_AddLogFile(wpath);
BT_SetPreErrHandler([](INT_PTR){
if (ui::Canvas::I)
{
auto path = App::I.data_path + "/recovery.ppi";
ui::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(NULL, message, "File Recovery", MB_OK | MB_ICONWARNING);
}
LogRemote::I.file_close();
}, 0);
}
int main(int argc, char** argv)
{
WNDCLASS wc;
PIXELFORMATDESCRIPTOR pfd;
SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
if (argc == 1)
App::I.initLog();
if (!App::I.check_license())
{
MessageBoxA(NULL, "Unable to verify this demo license, please make sure you are connected to Internet.",
"PanoPainter - License Error", MB_ICONERROR | MB_OK);
return -1;
}
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");
}
SetupExceptionHandler();
BT_SetTerminate();
read_WMI_info();
App::I.create();
RECT clientRect = { 0, 0, (int)App::I.width, (int)App::I.height };
// 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);
AdjustWindowRect(&clientRect, WS_OVERLAPPEDWINDOW, false);
hWnd = CreateWindow(wc.lpszClassName, L"PanoPainter", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, clientRect.right - clientRect.left,
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 = 16;
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", g_version_number_w);
// 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, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, clientRect.right - clientRect.left,
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
{
// 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
}
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;
default:
break;
}
}
auto monitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
auto x = unsigned{};
auto y = unsigned{};
GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &x, &y);
App::I.zoom = (float)x / 96.f;
// SetWindowPos(hWnd, NULL, 0, 0,
// (clientRect.right - clientRect.left) * App::I.zoom,
// (clientRect.bottom - clientRect.top) * App::I.zoom,
// SWP_NOREPOSITION | SWP_NOOWNERZORDER);
App::I.init();
ShowWindow(hWnd, SW_NORMAL);
WacomTablet::I.init(hWnd);
SendMessage(hWnd, WM_SETICON, ICON_SMALL,
(LPARAM)LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(IDI_ICON1)));
SetTimer(hWnd, 1, 500, [](HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) {
App::I.redraw = true;
});
bool running = true;
std::mutex render_mutex;
std::condition_variable render_cv;
std::thread renderer([&] {
BT_SetTerminate();
const float target_fps = 10;
unsigned long t0 = GetTickCount();
unsigned long t1;
int frames = 0;
float one_sec = 0;
float render_timer = 0;
float frame_timer = 0;
while(running)
{
t1 = GetTickCount();
float dt = (float)(t1 - t0) / 1000.0f;
one_sec += dt;
render_timer += dt;
frame_timer += dt;
t0 = t1;
if (one_sec > 1.f)
{
static wchar_t title_fps[512];
swprintf_s(title_fps, L"%s - %d fps", window_title, frames);
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);
}
async_lock();
while (!working_list.empty())
{
working_list.front()();
working_list.pop_front();
}
async_unlock();
}
std::unique_lock<std::mutex> lock(render_mutex);
if (render_timer > 1.0f / target_fps)
{
App::I.redraw = true;
render_timer = 0;
}
if (App::I.redraw)
{
async_lock();
App::I.redraw = true;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
App::I.clear();
App::I.update(frame_timer);
SwapBuffers(hDC);
async_unlock();
frame_timer = 0;
//LOG("swap main");
}
frames++;
const int framerate = (1.f / target_fps) * 1000;
const int diff = framerate - (t1 - t0);
render_cv.wait_for(lock, std::chrono::milliseconds(diff));
}
});
MSG msg;
while (running)
{
// 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);
running = !(msg.message == WM_QUIT/* || gl.keys[VK_ESCAPE]*/);
if (present)
{
DispatchMessage(&msg);
TranslateMessage(&msg);
}
if (!tasklist.empty())
render_cv.notify_all();
}
render_cv.notify_all();
if (renderer.joinable())
renderer.join();
App::I.terminate();
// Clean up
WacomTablet::I.terminate();
//DestroyWindow(hWnd);
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 POINT lastPoint;
switch (msg)
{
case WM_USER_CLOSE:
DestroyWindow(hWnd);
PostQuitMessage(0);
break;
case WM_PAINT:
App::I.redraw = true;
break;
case WM_CREATE:
BT_SetTerminate();
break;
case WM_CLOSE:
if (App::I.request_close())
PostQuitMessage(0);
else
return true;
break;
case WM_SIZE:
{
auto w = (float)LOWORD(lp);
auto h = (float)HIWORD(lp);
if (h != 0)
{
async_locker lock;
App::I.resize(w, h);
App::I.clear();
App::I.redraw = true;
App::I.update(0.f);
SwapBuffers(hDC);
}
break;
}
case WM_ACTIVATE:
{
int active = GET_WM_ACTIVATE_STATE(wp, lp);
WacomTablet::I.set_focus(active);
break;
}
case WT_PACKET:
WacomTablet::I.handle_message(hWnd, msg, wp, lp);
break;
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
{
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:
{
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:
{
std::lock_guard<std::mutex> lock(task_mutex);
tasklist.emplace_back([lp, p = WacomTablet::I.get_pressure()]{
App::I.mouse_move((float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp), p, kEventSource::Mouse);
});
}
break;
case WM_LBUTTONDOWN:
SetCapture(hWnd);
{
std::lock_guard<std::mutex> lock(task_mutex);
tasklist.emplace_back([lp, p = WacomTablet::I.get_pressure()]{
App::I.mouse_down(0, (float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp), p, kEventSource::Mouse);
});
}
break;
case WM_LBUTTONUP:
WacomTablet::I.reset_pressure();
ReleaseCapture();
{
std::lock_guard<std::mutex> lock(task_mutex);
tasklist.emplace_back([lp]{
App::I.mouse_up(0, (float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp), kEventSource::Mouse);
});
}
break;
case WM_RBUTTONDOWN:
SetCapture(hWnd);
{
std::lock_guard<std::mutex> lock(task_mutex);
tasklist.emplace_back([lp]{
App::I.mouse_down(1, (float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp), 1.f, kEventSource::Mouse);
});
}
break;
case WM_RBUTTONUP:
ReleaseCapture();
{
std::lock_guard<std::mutex> lock(task_mutex);
tasklist.emplace_back([lp]{
App::I.mouse_up(1, (float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp), kEventSource::Mouse);
});
}
break;
case WM_MOUSEWHEEL:
{
async_locker lock;
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;
// Retrieve common pointer information
if (!GetPointerInfo(pointerId, &pointerInfo))
{
// failure, call GetLastError()
}
else
{
// success, process pointerInfo
if (!GetPointerType(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(pointerId, &touchInfo))
{
// failure, call GetLastError()
}
else
{
// success, process touchInfo
// mark as handled to skip call to DefWindowProc
//fHandled = TRUE;
}
break;
case PT_PEN:
// Retrieve pen information
if (!GetPointerPenInfo(pointerId, &penInfo))
{
// failure, call GetLastError()
}
else
{
// success, process penInfo
// mark as handled to skip call to DefWindowProc
//fHandled = TRUE;
//penInfo.pressure
}
break;
default:
if (!GetPointerInfo(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(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine, _In_ int nShowCmd)
{
LPWSTR *szArgList{ nullptr };
int argc{ 0 };
char** argv{ nullptr };
szArgList = CommandLineToArgvW(GetCommandLine(), &argc);
if (szArgList == NULL)
{
MessageBox(NULL, L"Unable to parse command line", L"Error", MB_OK);
return 10;
}
argv = new char*[argc + 1];
for (int i = 0; i < argc; i++)
{
auto len = wcslen(szArgList[i]) + 1;
argv[i] = new char[len];
wcstombs_s(nullptr, argv[i], len, szArgList[i], len);
}
LocalFree(szArgList);
return main(argc, argv);
}