From e7d9aaf9bf756f7eb0e0f4d499def93d4389e8ae Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 20 Jul 2019 13:24:55 +0200 Subject: Use C for Windows implementation This avoids C++ binary compatibility and dependency hassles when cross-compiling. --- pugl/pugl_win.c | 791 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ pugl/pugl_win.cpp | 781 ----------------------------------------------------- wscript | 13 +- 3 files changed, 795 insertions(+), 790 deletions(-) create mode 100644 pugl/pugl_win.c delete mode 100644 pugl/pugl_win.cpp diff --git a/pugl/pugl_win.c b/pugl/pugl_win.c new file mode 100644 index 0000000..b1fa41a --- /dev/null +++ b/pugl/pugl_win.c @@ -0,0 +1,791 @@ +/* + Copyright 2012-2019 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_win.c Windows/WGL Pugl Implementation. +*/ + +#include "pugl/pugl_internal.h" + +#include +#include + +#include + +#include +#include +#include +#include + +#ifndef WM_MOUSEWHEEL +# define WM_MOUSEWHEEL 0x020A +#endif +#ifndef WM_MOUSEHWHEEL +# define WM_MOUSEHWHEEL 0x020E +#endif +#ifndef WHEEL_DELTA +# define WHEEL_DELTA 120 +#endif +#ifndef GWLP_USERDATA +# define GWLP_USERDATA (-21) +#endif + +#define PUGL_LOCAL_CLOSE_MSG (WM_USER + 50) + +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_COLOR_BITS_ARB 0x2014 +#define WGL_RED_BITS_ARB 0x2015 +#define WGL_GREEN_BITS_ARB 0x2017 +#define WGL_BLUE_BITS_ARB 0x2019 +#define WGL_ALPHA_BITS_ARB 0x201b +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_FULL_ACCELERATION_ARB 0x2027 +#define WGL_TYPE_RGBA_ARB 0x202b +#define WGL_SAMPLE_BUFFERS_ARB 0x2041 +#define WGL_SAMPLES_ARB 0x2042 + +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 + +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 + +struct PuglInternalsImpl { + HWND hwnd; + HDC hdc; + HGLRC hglrc; + double timerFrequency; +}; + +// Scoped class to manage the fake window used during window creation +typedef struct { + HWND hwnd; + HDC hdc; +} PuglFakeWindow; + +static const TCHAR* DEFAULT_CLASSNAME = "Pugl"; + +static PuglFakeWindow +puglMakeFakeWindow(HWND wnd) +{ + const PuglFakeWindow fakeWin = {wnd, wnd ? GetDC(wnd) : 0}; + return fakeWin; +} + +LRESULT CALLBACK +wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); + +PuglInternals* +puglInitInternals(void) +{ + return (PuglInternals*)calloc(1, sizeof(PuglInternals)); +} + +void +puglEnterContext(PuglView* view) +{ + PAINTSTRUCT ps; + BeginPaint(view->impl->hwnd, &ps); + +#ifdef PUGL_HAVE_GL + if (view->ctx_type == PUGL_GL) { + wglMakeCurrent(view->impl->hdc, view->impl->hglrc); + } +#endif +} + +void +puglLeaveContext(PuglView* view, bool flush) +{ +#ifdef PUGL_HAVE_GL + if (view->ctx_type == PUGL_GL && flush) { + SwapBuffers(view->impl->hdc); + } +#endif + + PAINTSTRUCT ps; + EndPaint(view->impl->hwnd, &ps); +} + +static PIXELFORMATDESCRIPTOR +puglGetPixelFormatDescriptor(const PuglHints* hints) +{ + const int rgbBits = hints->red_bits + hints->green_bits + hints->blue_bits; + + PIXELFORMATDESCRIPTOR pfd; + ZeroMemory(&pfd, sizeof(pfd)); + 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 = (BYTE)rgbBits; + pfd.cRedBits = (BYTE)hints->red_bits; + pfd.cGreenBits = (BYTE)hints->green_bits; + pfd.cBlueBits = (BYTE)hints->blue_bits; + pfd.cAlphaBits = (BYTE)hints->alpha_bits; + pfd.cDepthBits = (BYTE)hints->depth_bits; + pfd.cStencilBits = (BYTE)hints->stencil_bits; + pfd.iLayerType = PFD_MAIN_PLANE; + return pfd; +} + +static int +puglWinError(PuglFakeWindow* fakeWin, const int status) +{ + if (fakeWin->hwnd) { + ReleaseDC(fakeWin->hwnd, fakeWin->hdc); + DestroyWindow(fakeWin->hwnd); + } + + return status; +} + +int +puglCreateWindow(PuglView* view, const char* title) +{ + typedef BOOL (*WglChoosePixelFormat)( + HDC, const int*, const FLOAT*, UINT, int*, UINT*); + + typedef HGLRC (*WglCreateContextAttribs)(HDC, HGLRC, const int*); + + typedef BOOL (*WglSwapInterval)(int); + + const char* className = view->windowClass ? view->windowClass : DEFAULT_CLASSNAME; + + title = title ? title : "Window"; + + // Register window class + WNDCLASSEX wc; + memset(&wc, 0, sizeof(wc)); + wc.cbSize = sizeof(wc); + wc.style = CS_OWNDC; + wc.lpfnWndProc = wndProc; + wc.hInstance = GetModuleHandle(NULL); + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); // TODO: user-specified icon + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + wc.lpszClassName = className; + if (!RegisterClassEx(&wc)) { + return 1; + } + + // Calculate window flags + unsigned winFlags = view->parent ? WS_CHILD : WS_POPUPWINDOW | WS_CAPTION; + if (view->hints.resizable) { + winFlags |= WS_SIZEBOX; + if (view->min_width || view->min_height) { + // Adjust the minimum window size to accomodate requested view size + RECT mr = { 0, 0, view->min_width, view->min_height }; + AdjustWindowRectEx(&mr, winFlags, FALSE, WS_EX_TOPMOST); + view->min_width = mr.right - mr.left; + view->min_height = mr.bottom - mr.top; + } + } + + // Adjust the window size to accomodate requested view size + RECT wr = { 0, 0, view->width, view->height }; + AdjustWindowRectEx(&wr, winFlags, FALSE, WS_EX_TOPMOST); + + // Create fake window for getting at GL context + PuglFakeWindow fakeWin = puglMakeFakeWindow( + CreateWindowEx(WS_EX_TOPMOST, + className, title, + (view->parent ? WS_CHILD : winFlags), + CW_USEDEFAULT, CW_USEDEFAULT, + wr.right-wr.left, wr.bottom-wr.top, + (HWND)view->parent, NULL, NULL, NULL)); + + if (!fakeWin.hwnd) { + return puglWinError(&fakeWin, 2); + } + + // Choose pixel format for fake window + const PIXELFORMATDESCRIPTOR fakePfd = puglGetPixelFormatDescriptor( + &view->hints); + const int fakeFormatId = ChoosePixelFormat(fakeWin.hdc, &fakePfd); + if (!fakeFormatId) { + return puglWinError(&fakeWin, 3); + } else if (!SetPixelFormat(fakeWin.hdc, fakeFormatId, &fakePfd)) { + return puglWinError(&fakeWin, 4); + } + + HGLRC fakeRc = wglCreateContext(fakeWin.hdc); + if (!fakeRc) { + return puglWinError(&fakeWin, 5); + } + + wglMakeCurrent(fakeWin.hdc, fakeRc); + + WglChoosePixelFormat wglChoosePixelFormat = (WglChoosePixelFormat)( + wglGetProcAddress("wglChoosePixelFormatARB")); + WglCreateContextAttribs wglCreateContextAttribs = (WglCreateContextAttribs)( + wglGetProcAddress("wglCreateContextAttribsARB")); + WglSwapInterval wglSwapInterval = (WglSwapInterval)( + wglGetProcAddress("wglSwapIntervalEXT")); + + PuglInternals* impl = view->impl; + + if (wglChoosePixelFormat && wglCreateContextAttribs) { + // Now create real window + impl->hwnd = CreateWindowEx( + WS_EX_TOPMOST, + className, title, + (view->parent ? WS_CHILD : winFlags), + CW_USEDEFAULT, CW_USEDEFAULT, wr.right-wr.left, wr.bottom-wr.top, + (HWND)view->parent, NULL, NULL, NULL); + + impl->hdc = GetDC(impl->hwnd); + + const int pixelAttrs[] = { + WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, + WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, + WGL_SUPPORT_OPENGL_ARB, GL_TRUE, + WGL_DOUBLE_BUFFER_ARB, view->hints.double_buffer, + WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, + WGL_SAMPLE_BUFFERS_ARB, view->hints.samples ? 1 : 0, + WGL_SAMPLES_ARB, view->hints.samples, + WGL_RED_BITS_ARB, view->hints.red_bits, + WGL_GREEN_BITS_ARB, view->hints.green_bits, + WGL_BLUE_BITS_ARB, view->hints.blue_bits, + WGL_ALPHA_BITS_ARB, view->hints.alpha_bits, + WGL_DEPTH_BITS_ARB, view->hints.depth_bits, + WGL_STENCIL_BITS_ARB, view->hints.stencil_bits, + 0, + }; + + // Choose pixel format based on hints + int pixelFormatId; + UINT numFormats; + if (!wglChoosePixelFormat(impl->hdc, pixelAttrs, NULL, 1u, &pixelFormatId, &numFormats)) { + return puglWinError(&fakeWin, 6); + } + + // Set desired pixel format + PIXELFORMATDESCRIPTOR pfd; + DescribePixelFormat(impl->hdc, pixelFormatId, sizeof(pfd), &pfd); + if (!SetPixelFormat(impl->hdc, pixelFormatId, &pfd)) { + return puglWinError(&fakeWin, 7); + } + + // Create final GL context + const int contextAttribs[] = { + WGL_CONTEXT_MAJOR_VERSION_ARB, view->hints.context_version_major, + WGL_CONTEXT_MINOR_VERSION_ARB, view->hints.context_version_minor, + WGL_CONTEXT_PROFILE_MASK_ARB, (view->hints.use_compat_profile + ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB + : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB), + 0 + }; + + if (!(impl->hglrc = wglCreateContextAttribs(impl->hdc, 0, contextAttribs))) { + return puglWinError(&fakeWin, 8); + } + + // Switch to new context + wglMakeCurrent(NULL, NULL); + wglDeleteContext(fakeRc); + if (!wglMakeCurrent(impl->hdc, impl->hglrc)) { + return puglWinError(&fakeWin, 9); + } + + ReleaseDC(fakeWin.hwnd, fakeWin.hdc); + DestroyWindow(fakeWin.hwnd); + } else { + // Modern extensions not available, just use the original "fake" window + impl->hwnd = fakeWin.hwnd; + impl->hdc = fakeWin.hdc; + impl->hglrc = fakeRc; + fakeWin.hwnd = 0; + fakeWin.hdc = 0; + } + + if (wglSwapInterval) { + wglSwapInterval(1); + } + + LARGE_INTEGER frequency; + QueryPerformanceFrequency(&frequency); + impl->timerFrequency = (double)frequency.QuadPart; + + SetWindowLongPtr(impl->hwnd, GWLP_USERDATA, (LONG_PTR)view); + + return 0; +} + +void +puglShowWindow(PuglView* view) +{ + PuglInternals* impl = view->impl; + + ShowWindow(impl->hwnd, SW_SHOWNORMAL); + view->visible = true; +} + +void +puglHideWindow(PuglView* view) +{ + PuglInternals* impl = view->impl; + + ShowWindow(impl->hwnd, SW_HIDE); + view->visible = false; +} + +void +puglDestroy(PuglView* view) +{ + if (view) { + wglMakeCurrent(NULL, NULL); + wglDeleteContext(view->impl->hglrc); + ReleaseDC(view->impl->hwnd, view->impl->hdc); + DestroyWindow(view->impl->hwnd); + UnregisterClass(view->windowClass ? view->windowClass : DEFAULT_CLASSNAME, NULL); + free(view->windowClass); + free(view->impl); + free(view); + } +} + +static PuglKey +keySymToSpecial(WPARAM sym) +{ + switch (sym) { + case VK_F1: return PUGL_KEY_F1; + case VK_F2: return PUGL_KEY_F2; + case VK_F3: return PUGL_KEY_F3; + case VK_F4: return PUGL_KEY_F4; + case VK_F5: return PUGL_KEY_F5; + case VK_F6: return PUGL_KEY_F6; + case VK_F7: return PUGL_KEY_F7; + case VK_F8: return PUGL_KEY_F8; + case VK_F9: return PUGL_KEY_F9; + case VK_F10: return PUGL_KEY_F10; + case VK_F11: return PUGL_KEY_F11; + case VK_F12: return PUGL_KEY_F12; + case VK_LEFT: return PUGL_KEY_LEFT; + case VK_UP: return PUGL_KEY_UP; + case VK_RIGHT: return PUGL_KEY_RIGHT; + case VK_DOWN: return PUGL_KEY_DOWN; + case VK_PRIOR: return PUGL_KEY_PAGE_UP; + case VK_NEXT: return PUGL_KEY_PAGE_DOWN; + case VK_HOME: return PUGL_KEY_HOME; + case VK_END: return PUGL_KEY_END; + case VK_INSERT: return PUGL_KEY_INSERT; + case VK_SHIFT: return PUGL_KEY_SHIFT; + case VK_CONTROL: return PUGL_KEY_CTRL; + case VK_MENU: return PUGL_KEY_ALT; + case VK_LWIN: return PUGL_KEY_SUPER; + case VK_RWIN: return PUGL_KEY_SUPER; + } + return (PuglKey)0; +} + +static uint32_t +getModifiers(void) +{ + uint32_t mods = 0; + mods |= (GetKeyState(VK_SHIFT) < 0) ? PUGL_MOD_SHIFT : 0; + mods |= (GetKeyState(VK_CONTROL) < 0) ? PUGL_MOD_CTRL : 0; + mods |= (GetKeyState(VK_MENU) < 0) ? PUGL_MOD_ALT : 0; + mods |= (GetKeyState(VK_LWIN) < 0) ? PUGL_MOD_SUPER : 0; + mods |= (GetKeyState(VK_RWIN) < 0) ? PUGL_MOD_SUPER : 0; + return mods; +} + +static void +initMouseEvent(PuglEvent* event, + PuglView* view, + int button, + bool press, + LPARAM lParam) +{ + POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + ClientToScreen(view->impl->hwnd, &pt); + + if (press) { + SetCapture(view->impl->hwnd); + } else { + ReleaseCapture(); + } + + event->button.time = (uint32_t)GetMessageTime(); + event->button.type = press ? PUGL_BUTTON_PRESS : PUGL_BUTTON_RELEASE; + event->button.x = GET_X_LPARAM(lParam); + event->button.y = GET_Y_LPARAM(lParam); + event->button.x_root = pt.x; + event->button.y_root = pt.y; + event->button.state = getModifiers(); + event->button.button = (uint32_t)button; +} + +static void +initScrollEvent(PuglEvent* event, PuglView* view, LPARAM lParam) +{ + POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + ScreenToClient(view->impl->hwnd, &pt); + + event->scroll.time = (uint32_t)GetMessageTime(); + event->scroll.type = PUGL_SCROLL; + event->scroll.x = pt.x; + event->scroll.y = pt.y; + event->scroll.x_root = GET_X_LPARAM(lParam); + event->scroll.y_root = GET_Y_LPARAM(lParam); + event->scroll.state = getModifiers(); + event->scroll.dx = 0; + event->scroll.dy = 0; +} + +static uint32_t +utf16_to_code_point(const wchar_t* input, const int input_size) +{ + uint32_t code_unit = *input; + // Equiv. range check between 0xD800 to 0xDBFF inclusive + if ((code_unit & 0xFC00) == 0xD800) { + if (input_size < 2) { + // "Error: is surrogate but input_size too small" + return 0xFFFD; // replacement character + } + + uint32_t code_unit_2 = *++input; + // Equiv. range check between 0xDC00 to 0xDFFF inclusive + if ((code_unit_2 & 0xFC00) == 0xDC00) { + return (code_unit << 10) + code_unit_2 - 0x35FDC00; + } + + // TODO: push_back(code_unit_2); + // "Error: Unpaired surrogates." + return 0xFFFD; // replacement character + } + return code_unit; +} + +static void +initKeyEvent(PuglEvent* event, PuglView* view, bool press, LPARAM lParam) +{ + POINT rpos = { 0, 0 }; + GetCursorPos(&rpos); + + POINT cpos = { rpos.x, rpos.y }; + ScreenToClient(view->impl->hwnd, &rpos); + + event->key.type = press ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE; + event->key.time = (uint32_t)GetMessageTime(); + event->key.state = getModifiers(); + event->key.x_root = rpos.x; + event->key.y_root = rpos.y; + event->key.x = cpos.x; + event->key.y = cpos.y; + event->key.keycode = (uint32_t)((lParam & 0xFF0000) >> 16); + event->key.character = 0; + event->key.special = (PuglKey)0; + event->key.filter = 0; +} + +static void +wcharBufToEvent(wchar_t* buf, int n, PuglEvent* event) +{ + if (n > 0) { + char* charp = (char*)event->key.utf8; + if (!WideCharToMultiByte(CP_UTF8, 0, buf, n, + charp, 8, NULL, NULL)) { + /* error: could not convert to utf-8, + GetLastError has details */ + memset(event->key.utf8, 0, 8); + // replacement character + event->key.utf8[0] = 0xEF; + event->key.utf8[1] = 0xBF; + event->key.utf8[2] = 0xBD; + } + + event->key.character = utf16_to_code_point(buf, n); + } else { + // replacement character + event->key.utf8[0] = 0xEF; + event->key.utf8[1] = 0xBF; + event->key.utf8[2] = 0xBD; + event->key.character = 0xFFFD; + } +} + +static void +translateMessageParamsToEvent(LPARAM lParam, WPARAM wParam, PuglEvent* event) +{ + (void)lParam; + + /* TODO: This is a kludge. Would be nice to use ToUnicode here, but this + breaks composed keys because it messes with the keyboard state. Not + sure how to correctly handle this on Windows. */ + + // This is how I really want to do this, but it breaks composed keys (é, + // è, ü, ö, and so on) because ToUnicode messes with the keyboard state. + + //wchar_t buf[5]; + //BYTE keyboard_state[256]; + //int wcharCount = 0; + //GetKeyboardState(keyboard_state); + //wcharCount = ToUnicode(wParam, MapVirtualKey(wParam, MAPVK_VK_TO_VSC), + // keyboard_state, buf, 4, 0); + //wcharBufToEvent(buf, wcharCount, event); + + // So, since Google refuses to give me a better solution, and if no one + // else has a better solution, I will make a hack... + wchar_t buf[5] = { 0, 0, 0, 0, 0 }; + WPARAM c = MapVirtualKey((unsigned)wParam, MAPVK_VK_TO_CHAR); + buf[0] = c & 0xffff; + // TODO: This does not take caps lock into account + // TODO: Dead keys should affect key releases as well + if (!(event->key.state && PUGL_MOD_SHIFT)) + buf[0] = towlower(buf[0]); + wcharBufToEvent(buf, 1, event); + event->key.filter = ((c >> 31) & 0x1); +} + +static void +translateCharEventToEvent(WPARAM wParam, PuglEvent* event) +{ + wchar_t buf[2]; + int wcharCount; + if (wParam & 0xFFFF0000) { + wcharCount = 2; + buf[0] = (wParam & 0xFFFF); + buf[1] = ((wParam >> 16) & 0xFFFF); + } else { + wcharCount = 1; + buf[0] = (wParam & 0xFFFF); + } + wcharBufToEvent(buf, wcharCount, event); +} + +static bool +ignoreKeyEvent(PuglView* view, LPARAM lParam) +{ + return view->ignoreKeyRepeat && (lParam & (1 << 30)); +} + +static LRESULT +handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) +{ + PuglEvent event; + void* dummy_ptr = NULL; + RECT rect; + MINMAXINFO* mmi; + POINT pt; + + memset(&event, 0, sizeof(event)); + + event.any.type = PUGL_NOTHING; + event.any.view = view; + if (InSendMessageEx(dummy_ptr)) { + event.any.flags |= PUGL_IS_SEND_EVENT; + } + + switch (message) { + case WM_CREATE: + case WM_SHOWWINDOW: + case WM_SIZE: + GetWindowRect(view->impl->hwnd, &rect); + event.configure.type = PUGL_CONFIGURE; + event.configure.x = rect.left; + event.configure.y = rect.top; + view->width = rect.right - rect.left; + view->height = rect.bottom - rect.top; + event.configure.width = view->width; + event.configure.height = view->height; + InvalidateRect(view->impl->hwnd, NULL, FALSE); + UpdateWindow(view->impl->hwnd); + break; + case WM_GETMINMAXINFO: + mmi = (MINMAXINFO*)lParam; + mmi->ptMinTrackSize.x = view->min_width; + mmi->ptMinTrackSize.y = view->min_height; + break; + case WM_PAINT: + GetUpdateRect(view->impl->hwnd, &rect, false); + event.expose.type = PUGL_EXPOSE; + event.expose.x = rect.left; + event.expose.y = rect.top; + event.expose.width = rect.right - rect.left; + event.expose.height = rect.bottom - rect.top; + event.expose.count = 0; + break; + case WM_MOUSEMOVE: + pt.x = GET_X_LPARAM(lParam); + pt.y = GET_Y_LPARAM(lParam); + ClientToScreen(view->impl->hwnd, &pt); + + event.motion.type = PUGL_MOTION_NOTIFY; + event.motion.time = (uint32_t)GetMessageTime(); + event.motion.x = GET_X_LPARAM(lParam); + event.motion.y = GET_Y_LPARAM(lParam); + event.motion.x_root = pt.x; + event.motion.y_root = pt.y; + event.motion.state = getModifiers(); + event.motion.is_hint = false; + break; + case WM_LBUTTONDOWN: + initMouseEvent(&event, view, 1, true, lParam); + break; + case WM_MBUTTONDOWN: + initMouseEvent(&event, view, 2, true, lParam); + break; + case WM_RBUTTONDOWN: + initMouseEvent(&event, view, 3, true, lParam); + break; + case WM_LBUTTONUP: + initMouseEvent(&event, view, 1, false, lParam); + break; + case WM_MBUTTONUP: + initMouseEvent(&event, view, 2, false, lParam); + break; + case WM_RBUTTONUP: + initMouseEvent(&event, view, 3, false, lParam); + break; + case WM_MOUSEWHEEL: + initScrollEvent(&event, view, lParam); + event.scroll.dy = GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA; + break; + case WM_MOUSEHWHEEL: + initScrollEvent(&event, view, lParam); + event.scroll.dx = GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA; + break; + case WM_KEYDOWN: + if (!ignoreKeyEvent(view, lParam)) { + initKeyEvent(&event, view, true, lParam); + if (!(event.key.special = keySymToSpecial(wParam))) { + event.key.type = PUGL_NOTHING; + } + } + break; + case WM_CHAR: + if (!ignoreKeyEvent(view, lParam)) { + initKeyEvent(&event, view, true, lParam); + translateCharEventToEvent(wParam, &event); + } + break; + case WM_DEADCHAR: + if (!ignoreKeyEvent(view, lParam)) { + initKeyEvent(&event, view, true, lParam); + translateCharEventToEvent(wParam, &event); + event.key.filter = 1; + } + break; + case WM_KEYUP: + initKeyEvent(&event, view, false, lParam); + if (!(event.key.special = keySymToSpecial(wParam))) { + translateMessageParamsToEvent(lParam, wParam, &event); + } + break; + case WM_QUIT: + case PUGL_LOCAL_CLOSE_MSG: + event.close.type = PUGL_CLOSE; + break; + default: + return DefWindowProc( + view->impl->hwnd, message, wParam, lParam); + } + + puglDispatchEvent(view, &event); + + return 0; +} + +void +puglGrabFocus(PuglView* view) +{ + (void)view; + // TODO +} + +PuglStatus +puglWaitForEvent(PuglView* view) +{ + (void)view; + WaitMessage(); + return PUGL_SUCCESS; +} + +PuglStatus +puglProcessEvents(PuglView* view) +{ + MSG msg; + while (PeekMessage(&msg, view->impl->hwnd, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + handleMessage(view, msg.message, msg.wParam, msg.lParam); + } + + if (view->redisplay) { + InvalidateRect(view->impl->hwnd, NULL, FALSE); + view->redisplay = false; + } + + return PUGL_SUCCESS; +} + +LRESULT CALLBACK +wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + + switch (message) { + case WM_CREATE: + PostMessage(hwnd, WM_SHOWWINDOW, TRUE, 0); + return 0; + case WM_CLOSE: + PostMessage(hwnd, PUGL_LOCAL_CLOSE_MSG, wParam, lParam); + return 0; + case WM_DESTROY: + return 0; + default: + if (view && hwnd == view->impl->hwnd) { + return handleMessage(view, message, wParam, lParam); + } else { + return DefWindowProc(hwnd, message, wParam, lParam); + } + } +} + +PuglGlFunc +puglGetProcAddress(const char* name) +{ + return (PuglGlFunc)wglGetProcAddress(name); +} + +double +puglGetTime(PuglView* view) +{ + LARGE_INTEGER count; + QueryPerformanceCounter(&count); + return (double)count.QuadPart / view->impl->timerFrequency; +} + +void +puglPostRedisplay(PuglView* view) +{ + view->redisplay = true; +} + +PuglNativeWindow +puglGetNativeWindow(PuglView* view) +{ + return (PuglNativeWindow)view->impl->hwnd; +} diff --git a/pugl/pugl_win.cpp b/pugl/pugl_win.cpp deleted file mode 100644 index 6e27a8d..0000000 --- a/pugl/pugl_win.cpp +++ /dev/null @@ -1,781 +0,0 @@ -/* - Copyright 2012-2019 David Robillard - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -/** - @file pugl_win.cpp Windows/WGL Pugl Implementation. -*/ - -#include "pugl/pugl_internal.h" - -#include -#include - -#include - -#include -#include -#include -#include - -#ifndef WM_MOUSEWHEEL -# define WM_MOUSEWHEEL 0x020A -#endif -#ifndef WM_MOUSEHWHEEL -# define WM_MOUSEHWHEEL 0x020E -#endif -#ifndef WHEEL_DELTA -# define WHEEL_DELTA 120 -#endif -#ifndef GWLP_USERDATA -# define GWLP_USERDATA (-21) -#endif - -#define PUGL_LOCAL_CLOSE_MSG (WM_USER + 50) - -#define WGL_DRAW_TO_WINDOW_ARB 0x2001 -#define WGL_ACCELERATION_ARB 0x2003 -#define WGL_SUPPORT_OPENGL_ARB 0x2010 -#define WGL_DOUBLE_BUFFER_ARB 0x2011 -#define WGL_PIXEL_TYPE_ARB 0x2013 -#define WGL_COLOR_BITS_ARB 0x2014 -#define WGL_RED_BITS_ARB 0x2015 -#define WGL_GREEN_BITS_ARB 0x2017 -#define WGL_BLUE_BITS_ARB 0x2019 -#define WGL_ALPHA_BITS_ARB 0x201b -#define WGL_DEPTH_BITS_ARB 0x2022 -#define WGL_STENCIL_BITS_ARB 0x2023 -#define WGL_FULL_ACCELERATION_ARB 0x2027 -#define WGL_TYPE_RGBA_ARB 0x202b -#define WGL_SAMPLE_BUFFERS_ARB 0x2041 -#define WGL_SAMPLES_ARB 0x2042 - -#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 -#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 -#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093 -#define WGL_CONTEXT_FLAGS_ARB 0x2094 -#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 - -#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 -#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 - -struct PuglInternalsImpl { - HWND hwnd; - HDC hdc; - HGLRC hglrc; - double timerFrequency; -}; - -// Scoped class to manage the fake window used during window creation -struct PuglFakeWindow { - PuglFakeWindow(HWND wnd) : hwnd{wnd}, hdc{wnd ? GetDC(wnd) : 0} {} - PuglFakeWindow(const PuglFakeWindow&) = delete; - - ~PuglFakeWindow() { - if (hwnd) { - ReleaseDC(hwnd, hdc); - DestroyWindow(hwnd); - } - } - - HWND hwnd; - HDC hdc; -}; - -static const TCHAR* DEFAULT_CLASSNAME = "Pugl"; - -LRESULT CALLBACK -wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); - -PuglInternals* -puglInitInternals(void) -{ - return (PuglInternals*)calloc(1, sizeof(PuglInternals)); -} - -void -puglEnterContext(PuglView* view) -{ - PAINTSTRUCT ps; - BeginPaint(view->impl->hwnd, &ps); - -#ifdef PUGL_HAVE_GL - if (view->ctx_type == PUGL_GL) { - wglMakeCurrent(view->impl->hdc, view->impl->hglrc); - } -#endif -} - -void -puglLeaveContext(PuglView* view, bool flush) -{ -#ifdef PUGL_HAVE_GL - if (view->ctx_type == PUGL_GL && flush) { - SwapBuffers(view->impl->hdc); - } -#endif - - PAINTSTRUCT ps; - EndPaint(view->impl->hwnd, &ps); -} - -static PIXELFORMATDESCRIPTOR -puglGetPixelFormatDescriptor(const PuglHints* hints) -{ - const int rgbBits = hints->red_bits + hints->green_bits + hints->blue_bits; - - PIXELFORMATDESCRIPTOR pfd; - ZeroMemory(&pfd, sizeof(pfd)); - 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 = (BYTE)rgbBits; - pfd.cRedBits = (BYTE)hints->red_bits; - pfd.cGreenBits = (BYTE)hints->green_bits; - pfd.cBlueBits = (BYTE)hints->blue_bits; - pfd.cAlphaBits = (BYTE)hints->alpha_bits; - pfd.cDepthBits = (BYTE)hints->depth_bits; - pfd.cStencilBits = (BYTE)hints->stencil_bits; - pfd.iLayerType = PFD_MAIN_PLANE; - return pfd; -} - -template -Proc puglGetProc(const char* name) -{ - return reinterpret_cast(wglGetProcAddress(name)); -} - -int -puglCreateWindow(PuglView* view, const char* title) -{ - typedef BOOL (*WglChoosePixelFormat)( - HDC, const int*, const FLOAT*, UINT, int*, UINT*); - - typedef HGLRC (*WglCreateContextAttribs)(HDC, HGLRC, const int*); - - typedef BOOL (*WglSwapInterval)(int); - - const char* className = view->windowClass ? view->windowClass : DEFAULT_CLASSNAME; - - title = title ? title : "Window"; - - // Register window class - WNDCLASSEX wc; - memset(&wc, 0, sizeof(wc)); - wc.cbSize = sizeof(wc); - wc.style = CS_OWNDC; - wc.lpfnWndProc = wndProc; - wc.hInstance = GetModuleHandle(NULL); - wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); // TODO: user-specified icon - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); - wc.lpszClassName = className; - if (!RegisterClassEx(&wc)) { - return 1; - } - - // Calculate window flags - unsigned winFlags = view->parent ? WS_CHILD : WS_POPUPWINDOW | WS_CAPTION; - if (view->hints.resizable) { - winFlags |= WS_SIZEBOX; - if (view->min_width || view->min_height) { - // Adjust the minimum window size to accomodate requested view size - RECT mr = { 0, 0, view->min_width, view->min_height }; - AdjustWindowRectEx(&mr, winFlags, FALSE, WS_EX_TOPMOST); - view->min_width = mr.right - mr.left; - view->min_height = mr.bottom - mr.top; - } - } - - // Adjust the window size to accomodate requested view size - RECT wr = { 0, 0, view->width, view->height }; - AdjustWindowRectEx(&wr, winFlags, FALSE, WS_EX_TOPMOST); - - // Create fake window for getting at GL context - PuglFakeWindow fakeWin( - CreateWindowEx(WS_EX_TOPMOST, - className, title, - (view->parent ? WS_CHILD : winFlags), - CW_USEDEFAULT, CW_USEDEFAULT, - wr.right-wr.left, wr.bottom-wr.top, - (HWND)view->parent, NULL, NULL, NULL)); - - if (!fakeWin.hwnd) { - return 2; - } - - // Choose pixel format for fake window - const auto fakePfd = puglGetPixelFormatDescriptor(&view->hints); - const int fakeFormatId = ChoosePixelFormat(fakeWin.hdc, &fakePfd); - if (!fakeFormatId) { - return 3; - } else if (!SetPixelFormat(fakeWin.hdc, fakeFormatId, &fakePfd)) { - return 4; - } - - HGLRC fakeRc = wglCreateContext(fakeWin.hdc); - if (!fakeRc) { - return 5; - } - - wglMakeCurrent(fakeWin.hdc, fakeRc); - - auto wglChoosePixelFormat = puglGetProc( - "wglChoosePixelFormatARB"); - auto wglCreateContextAttribs = puglGetProc( - "wglCreateContextAttribsARB"); - auto wglSwapInterval = puglGetProc( - "wglSwapIntervalEXT"); - - PuglInternals* impl = view->impl; - - if (wglChoosePixelFormat && wglCreateContextAttribs) { - // Now create real window - impl->hwnd = CreateWindowEx( - WS_EX_TOPMOST, - className, title, - (view->parent ? WS_CHILD : winFlags), - CW_USEDEFAULT, CW_USEDEFAULT, wr.right-wr.left, wr.bottom-wr.top, - (HWND)view->parent, NULL, NULL, NULL); - - impl->hdc = GetDC(impl->hwnd); - - const int pixelAttrs[] = { - WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, - WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, - WGL_SUPPORT_OPENGL_ARB, GL_TRUE, - WGL_DOUBLE_BUFFER_ARB, view->hints.double_buffer, - WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, - WGL_SAMPLE_BUFFERS_ARB, view->hints.samples ? 1 : 0, - WGL_SAMPLES_ARB, view->hints.samples, - WGL_RED_BITS_ARB, view->hints.red_bits, - WGL_GREEN_BITS_ARB, view->hints.green_bits, - WGL_BLUE_BITS_ARB, view->hints.blue_bits, - WGL_ALPHA_BITS_ARB, view->hints.alpha_bits, - WGL_DEPTH_BITS_ARB, view->hints.depth_bits, - WGL_STENCIL_BITS_ARB, view->hints.stencil_bits, - 0, - }; - - // Choose pixel format based on hints - int pixelFormatId; - UINT numFormats; - if (!wglChoosePixelFormat(impl->hdc, pixelAttrs, NULL, 1u, &pixelFormatId, &numFormats)) { - return 6; - } - - // Set desired pixel format - PIXELFORMATDESCRIPTOR pfd; - DescribePixelFormat(impl->hdc, pixelFormatId, sizeof(pfd), &pfd); - if (!SetPixelFormat(impl->hdc, pixelFormatId, &pfd)) { - return 7; - } - - // Create final GL context - const int contextAttribs[] = { - WGL_CONTEXT_MAJOR_VERSION_ARB, view->hints.context_version_major, - WGL_CONTEXT_MINOR_VERSION_ARB, view->hints.context_version_minor, - WGL_CONTEXT_PROFILE_MASK_ARB, (view->hints.use_compat_profile - ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB - : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB), - 0 - }; - - if (!(impl->hglrc = wglCreateContextAttribs(impl->hdc, 0, contextAttribs))) { - return 8; - } - - // Switch to new context - wglMakeCurrent(NULL, NULL); - wglDeleteContext(fakeRc); - if (!wglMakeCurrent(impl->hdc, impl->hglrc)) { - return 9; - } - } else { - // Modern extensions not available, just use the original "fake" window - impl->hwnd = fakeWin.hwnd; - impl->hdc = fakeWin.hdc; - impl->hglrc = fakeRc; - fakeWin.hwnd = 0; - fakeWin.hdc = 0; - } - - if (wglSwapInterval) { - wglSwapInterval(1); - } - - LARGE_INTEGER frequency; - QueryPerformanceFrequency(&frequency); - impl->timerFrequency = static_cast(frequency.QuadPart); - - SetWindowLongPtr(impl->hwnd, GWLP_USERDATA, (LONG_PTR)view); - - return 0; -} - -void -puglShowWindow(PuglView* view) -{ - PuglInternals* impl = view->impl; - - ShowWindow(impl->hwnd, SW_SHOWNORMAL); - view->visible = true; -} - -void -puglHideWindow(PuglView* view) -{ - PuglInternals* impl = view->impl; - - ShowWindow(impl->hwnd, SW_HIDE); - view->visible = false; -} - -void -puglDestroy(PuglView* view) -{ - if (view) { - wglMakeCurrent(NULL, NULL); - wglDeleteContext(view->impl->hglrc); - ReleaseDC(view->impl->hwnd, view->impl->hdc); - DestroyWindow(view->impl->hwnd); - UnregisterClass(view->windowClass ? view->windowClass : DEFAULT_CLASSNAME, NULL); - free(view->windowClass); - free(view->impl); - free(view); - } -} - -static PuglKey -keySymToSpecial(WPARAM sym) -{ - switch (sym) { - case VK_F1: return PUGL_KEY_F1; - case VK_F2: return PUGL_KEY_F2; - case VK_F3: return PUGL_KEY_F3; - case VK_F4: return PUGL_KEY_F4; - case VK_F5: return PUGL_KEY_F5; - case VK_F6: return PUGL_KEY_F6; - case VK_F7: return PUGL_KEY_F7; - case VK_F8: return PUGL_KEY_F8; - case VK_F9: return PUGL_KEY_F9; - case VK_F10: return PUGL_KEY_F10; - case VK_F11: return PUGL_KEY_F11; - case VK_F12: return PUGL_KEY_F12; - case VK_LEFT: return PUGL_KEY_LEFT; - case VK_UP: return PUGL_KEY_UP; - case VK_RIGHT: return PUGL_KEY_RIGHT; - case VK_DOWN: return PUGL_KEY_DOWN; - case VK_PRIOR: return PUGL_KEY_PAGE_UP; - case VK_NEXT: return PUGL_KEY_PAGE_DOWN; - case VK_HOME: return PUGL_KEY_HOME; - case VK_END: return PUGL_KEY_END; - case VK_INSERT: return PUGL_KEY_INSERT; - case VK_SHIFT: return PUGL_KEY_SHIFT; - case VK_CONTROL: return PUGL_KEY_CTRL; - case VK_MENU: return PUGL_KEY_ALT; - case VK_LWIN: return PUGL_KEY_SUPER; - case VK_RWIN: return PUGL_KEY_SUPER; - } - return (PuglKey)0; -} - -static uint32_t -getModifiers() -{ - uint32_t mods = 0; - mods |= (GetKeyState(VK_SHIFT) < 0) ? PUGL_MOD_SHIFT : 0; - mods |= (GetKeyState(VK_CONTROL) < 0) ? PUGL_MOD_CTRL : 0; - mods |= (GetKeyState(VK_MENU) < 0) ? PUGL_MOD_ALT : 0; - mods |= (GetKeyState(VK_LWIN) < 0) ? PUGL_MOD_SUPER : 0; - mods |= (GetKeyState(VK_RWIN) < 0) ? PUGL_MOD_SUPER : 0; - return mods; -} - -static void -initMouseEvent(PuglEvent* event, - PuglView* view, - int button, - bool press, - LPARAM lParam) -{ - POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; - ClientToScreen(view->impl->hwnd, &pt); - - if (press) { - SetCapture(view->impl->hwnd); - } else { - ReleaseCapture(); - } - - event->button.time = static_cast(GetMessageTime()); - event->button.type = press ? PUGL_BUTTON_PRESS : PUGL_BUTTON_RELEASE; - event->button.x = GET_X_LPARAM(lParam); - event->button.y = GET_Y_LPARAM(lParam); - event->button.x_root = pt.x; - event->button.y_root = pt.y; - event->button.state = getModifiers(); - event->button.button = static_cast(button); -} - -static void -initScrollEvent(PuglEvent* event, PuglView* view, LPARAM lParam, WPARAM) -{ - POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; - ScreenToClient(view->impl->hwnd, &pt); - - event->scroll.time = static_cast(GetMessageTime()); - event->scroll.type = PUGL_SCROLL; - event->scroll.x = pt.x; - event->scroll.y = pt.y; - event->scroll.x_root = GET_X_LPARAM(lParam); - event->scroll.y_root = GET_Y_LPARAM(lParam); - event->scroll.state = getModifiers(); - event->scroll.dx = 0; - event->scroll.dy = 0; -} - -static uint32_t -utf16_to_code_point(const wchar_t* input, const int input_size) -{ - uint32_t code_unit = *input; - // Equiv. range check between 0xD800 to 0xDBFF inclusive - if ((code_unit & 0xFC00) == 0xD800) { - if (input_size < 2) { - // "Error: is surrogate but input_size too small" - return 0xFFFD; // replacement character - } - - uint32_t code_unit_2 = *++input; - // Equiv. range check between 0xDC00 to 0xDFFF inclusive - if ((code_unit_2 & 0xFC00) == 0xDC00) { - return (code_unit << 10) + code_unit_2 - 0x35FDC00; - } - - // TODO: push_back(code_unit_2); - // "Error: Unpaired surrogates." - return 0xFFFD; // replacement character - } - return code_unit; -} - -static void -initKeyEvent(PuglEvent* event, PuglView* view, bool press, LPARAM lParam) -{ - POINT rpos = { 0, 0 }; - GetCursorPos(&rpos); - - POINT cpos = { rpos.x, rpos.y }; - ScreenToClient(view->impl->hwnd, &rpos); - - event->key.type = press ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE; - event->key.time = static_cast(GetMessageTime()); - event->key.state = getModifiers(); - event->key.x_root = rpos.x; - event->key.y_root = rpos.y; - event->key.x = cpos.x; - event->key.y = cpos.y; - event->key.keycode = static_cast((lParam & 0xFF0000) >> 16); - event->key.character = 0; - event->key.special = static_cast(0); - event->key.filter = 0; -} - -static void -wcharBufToEvent(wchar_t* buf, int n, PuglEvent* event) -{ - if (n > 0) { - char* charp = reinterpret_cast(event->key.utf8); - if (!WideCharToMultiByte(CP_UTF8, 0, buf, n, - charp, 8, NULL, NULL)) { - /* error: could not convert to utf-8, - GetLastError has details */ - memset(event->key.utf8, 0, 8); - // replacement character - event->key.utf8[0] = 0xEF; - event->key.utf8[1] = 0xBF; - event->key.utf8[2] = 0xBD; - } - - event->key.character = utf16_to_code_point(buf, n); - } else { - // replacement character - event->key.utf8[0] = 0xEF; - event->key.utf8[1] = 0xBF; - event->key.utf8[2] = 0xBD; - event->key.character = 0xFFFD; - } -} - -static void -translateMessageParamsToEvent(LPARAM, WPARAM wParam, PuglEvent* event) -{ - /* TODO: This is a kludge. Would be nice to use ToUnicode here, but this - breaks composed keys because it messes with the keyboard state. Not - sure how to correctly handle this on Windows. */ - - // This is how I really want to do this, but it breaks composed keys (é, - // è, ü, ö, and so on) because ToUnicode messes with the keyboard state. - - //wchar_t buf[5]; - //BYTE keyboard_state[256]; - //int wcharCount = 0; - //GetKeyboardState(keyboard_state); - //wcharCount = ToUnicode(wParam, MapVirtualKey(wParam, MAPVK_VK_TO_VSC), - // keyboard_state, buf, 4, 0); - //wcharBufToEvent(buf, wcharCount, event); - - // So, since Google refuses to give me a better solution, and if no one - // else has a better solution, I will make a hack... - wchar_t buf[5] = { 0, 0, 0, 0, 0 }; - WPARAM c = MapVirtualKey(static_cast(wParam), MAPVK_VK_TO_CHAR); - buf[0] = c & 0xffff; - // TODO: This does not take caps lock into account - // TODO: Dead keys should affect key releases as well - if (!(event->key.state && PUGL_MOD_SHIFT)) - buf[0] = towlower(buf[0]); - wcharBufToEvent(buf, 1, event); - event->key.filter = ((c >> 31) & 0x1); -} - -static void -translateCharEventToEvent(WPARAM wParam, PuglEvent* event) -{ - wchar_t buf[2]; - int wcharCount; - if (wParam & 0xFFFF0000) { - wcharCount = 2; - buf[0] = (wParam & 0xFFFF); - buf[1] = ((wParam >> 16) & 0xFFFF); - } else { - wcharCount = 1; - buf[0] = (wParam & 0xFFFF); - } - wcharBufToEvent(buf, wcharCount, event); -} - -static bool -ignoreKeyEvent(PuglView* view, LPARAM lParam) -{ - return view->ignoreKeyRepeat && (lParam & (1 << 30)); -} - -static LRESULT -handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) -{ - PuglEvent event; - void* dummy_ptr = NULL; - RECT rect; - MINMAXINFO* mmi; - POINT pt; - - memset(&event, 0, sizeof(event)); - - event.any.type = PUGL_NOTHING; - event.any.view = view; - if (InSendMessageEx(dummy_ptr)) { - event.any.flags |= PUGL_IS_SEND_EVENT; - } - - switch (message) { - case WM_CREATE: - case WM_SHOWWINDOW: - case WM_SIZE: - GetWindowRect(view->impl->hwnd, &rect); - event.configure.type = PUGL_CONFIGURE; - event.configure.x = rect.left; - event.configure.y = rect.top; - view->width = rect.right - rect.left; - view->height = rect.bottom - rect.top; - event.configure.width = view->width; - event.configure.height = view->height; - InvalidateRect(view->impl->hwnd, NULL, FALSE); - UpdateWindow(view->impl->hwnd); - break; - case WM_GETMINMAXINFO: - mmi = (MINMAXINFO*)lParam; - mmi->ptMinTrackSize.x = view->min_width; - mmi->ptMinTrackSize.y = view->min_height; - break; - case WM_PAINT: - GetUpdateRect(view->impl->hwnd, &rect, false); - event.expose.type = PUGL_EXPOSE; - event.expose.x = rect.left; - event.expose.y = rect.top; - event.expose.width = rect.right - rect.left; - event.expose.height = rect.bottom - rect.top; - event.expose.count = 0; - break; - case WM_MOUSEMOVE: - pt.x = GET_X_LPARAM(lParam); - pt.y = GET_Y_LPARAM(lParam); - ClientToScreen(view->impl->hwnd, &pt); - - event.motion.type = PUGL_MOTION_NOTIFY; - event.motion.time = static_cast(GetMessageTime()); - event.motion.x = GET_X_LPARAM(lParam); - event.motion.y = GET_Y_LPARAM(lParam); - event.motion.x_root = pt.x; - event.motion.y_root = pt.y; - event.motion.state = getModifiers(); - event.motion.is_hint = false; - break; - case WM_LBUTTONDOWN: - initMouseEvent(&event, view, 1, true, lParam); - break; - case WM_MBUTTONDOWN: - initMouseEvent(&event, view, 2, true, lParam); - break; - case WM_RBUTTONDOWN: - initMouseEvent(&event, view, 3, true, lParam); - break; - case WM_LBUTTONUP: - initMouseEvent(&event, view, 1, false, lParam); - break; - case WM_MBUTTONUP: - initMouseEvent(&event, view, 2, false, lParam); - break; - case WM_RBUTTONUP: - initMouseEvent(&event, view, 3, false, lParam); - break; - case WM_MOUSEWHEEL: - initScrollEvent(&event, view, lParam, wParam); - event.scroll.dy = GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA; - break; - case WM_MOUSEHWHEEL: - initScrollEvent(&event, view, lParam, wParam); - event.scroll.dx = GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA; - break; - case WM_KEYDOWN: - if (!ignoreKeyEvent(view, lParam)) { - initKeyEvent(&event, view, true, lParam); - if (!(event.key.special = keySymToSpecial(wParam))) { - event.key.type = PUGL_NOTHING; - } - } - break; - case WM_CHAR: - if (!ignoreKeyEvent(view, lParam)) { - initKeyEvent(&event, view, true, lParam); - translateCharEventToEvent(wParam, &event); - } - break; - case WM_DEADCHAR: - if (!ignoreKeyEvent(view, lParam)) { - initKeyEvent(&event, view, true, lParam); - translateCharEventToEvent(wParam, &event); - event.key.filter = 1; - } - break; - case WM_KEYUP: - initKeyEvent(&event, view, false, lParam); - if (!(event.key.special = keySymToSpecial(wParam))) { - translateMessageParamsToEvent(lParam, wParam, &event); - } - break; - case WM_QUIT: - case PUGL_LOCAL_CLOSE_MSG: - event.close.type = PUGL_CLOSE; - break; - default: - return DefWindowProc( - view->impl->hwnd, message, wParam, lParam); - } - - puglDispatchEvent(view, &event); - - return 0; -} - -void -puglGrabFocus(PuglView*) -{ - // TODO -} - -PuglStatus -puglWaitForEvent(PuglView*) -{ - WaitMessage(); - return PUGL_SUCCESS; -} - -PuglStatus -puglProcessEvents(PuglView* view) -{ - MSG msg; - while (PeekMessage(&msg, view->impl->hwnd, 0, 0, PM_REMOVE)) { - TranslateMessage(&msg); - handleMessage(view, msg.message, msg.wParam, msg.lParam); - } - - if (view->redisplay) { - InvalidateRect(view->impl->hwnd, NULL, FALSE); - view->redisplay = false; - } - - return PUGL_SUCCESS; -} - -LRESULT CALLBACK -wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWLP_USERDATA); - - switch (message) { - case WM_CREATE: - PostMessage(hwnd, WM_SHOWWINDOW, TRUE, 0); - return 0; - case WM_CLOSE: - PostMessage(hwnd, PUGL_LOCAL_CLOSE_MSG, wParam, lParam); - return 0; - case WM_DESTROY: - return 0; - default: - if (view && hwnd == view->impl->hwnd) { - return handleMessage(view, message, wParam, lParam); - } else { - return DefWindowProc(hwnd, message, wParam, lParam); - } - } -} - -PuglGlFunc -puglGetProcAddress(const char* name) -{ - return (PuglGlFunc)wglGetProcAddress(name); -} - -double -puglGetTime(PuglView* view) -{ - LARGE_INTEGER count; - QueryPerformanceCounter(&count); - return double(count.QuadPart) / view->impl->timerFrequency; -} - -void -puglPostRedisplay(PuglView* view) -{ - view->redisplay = true; -} - -PuglNativeWindow -puglGetNativeWindow(PuglView* view) -{ - return (PuglNativeWindow)view->impl->hwnd; -} diff --git a/wscript b/wscript index 31e59ac..09a8296 100644 --- a/wscript +++ b/wscript @@ -21,7 +21,6 @@ out = 'build' # Build directory def options(ctx): ctx.load('compiler_c') - ctx.load('compiler_cxx') opts = ctx.configuration_options() opts.add_option('--target', default=None, dest='target', @@ -41,9 +40,8 @@ def configure(conf): conf.load('autowaf', cache=True) if conf.env.TARGET_PLATFORM == 'win32': - conf.load('compiler_cxx', cache=True) if conf.env.MSVC_COMPILER: - conf.env.append_unique('CXXFLAGS', ['/wd4191']) + conf.env.append_unique('CFLAGS', ['/wd4191']) elif conf.env.TARGET_PLATFORM == 'darwin': conf.env.append_unique('CFLAGS', ['-Wno-deprecated-declarations']) @@ -95,15 +93,12 @@ def build(bld): framework = [] libs = [] if bld.env.TARGET_PLATFORM == 'win32': - lang = 'cxx' - lib_source = ['pugl/pugl_win.cpp'] + lib_source = ['pugl/pugl_win.c'] libs = ['opengl32', 'gdi32', 'user32'] elif bld.env.TARGET_PLATFORM == 'darwin': - lang = 'c' # Objective C, actually lib_source = ['pugl/pugl_osx.m'] framework = ['Cocoa', 'OpenGL'] else: - lang = 'c' lib_source = ['pugl/pugl_x11.c'] libs = ['X11'] if bld.is_defined('HAVE_GL'): @@ -134,14 +129,14 @@ def build(bld): # Shared Library if bld.env['BUILD_SHARED']: - bld(features = '%s %sshlib' % (lang, lang), + bld(features = 'c cshlib', name = 'libpugl', cflags = libflags + ['-DPUGL_SHARED', '-DPUGL_INTERNAL'], **lib_common) # Static library if bld.env['BUILD_STATIC']: - bld(features = '%s %sstlib' % (lang, lang), + bld(features = 'c cstlib', name = 'libpugl_static', cflags = ['-DPUGL_INTERNAL'], **lib_common) -- cgit v1.2.1