diff options
-rw-r--r-- | AUTHORS | 2 | ||||
-rw-r--r-- | pugl/pugl_win.cpp | 343 |
2 files changed, 259 insertions, 86 deletions
@@ -7,5 +7,5 @@ Original GLX inspiration, portability fixes: Various fixes and improvements: Robin Gareus <robin@gareus.org> -UTF-8 work: +UTF-8 and Windows event work: Erik Åldstedt Sund <erikalds@gmail.com> diff --git a/pugl/pugl_win.cpp b/pugl/pugl_win.cpp index e3ceef7..4ee47c5 100644 --- a/pugl/pugl_win.cpp +++ b/pugl/pugl_win.cpp @@ -24,6 +24,8 @@ #include <stdio.h> #include <stdlib.h> +#include <string.h> +#include <wctype.h> #include "pugl/pugl_internal.h" @@ -83,6 +85,9 @@ puglInitInternals() 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); @@ -99,6 +104,9 @@ puglLeaveContext(PuglView* view, bool flush) SwapBuffers(view->impl->hdc); } #endif + + PAINTSTRUCT ps; + EndPaint(view->impl->hwnd, &ps); } int @@ -229,32 +237,6 @@ puglDestroy(PuglView* view) free(view); } -static void -puglReshape(PuglView* view, int width, int height) -{ - puglEnterContext(view); - - if (view->reshapeFunc) { - view->reshapeFunc(view, width, height); - } - - view->width = width; - view->height = height; -} - -static void -puglDisplay(PuglView* view) -{ - puglEnterContext(view); - - if (view->displayFunc) { - view->displayFunc(view); - } - - puglLeaveContext(view, true); - view->redisplay = false; -} - static PuglKey keySymToSpecial(int sym) { @@ -290,117 +272,305 @@ keySymToSpecial(int sym) } static void -processMouseEvent(PuglView* view, int button, bool press, LPARAM lParam) +setModifiers(PuglView* view) { - view->event_timestamp_ms = GetMessageTime(); + view->mods = 0; + view->mods |= (GetKeyState(VK_SHIFT) < 0) ? PUGL_MOD_SHIFT : 0; + view->mods |= (GetKeyState(VK_CONTROL) < 0) ? PUGL_MOD_CTRL : 0; + view->mods |= (GetKeyState(VK_MENU) < 0) ? PUGL_MOD_ALT : 0; + view->mods |= (GetKeyState(VK_LWIN) < 0) ? PUGL_MOD_SUPER : 0; + view->mods |= (GetKeyState(VK_RWIN) < 0) ? PUGL_MOD_SUPER : 0; +} + +static unsigned int +getModifiers() +{ + unsigned int 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(); } - if (view->mouseFunc) { - view->mouseFunc(view, button, press, - GET_X_LPARAM(lParam), - GET_Y_LPARAM(lParam)); + event->button.time = 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 = button; +} + +static void +initScrollEvent(PuglEvent* event, PuglView* view, LPARAM lParam, WPARAM wParam) +{ + POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + ScreenToClient(view->impl->hwnd, &pt); + + event->scroll.time = 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 unsigned int +utf16_to_code_point(const wchar_t* input, size_t input_size) +{ + unsigned int 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 + } + + unsigned int 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(&pos); + + POINT cpos = { pt.x, pt.y }; + ScreenToClient(view->impl->hwnd, &rpos); + + event->key.type = press ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE; + event->key.time = 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 = (lParam & 0xFF0000) >> 16; + event->key.character = 0; + event->key.special = static_cast<PuglKey>(0); + event->key.filter = 0; +} + +static void +wcharBufToEvent(wchar_t* buf, int n, PuglEvent* event) +{ + if (n > 0) { + char* charp = reinterpret_cast<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 -setModifiers(PuglView* view) +translateMessageParamsToEvent(LPARAM lParam, WPARAM wParam, PuglEvent* event) { - view->mods = 0; - view->mods |= (GetKeyState(VK_SHIFT) < 0) ? PUGL_MOD_SHIFT : 0; - view->mods |= (GetKeyState(VK_CONTROL) < 0) ? PUGL_MOD_CTRL : 0; - view->mods |= (GetKeyState(VK_MENU) < 0) ? PUGL_MOD_ALT : 0; - view->mods |= (GetKeyState(VK_LWIN) < 0) ? PUGL_MOD_SUPER : 0; - view->mods |= (GetKeyState(VK_RWIN) < 0) ? PUGL_MOD_SUPER : 0; + /* 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 }; + UINT c = MapVirtualKey(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 bol +ignoreKeyEvent(PuglView* view, LPARAM lParam) +{ + return view->ignoreKeyRepeat && (lParam & (1 << 30)); } static LRESULT handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) { - PAINTSTRUCT ps; - PuglKey key; + PuglEvent event; + void* dummy_ptr = NULL; RECT rect; MINMAXINFO* mmi; + POINT pt; + bool dispatchThisEvent = true; + + memset(&event, 0, sizeof(event)); + + event.any.type = PUGL_NOTHING; + event.any.view = view; + event.any.send_event = InSendMessageEx(dummy_ptr); setModifiers(view); switch (message) { case WM_CREATE: case WM_SHOWWINDOW: case WM_SIZE: - GetClientRect(view->impl->hwnd, &rect); - puglReshape(view, rect.right, rect.bottom); - view->width = rect.right; - view->height = rect.bottom; + 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; break; case WM_GETMINMAXINFO: - mmi = (MINMAXINFO*)lParam; + mmi = (MINMAXINFO*)lParam; mmi->ptMinTrackSize.x = view->min_width; mmi->ptMinTrackSize.y = view->min_height; break; case WM_PAINT: - BeginPaint(view->impl->hwnd, &ps); - puglDisplay(view); - EndPaint(view->impl->hwnd, &ps); + 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: - if (view->motionFunc) { - view->event_timestamp_ms = GetMessageTime(); - view->motionFunc(view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); - } + 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 = 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: - processMouseEvent(view, 1, true, lParam); + processMouseEvent(&event, view, 1, true, lParam); break; case WM_MBUTTONDOWN: - processMouseEvent(view, 2, true, lParam); + processMouseEvent(&event, view, 2, true, lParam); break; case WM_RBUTTONDOWN: - processMouseEvent(view, 3, true, lParam); + processMouseEvent(&event, view, 3, true, lParam); break; case WM_LBUTTONUP: - processMouseEvent(view, 1, false, lParam); + processMouseEvent(&event, view, 1, false, lParam); break; case WM_MBUTTONUP: - processMouseEvent(view, 2, false, lParam); + processMouseEvent(&event, view, 2, false, lParam); break; case WM_RBUTTONUP: - processMouseEvent(view, 3, false, lParam); + processMouseEvent(&event, view, 3, false, lParam); break; case WM_MOUSEWHEEL: - if (view->scrollFunc) { - POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; - ScreenToClient(view->impl->hwnd, &pt); - view->event_timestamp_ms = GetMessageTime(); - view->scrollFunc( - view, pt.x, pt.y, - 0.0f, GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA); - } + initScrollEvent(&event, view, lParam, wParam); + event.scroll.dy = GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA; break; case WM_MOUSEHWHEEL: - if (view->scrollFunc) { - POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; - ScreenToClient(view->impl->hwnd, &pt); - view->event_timestamp_ms = GetMessageTime(); - view->scrollFunc( - view, pt.x, pt.y, - GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA, 0.0f); - } + initScrollEvent(&event, view, lParam, wParam); + event.scroll.dx = GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA; break; case WM_KEYDOWN: - if (view->ignoreKeyRepeat && (lParam & (1 << 30))) { - break; - } // else nobreak - case WM_KEYUP: - view->event_timestamp_ms = GetMessageTime(); - if ((key = keySymToSpecial(wParam))) { - if (view->specialFunc) { - view->specialFunc(view, message == WM_KEYDOWN, key); + if (!ignoreKeyEvent(view, lParam)) { + initKeyEvent(event, view, true, lParam); + if (!(event->key.special = keySymToSpecial(wParam))) { + event->key.type = PUGL_NOTHING; } - } else if (view->keyboardFunc) { - view->keyboardFunc(view, message == WM_KEYDOWN, wParam); + } + 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: @@ -415,6 +585,8 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) view->impl->hwnd, message, wParam, lParam); } + puglDispatchEvent(view, &event); + return 0; } @@ -436,6 +608,7 @@ 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); } |