#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" #pragma comment (lib, "opengl32.lib") #pragma comment (lib, "glew32.lib") #pragma comment(lib, "wbemuuid.lib") 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; #include #include "wacom.h" //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 async_lock() { //std::lock_guard _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); } void async_swap() { SwapBuffers(hDC); //LOG("swap"); } void async_unlock() { //std::lock_guard _lock(async_mutex); gl_count--; if (gl_count == 0) { //LOG("unlock"); wglMakeCurrent(0, 0); gl_mutex.unlock(); } } std::string win32_open_file() { OPENFILENAMEA ofn; char fileName[MAX_PATH] = ""; ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hWnd; ofn.lpstrFilter = "Image Files (*.jpg, *.png)\0*.jpg;*.png"; ofn.lpstrFile = fileName; ofn.nMaxFile = MAX_PATH; ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; ofn.lpstrDefExt = ""; ofn.lpstrInitialDir = "Missions\\"; if (GetOpenFileNameA(&ofn) != NULL) { return fileName; } return ""; } 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){ LogRemote::I.file_close(); }, 0); } int main(int argc, char** argv) { WNDCLASS wc; PIXELFORMATDESCRIPTOR pfd; if (argc == 1) App::I.initLog(); 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)); // 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, L"PanoPainter 0.1.2 alpha - OpenGL 3.1", 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; } } App::I.init(); ShowWindow(hWnd, SW_NORMAL); WacomTablet::I.init(hWnd); SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(IDI_ICON1))); MSG msg; bool running = true; unsigned long t0 = GetTickCount(); unsigned long t1; float one_sec = 0; while (running) { // If there any message in the queue process it if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { running = !(msg.message == WM_QUIT/* || gl.keys[VK_ESCAPE]*/); DispatchMessage(&msg); TranslateMessage(&msg); } else // Otherwise render next frame { t1 = GetTickCount(); float dt = (float)(t1 - t0) / 1000.0f; // force redraw every one second one_sec += dt; if (one_sec > 1.f) { one_sec = 0; App::I.redraw = true; } if (dt > 1.0f / 60.0f) { t0 = t1; if (App::I.redraw) { async_lock(); App::I.redraw = true; glBindFramebuffer(GL_FRAMEBUFFER, 0); App::I.clear(); App::I.update((float)(t1 - t0) / 1000.0f); SwapBuffers(hDC); async_unlock(); //LOG("swap main"); } } // else // { // Sleep((DWORD)(1.0f / 60.0f * 1000.f)); // } } } // Clean up WacomTablet::I.terminate(); DestroyWindow(hWnd); UnregisterClass(className, hInst); } 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_CREATE: BT_SetTerminate(); break; case WM_CLOSE: PostQuitMessage(0); 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: async_lock(); keys[wp] = true; App::I.key_down(convert_key((int)wp)); async_unlock(); break; case WM_SYSKEYUP: case WM_KEYUP: async_lock(); keys[wp] = false; App::I.key_up(convert_key((int)wp)); async_unlock(); break; case WM_CHAR: async_lock(); App::I.key_char((int)wp); async_unlock(); break; case WM_MOUSEMOVE: async_lock(); //TODO: find a way to check if event is mouse/stylus. For now use Mouse for all if (0 && leftDown) { POINT pt = { GET_X_LPARAM(lp), GET_Y_LPARAM(lp) }; // See discussion for why this code is wrong ClientToScreen(hWnd, &pt); MOUSEMOVEPOINT mmpt = { pt.x & 0x0000FFFF, pt.y & 0x0000FFFF, GetMessageTime() }; MOUSEMOVEPOINT rgmmpt[64]; int cmmpt = GetMouseMovePointsEx(sizeof(mmpt), &mmpt, rgmmpt, 64, GMMP_USE_DISPLAY_POINTS); if (cmmpt == -1 || cmmpt == 64) { App::I.mouse_move((float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp), WacomTablet::I.get_pressure(), kEventSource::Mouse); lastPoint = pt; lastTime = GetMessageTime(); break; } POINT ptLastScreen = lastPoint; ClientToScreen(hWnd, &ptLastScreen); int i; for (i = 0; i < cmmpt; i++) { if (rgmmpt[i].x > 32767) rgmmpt[i].x -= 65536; if (rgmmpt[i].y > 32767) rgmmpt[i].y -= 65536; LOG("x %4d y %4d", rgmmpt[i].x, rgmmpt[i].y); if (rgmmpt[i].time < lastTime) break; if (rgmmpt[i].time == lastTime && rgmmpt[i].x == ptLastScreen.x && rgmmpt[i].y == ptLastScreen.y) break; } while (--i >= 0) { POINT ptClient = { rgmmpt[i].x, rgmmpt[i].y }; ScreenToClient(hWnd, &ptClient); if (ptClient.x != lastPoint.x || ptClient.y != lastPoint.y) { lastPoint = ptClient; App::I.mouse_move((float)ptClient.x, (float)ptClient.y, WacomTablet::I.get_pressure(), kEventSource::Mouse); } } lastTime = GetMessageTime(); } else { App::I.mouse_move((float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp), WacomTablet::I.get_pressure(), kEventSource::Mouse); } async_unlock(); break; case WM_LBUTTONDOWN: //TODO: find a way to check if event is mouse/stylus. For now use Mouse for all async_lock(); leftDown = true; SetCapture(hWnd); lastTime = GetMessageTime(); lastPoint = { GET_X_LPARAM(lp), GET_Y_LPARAM(lp) }; App::I.mouse_down(0, (float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp), WacomTablet::I.get_pressure(), kEventSource::Mouse); async_unlock(); break; case WM_LBUTTONUP: async_lock(); WacomTablet::I.reset_pressure(); //TODO: find a way to check if event is mouse/stylus. For now use Mouse for all App::I.mouse_up(0, (float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp), kEventSource::Mouse); ReleaseCapture(); leftDown = false; async_unlock(); break; case WM_RBUTTONDOWN: async_lock(); App::I.mouse_down(1, (float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp), 1.f, kEventSource::Mouse); SetCapture(hWnd); async_unlock(); break; case WM_RBUTTONUP: async_lock(); App::I.mouse_up(1, (float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp), kEventSource::Mouse); ReleaseCapture(); async_unlock(); break; case WM_MOUSEWHEEL: { async_locker lock; POINT pt; pt.x = GET_X_LPARAM(lp); pt.y = GET_Y_LPARAM(lp); ScreenToClient(hWnd, &pt); 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; } } 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); }