diff --git a/Source/OpenTK/Platform/Windows/WinGLNative.cs b/Source/OpenTK/Platform/Windows/WinGLNative.cs index 1d24322d..f486d896 100644 --- a/Source/OpenTK/Platform/Windows/WinGLNative.cs +++ b/Source/OpenTK/Platform/Windows/WinGLNative.cs @@ -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();