summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2015-09-13 16:42:39 -0400
committerDavid Robillard <d@drobilla.net>2015-09-13 16:42:39 -0400
commite5ec8fbf3f94c5a5143f43287a3cc8888f2b99ee (patch)
treef6106f3c8b9e8214b02c05525b8000d42b675d87
parent77d1640d8a704a124b4ad0f953687f6bc65893b8 (diff)
Preliminary Windows event work.
-rw-r--r--AUTHORS2
-rw-r--r--pugl/pugl_win.cpp343
2 files changed, 259 insertions, 86 deletions
diff --git a/AUTHORS b/AUTHORS
index 362287b..5625baa 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -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);
}