[Win32] Fix spurious mouse enter and leave messages

Mouse tracking and mouse capture didn't work well together. Mouse capture was
also buggy in that it could release capture prematurely.

Mouse capture is now counted and tracked better, multiple requests to set
capture will only call SetCapture once. ReleaseCapture will only be called once
the same number of releases have been made as sets.
MW_MOUSELEAVE messages are now ignored if the mouse is captured.
Mouse tracking is renabled when mouse capture is released.
While the mouse is captured enter and leave events are genereated based on
tracking of the mouse inside MouseMove.

Fixes #301
This commit is contained in:
Fraser Waters 2015-10-17 16:56:35 +01:00
parent 616ac5a017
commit a1cc67c70e

View file

@ -70,6 +70,7 @@ namespace OpenTK.Platform.Windows
bool borderless_maximized_window_state = false; // Hack to get maximized mode with hidden border (not normally possible).
bool focused;
bool mouse_outside_window = true;
int mouse_capture_count = 0;
int mouse_last_timestamp = 0;
bool invisible_since_creation; // Set by WindowsMessage.CREATE and consumed by Visible = true (calls BringWindowToFront).
int suppress_resize; // Used in WindowBorder and WindowState in order to avoid rapid, consecutive resize events.
@ -384,6 +385,14 @@ namespace OpenTK.Platform.Windows
return null;
}
private void HandleCaptureChanged(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
if (lParam != window.Handle)
{
mouse_capture_count = 0;
}
}
void HandleChar(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
char c;
@ -400,12 +409,40 @@ namespace OpenTK.Platform.Windows
void HandleMouseMove(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Point point = new Point(
(short)((uint)lParam.ToInt32() & 0x0000FFFF),
(short)(((uint)lParam.ToInt32() & 0xFFFF0000) >> 16));
if (mouse_capture_count > 0)
{
bool mouse_was_outside_window = mouse_outside_window;
mouse_outside_window = !ClientRectangle.Contains(point);
if (mouse_outside_window && !mouse_was_outside_window)
{
// Mouse leaving
// If we have mouse capture we ignore WM_MOUSELEAVE events, so
// have to manually call OnMouseLeave here.
// Mouse tracking is disabled automatically by the OS
OnMouseLeave(EventArgs.Empty);
}
else if (!mouse_outside_window && mouse_was_outside_window)
{
// Mouse entring
OnMouseEnter(EventArgs.Empty);
}
}
else if (/* !mouse_is_captured && */ mouse_outside_window)
{
// Once we receive a mouse move event, it means that the mouse has
// re-entered the window.
mouse_outside_window = false;
EnableMouseTracking();
OnMouseEnter(EventArgs.Empty);
}
unsafe
{
Point point = new Point(
(short)((uint)lParam.ToInt32() & 0x0000FFFF),
(short)(((uint)lParam.ToInt32() & 0xFFFF0000) >> 16));
// GetMouseMovePointsEx works with screen coordinates
Point screenPoint = point;
Functions.ClientToScreen(handle, ref screenPoint);
@ -479,24 +516,18 @@ namespace OpenTK.Platform.Windows
}
mouse_last_timestamp = timestamp;
}
if (mouse_outside_window)
{
// Once we receive a mouse move event, it means that the mouse has
// re-entered the window.
mouse_outside_window = false;
EnableMouseTracking();
OnMouseEnter(EventArgs.Empty);
}
}
void HandleMouseLeave(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
mouse_outside_window = true;
// Mouse tracking is disabled automatically by the OS
OnMouseLeave(EventArgs.Empty);
// If the mouse is captured we get spurious MOUSELEAVE events.
// So ignore WM_MOUSELEAVE if capture count != 0.
if (mouse_capture_count == 0 )
{
mouse_outside_window = true;
// Mouse tracking is disabled automatically by the OS
OnMouseLeave(EventArgs.Empty);
}
}
void HandleMouseWheel(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
@ -515,25 +546,25 @@ namespace OpenTK.Platform.Windows
void HandleLButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Functions.SetCapture(window.Handle);
SetCapture();
OnMouseDown(MouseButton.Left);
}
void HandleMButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Functions.SetCapture(window.Handle);
SetCapture();
OnMouseDown(MouseButton.Middle);
}
void HandleRButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Functions.SetCapture(window.Handle);
SetCapture();
OnMouseDown(MouseButton.Right);
}
void HandleXButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Functions.SetCapture(window.Handle);
SetCapture();
MouseButton button =
((wParam.ToInt32() & 0xFFFF0000) >> 16) == 1 ?
MouseButton.Button1 : MouseButton.Button2;
@ -542,25 +573,25 @@ namespace OpenTK.Platform.Windows
void HandleLButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Functions.ReleaseCapture();
ReleaseCapture();
OnMouseUp(MouseButton.Left);
}
void HandleMButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Functions.ReleaseCapture();
ReleaseCapture();
OnMouseUp(MouseButton.Middle);
}
void HandleRButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Functions.ReleaseCapture();
ReleaseCapture();
OnMouseUp(MouseButton.Right);
}
void HandleXButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Functions.ReleaseCapture();
ReleaseCapture();
MouseButton button =
((wParam.ToInt32() & 0xFFFF0000) >> 16) == 1 ?
MouseButton.Button1 : MouseButton.Button2;
@ -694,6 +725,10 @@ namespace OpenTK.Platform.Windows
result = HandleSetCursor(handle, message, wParam, lParam);
break;
case WindowMessage.CAPTURECHANGED:
HandleCaptureChanged(handle, message, wParam, lParam);
break;
#endregion
#region Input events
@ -794,6 +829,26 @@ namespace OpenTK.Platform.Windows
}
}
private void SetCapture()
{
if (mouse_capture_count == 0)
{
Functions.SetCapture(window.Handle);
}
mouse_capture_count++;
}
private void ReleaseCapture()
{
mouse_capture_count--;
if (mouse_capture_count == 0)
{
Functions.ReleaseCapture();
// Renable mouse tracking
EnableMouseTracking();
}
}
private void EnableMouseTracking()
{
TrackMouseEventStructure me = new TrackMouseEventStructure();