diff options
| -rw-r--r-- | AUTHORS | 2 | ||||
| -rw-r--r-- | pugl/pugl_win.cpp | 343 | ||||
| -rw-r--r-- | pugl/pugl_x11.c | 28 | 
3 files changed, 272 insertions, 101 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 3686b37..0a68569 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);  	} diff --git a/pugl/pugl_x11.c b/pugl/pugl_x11.c index 35cca8c..8167ece 100644 --- a/pugl/pugl_x11.c +++ b/pugl/pugl_x11.c @@ -505,10 +505,8 @@ PuglStatus  puglProcessEvents(PuglView* view)  {  	XEvent xevent; -	bool   resized = false;  	while (XPending(view->impl->display) > 0) {  		XNextEvent(view->impl->display, &xevent); -		bool ignore = false;  		if (xevent.type == ClientMessage) {  			// Handle close message  			char* type = XGetAtomName(view->impl->display, @@ -529,32 +527,32 @@ puglProcessEvents(PuglView* view)  				    next.xkey.time == xevent.xkey.time &&  				    next.xkey.keycode == xevent.xkey.keycode) {  					XNextEvent(view->impl->display, &xevent); -					ignore = true; +					continue;  				}  			}  		} else if (xevent.type == FocusIn) {  			XSetICFocus(view->impl->xic);  		} else if (xevent.type == FocusOut) {  			XUnsetICFocus(view->impl->xic); -		} else if (xevent.type == ConfigureNotify) { -			resized = true;  		} -		if (!ignore) { -			// Translate and dispatch event -			const PuglEvent event = translateEvent(view, xevent); -			puglDispatchEvent(view, &event); -		} -	} +		// Translate X11 event to Pugl event +		const PuglEvent event = translateEvent(view, xevent); -	if (resized) {  #ifdef PUGL_HAVE_CAIRO -		if (view->ctx_type == PUGL_CAIRO) { -			cairo_xlib_surface_set_size( -				view->impl->surface, view->width, view->height); +		if (event.type == PUGL_CONFIGURE && view->ctx_type == PUGL_CAIRO) { +			// Resize surfaces/contexts before dispatching +			cairo_xlib_surface_set_size(view->impl->surface, +			                            event.configure.width, +			                            event.configure.height);  			view->redisplay = true;  		}  #endif + +		// Dispatch event to application +		if (!(view->redisplay && event.type == PUGL_EXPOSE)) { +			puglDispatchEvent(view, &event); +		}  	}  	if (view->redisplay) { | 
