From 5ba1bf89f1f86547531a7d74290caba87cd13610 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 27 Apr 2014 09:20:33 +0200 Subject: [PATCH 01/51] [OpenTK] Added NativeWindowBase This acts as a common base class to INativeWindow implementations and can be used to reduce code duplication. --- Source/OpenTK/OpenTK.csproj | 1 + Source/OpenTK/Platform/NativeWindowBase.cs | 202 +++++++++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 Source/OpenTK/Platform/NativeWindowBase.cs diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index bdd4e7d0..7d2cbda8 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -797,6 +797,7 @@ + diff --git a/Source/OpenTK/Platform/NativeWindowBase.cs b/Source/OpenTK/Platform/NativeWindowBase.cs new file mode 100644 index 00000000..66252fe1 --- /dev/null +++ b/Source/OpenTK/Platform/NativeWindowBase.cs @@ -0,0 +1,202 @@ +#region License +// +// NativeWindowBase.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2014 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; +using System.Diagnostics; +using System.Drawing; +using OpenTK.Input; + +namespace OpenTK.Platform +{ + + abstract class NativeWindowBase : INativeWindow + { + readonly LegacyInputDriver LegacyInputDriver = + new LegacyInputDriver(); + + public event EventHandler Move = delegate { }; + public event EventHandler Resize = delegate { }; + public event EventHandler Closing = delegate { }; + public event EventHandler Closed = delegate { }; + public event EventHandler Disposed = delegate { }; + public event EventHandler IconChanged = delegate { }; + public event EventHandler TitleChanged = delegate { }; + public event EventHandler VisibleChanged = delegate { }; + public event EventHandler FocusedChanged = delegate { }; + public event EventHandler WindowBorderChanged = delegate { }; + public event EventHandler WindowStateChanged = delegate { }; + public event EventHandler KeyDown = delegate { }; + public event EventHandler KeyPress = delegate { }; + public event EventHandler KeyUp = delegate { }; + public event EventHandler MouseLeave = delegate { }; + public event EventHandler MouseEnter = delegate { }; + + public abstract void Close(); + + public abstract void ProcessEvents(); + + public abstract Point PointToClient(Point point); + + public abstract Point PointToScreen(Point point); + + public abstract Icon Icon { get; set; } + + public abstract string Title { get; set; } + + public abstract bool Focused { get; } + + public abstract bool Visible { get; set; } + + public abstract bool Exists { get; set; } + + public abstract IWindowInfo WindowInfo { get; } + + public abstract WindowState WindowState { get; set; } + + public abstract WindowBorder WindowBorder { get; set; } + + public abstract Rectangle Bounds { get; set; } + + public virtual Point Location + { + get + { + return Bounds.Location; + } + set + { + Bounds = new Rectangle(value, Bounds.Size); + } + } + + public Size Size + { + get + { + return Bounds.Size; + } + set + { + Bounds = new Rectangle(Bounds.Location, value); + } + } + + public int X + { + get + { + return Bounds.X; + } + set + { + Rectangle old = Bounds; + Bounds = new Rectangle(value, old.Y, old.Width, old.Height); + } + } + + public int Y + { + get + { + return Bounds.Y; + } + set + { + Rectangle old = Bounds; + Bounds = new Rectangle(old.X, value, old.Width, old.Height); + } + } + + public int Width + { + get + { + return ClientSize.Width; + } + set + { + Rectangle old = ClientRectangle; + ClientRectangle = new Rectangle(old.X, old.Y, value, old.Height); + } + } + + public int Height + { + get + { + return ClientSize.Height; + } + set + { + Rectangle old = ClientRectangle; + Bounds = new Rectangle(old.X, old.Y, old.Width, value); + } + } + + public Rectangle ClientRectangle + { + get + { + return new Rectangle(Point.Empty, ClientSize); + } + set + { + ClientSize = value.Size; + } + } + + public abstract Size ClientSize { get; set; } + + public virtual IInputDriver InputDriver + { + get + { + return LegacyInputDriver; + } + } + + public abstract bool CursorVisible { get; set; } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(); + } + + protected abstract void Dispose(bool disposing); + + [Conditional("DEBUG")] + ~NativeWindowBase() + { + Debug.Print("NativeWindowBase leaked, did you forget to call Dispose()?"); + Dispose(false); + } + } +} + From f2eca2924f583186fa84d2cb3e14904b7a8c20b6 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 27 Apr 2014 09:21:33 +0200 Subject: [PATCH 02/51] [Mac] Inherit from NativeWindowBase This reduces code duplication significantly. --- .../Platform/MacOS/CocoaNativeWindow.cs | 198 ++++-------------- 1 file changed, 46 insertions(+), 152 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs index 33d53daf..dfdbbf92 100644 --- a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs +++ b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs @@ -1,4 +1,4 @@ -#region License +#region License // // CocoaNativeWindow.cs // @@ -38,27 +38,10 @@ using OpenTK.Input; namespace OpenTK.Platform.MacOS { - class CocoaNativeWindow : INativeWindow + class CocoaNativeWindow : NativeWindowBase { static int UniqueId; - public event EventHandler Move = delegate { }; - public event EventHandler Resize = delegate { }; - public event EventHandler Closing = delegate { }; - public event EventHandler Closed = delegate { }; - public event EventHandler Disposed = delegate { }; - public event EventHandler IconChanged = delegate { }; - public event EventHandler TitleChanged = delegate { }; - public event EventHandler VisibleChanged = delegate { }; - public event EventHandler FocusedChanged = delegate { }; - public event EventHandler WindowBorderChanged = delegate { }; - public event EventHandler WindowStateChanged = delegate { }; - public event EventHandler KeyDown = delegate { }; - public event EventHandler KeyPress = delegate { }; - public event EventHandler KeyUp = delegate { }; - public event EventHandler MouseLeave = delegate { }; - public event EventHandler MouseEnter = delegate { }; - static readonly IntPtr selNextEventMatchingMask = Selector.Get("nextEventMatchingMask:untilDate:inMode:dequeue:"); static readonly IntPtr selSendEvent = Selector.Get("sendEvent:"); //static readonly IntPtr selUpdateWindows = Selector.Get("updateWindows"); @@ -95,6 +78,8 @@ namespace OpenTK.Platform.MacOS static readonly IntPtr selHide = Selector.Get("hide"); static readonly IntPtr selUnhide = Selector.Get("unhide"); static readonly IntPtr selScrollingDeltaY = Selector.Get("scrollingDeltaY"); + static readonly IntPtr selDeltaX = Selector.Get("deltaX"); + static readonly IntPtr selDeltaY = Selector.Get("deltaY"); static readonly IntPtr selButtonNumber = Selector.Get("buttonNumber"); static readonly IntPtr selSetStyleMask = Selector.Get("setStyleMask:"); static readonly IntPtr selStyleMask = Selector.Get("styleMask"); @@ -145,7 +130,6 @@ namespace OpenTK.Platform.MacOS private bool exists; private bool cursorVisible = true; private System.Drawing.Icon icon; - private LegacyInputDriver inputDriver = new LegacyInputDriver(); private WindowBorder windowBorder = WindowBorder.Resizable; private Nullable deferredWindowBorder; private Nullable previousWindowBorder; @@ -377,7 +361,7 @@ namespace OpenTK.Platform.MacOS Cocoa.SendVoid(owner, selAddTrackingArea, trackingArea); } - public void Close() + public override void Close() { shouldClose = true; } @@ -409,7 +393,7 @@ namespace OpenTK.Platform.MacOS return (MouseButton)cocoaButtonIndex; } - public void ProcessEvents() + public override void ProcessEvents() { while (true) { @@ -500,16 +484,32 @@ namespace OpenTK.Platform.MacOS case NSEventType.OtherMouseDragged: case NSEventType.MouseMoved: { - var pf = Cocoa.SendPoint(e, selLocationInWindowOwner); + Point p = InputDriver.Mouse[0].Position; + if (CursorVisible) + { + // Use absolute coordinates + var pf = Cocoa.SendPoint(e, selLocationInWindowOwner); - // Convert from points to pixel coordinates - var rf = Cocoa.SendRect(windowInfo.Handle, selConvertRectToBacking, - new RectangleF(pf.X, pf.Y, 0, 0)); + // Convert from points to pixel coordinates + var rf = Cocoa.SendRect(windowInfo.Handle, selConvertRectToBacking, + new RectangleF(pf.X, pf.Y, 0, 0)); - // See CocoaDrawingGuide under "Converting from Window to View Coordinates" - var p = new Point( - MathHelper.Clamp((int)Math.Round(rf.X), 0, Width), - MathHelper.Clamp((int)Math.Round(Height - rf.Y), 0, Height)); + // See CocoaDrawingGuide under "Converting from Window to View Coordinates" + p = new Point( + MathHelper.Clamp((int)Math.Round(rf.X), 0, Width), + MathHelper.Clamp((int)Math.Round(Height - rf.Y), 0, Height)); + } + else + { + // Mouse has been disassociated, + // use relative coordinates + var dx = Cocoa.SendFloat(e, selDeltaX); + var dy = Cocoa.SendFloat(e, selDeltaY); + + p = new Point( + MathHelper.Clamp((int)Math.Round(p.X + dx)), + MathHelper.Clamp((int)Math.Round(p.Y - dy))); + } InputDriver.Mouse[0].Position = p; } @@ -575,19 +575,19 @@ namespace OpenTK.Platform.MacOS } } - public System.Drawing.Point PointToClient(System.Drawing.Point point) + public override System.Drawing.Point PointToClient(System.Drawing.Point point) { var r = Cocoa.SendRect(windowInfo.Handle, selConvertRectFromScreen, new RectangleF(point.X, point.Y, 0, 0)); return new Point((int)r.X, (int)(GetContentViewFrame().Height - GetCurrentScreenFrame().Height - r.Y)); } - public System.Drawing.Point PointToScreen(System.Drawing.Point point) + public override System.Drawing.Point PointToScreen(System.Drawing.Point point) { var r = Cocoa.SendRect(windowInfo.Handle, selConvertRectToScreen, new RectangleF(point.X, point.Y, 0, 0)); return new Point((int)r.X, (int)(-GetContentViewFrame().Height + GetCurrentScreenFrame().Height - r.Y)); } - public System.Drawing.Icon Icon + public override System.Drawing.Icon Icon { get { return icon; } set @@ -602,7 +602,7 @@ namespace OpenTK.Platform.MacOS } } - public string Title + public override string Title { get { @@ -614,7 +614,7 @@ namespace OpenTK.Platform.MacOS } } - public bool Focused + public override bool Focused { get { @@ -622,7 +622,7 @@ namespace OpenTK.Platform.MacOS } } - public bool Visible + public override bool Visible { get { @@ -635,7 +635,7 @@ namespace OpenTK.Platform.MacOS } } - public bool Exists + public override bool Exists { get { @@ -643,7 +643,7 @@ namespace OpenTK.Platform.MacOS } } - public IWindowInfo WindowInfo + public override IWindowInfo WindowInfo { get { @@ -702,7 +702,7 @@ namespace OpenTK.Platform.MacOS previousWindowBorder = null; } - public WindowState WindowState + public override WindowState WindowState { get { @@ -754,7 +754,7 @@ namespace OpenTK.Platform.MacOS } } - public WindowBorder WindowBorder + public override WindowBorder WindowBorder { get { @@ -795,7 +795,7 @@ namespace OpenTK.Platform.MacOS return (NSWindowStyle)0; } - public System.Drawing.Rectangle Bounds + public override System.Drawing.Rectangle Bounds { get { @@ -820,105 +820,13 @@ namespace OpenTK.Platform.MacOS } } - public System.Drawing.Point Location + public override System.Drawing.Size ClientSize { get - { - return Bounds.Location; - } - set - { - var b = Bounds; - b.Location = value; - Bounds = b; - } - } - - public System.Drawing.Size Size - { - get - { - return Bounds.Size; - } - set - { - var b = Bounds; - b.Y -= Bounds.Height; - b.Y += value.Height; - b.Size = value; - Bounds = b; - } - } - - public int X - { - get - { - return Bounds.X; - } - set - { - var b = Bounds; - b.X = value; - Bounds = b; - } - } - - public int Y - { - get - { - return Bounds.Y; - } - set - { - var b = Bounds; - b.Y = value; - Bounds = b; - } - } - - public int Width - { - get { return ClientRectangle.Width; } - set - { - var s = ClientSize; - s.Width = value; - ClientSize = s; - } - } - - public int Height - { - get { return ClientRectangle.Height; } - set - { - var s = ClientSize; - s.Height = value; - ClientSize = s; - } - } - - public System.Drawing.Rectangle ClientRectangle - { - get { var contentViewBounds = Cocoa.SendRect(windowInfo.ViewHandle, selBounds); var bounds = Cocoa.SendRect(windowInfo.Handle, selConvertRectToBacking, contentViewBounds); - return new Rectangle((int)bounds.X, (int)bounds.Y, (int)bounds.Width, (int)bounds.Height); - } - set - { - ClientSize = value.Size; // Just set size, to be consistent with WinGLNative. - } - } - - public System.Drawing.Size ClientSize - { - get - { - return ClientRectangle.Size; + return new Size(0, 0, (int)bounds.Width, (int)bounds.Height); } set { @@ -928,14 +836,6 @@ namespace OpenTK.Platform.MacOS } } - public OpenTK.Input.IInputDriver InputDriver - { - get - { - return inputDriver; - } - } - public MouseCursor Cursor { get @@ -1075,13 +975,7 @@ namespace OpenTK.Platform.MacOS } } - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) + protected override void Dispose(bool disposing) { if (disposed) return; @@ -1108,9 +1002,9 @@ namespace OpenTK.Platform.MacOS Disposed(this, EventArgs.Empty); } - ~CocoaNativeWindow() + public static IntPtr GetView(IntPtr windowHandle) { - Dispose(false); + return Cocoa.SendIntPtr(windowHandle, selContentView); } private RectangleF GetContentViewFrame() From 24b19d24870be1af8f0441a144cd83d8d719101a Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 27 Apr 2014 09:38:55 +0200 Subject: [PATCH 03/51] [Input] Added internal MouseDevice.Position getter --- Source/OpenTK/Input/MouseDevice.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/OpenTK/Input/MouseDevice.cs b/Source/OpenTK/Input/MouseDevice.cs index 79e1caa0..16a81c97 100644 --- a/Source/OpenTK/Input/MouseDevice.cs +++ b/Source/OpenTK/Input/MouseDevice.cs @@ -232,6 +232,10 @@ namespace OpenTK.Input /// internal Point Position { + get + { + return pos; + } set { pos = value; From 10939fcaec86ab82766e9f5754bde350ef29bc3e Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 27 Apr 2014 09:39:18 +0200 Subject: [PATCH 04/51] [OpenTK] Add On* methods to raise events --- Source/OpenTK/Platform/NativeWindowBase.cs | 98 +++++++++++++++++++++- 1 file changed, 95 insertions(+), 3 deletions(-) diff --git a/Source/OpenTK/Platform/NativeWindowBase.cs b/Source/OpenTK/Platform/NativeWindowBase.cs index 66252fe1..ee974391 100644 --- a/Source/OpenTK/Platform/NativeWindowBase.cs +++ b/Source/OpenTK/Platform/NativeWindowBase.cs @@ -28,6 +28,7 @@ #endregion using System; +using System.ComponentModel; using System.Diagnostics; using System.Drawing; using OpenTK.Input; @@ -40,6 +41,92 @@ namespace OpenTK.Platform readonly LegacyInputDriver LegacyInputDriver = new LegacyInputDriver(); + #region Protected Members + + protected void OnMove(EventArgs e) + { + Move(this, e); + } + + protected void OnResize(EventArgs e) + { + Resize(this, e); + } + + protected void OnClosing(CancelEventArgs e) + { + Closing(this, e); + } + + protected void OnClosed(EventArgs e) + { + Closed(this, e); + } + + protected void OnDisposed(EventArgs e) + { + Disposed(this, e); + } + + protected void OnIconChanged(EventArgs e) + { + IconChanged(this, e); + } + + protected void OnTitleChanged(EventArgs e) + { + TitleChanged(this, e); + } + + protected void OnVisibleChanged(EventArgs e) + { + VisibleChanged(this, e); + } + + protected void OnFocusedChanged(EventArgs e) + { + FocusedChanged(this, e); + } + + protected void OnWindowBorderChanged(EventArgs e) + { + WindowBorderChanged(this, e); + } + + protected void OnWindowStateChanged(EventArgs e) + { + WindowStateChanged(this, e); + } + + protected void OnKeyDown(KeyboardKeyEventArgs e) + { + KeyDown(this, e); + } + + protected void OnKeyPress(KeyPressEventArgs e) + { + KeyPress(this, e); + } + + protected void OnKeyUp(KeyboardKeyEventArgs e) + { + KeyUp(this, e); + } + + protected void OnMouseLeave(EventArgs e) + { + MouseLeave(this, e); + } + + protected void OnMouseEnter(EventArgs e) + { + MouseEnter(this, e); + } + + #endregion + + #region INativeWindow Members + public event EventHandler Move = delegate { }; public event EventHandler Resize = delegate { }; public event EventHandler Closing = delegate { }; @@ -73,7 +160,7 @@ namespace OpenTK.Platform public abstract bool Visible { get; set; } - public abstract bool Exists { get; set; } + public abstract bool Exists { get; } public abstract IWindowInfo WindowInfo { get; } @@ -183,20 +270,25 @@ namespace OpenTK.Platform public abstract bool CursorVisible { get; set; } + #endregion + + #region IDisposable Members + public void Dispose() { Dispose(true); - GC.SuppressFinalize(); + GC.SuppressFinalize(this); } protected abstract void Dispose(bool disposing); - [Conditional("DEBUG")] ~NativeWindowBase() { Debug.Print("NativeWindowBase leaked, did you forget to call Dispose()?"); Dispose(false); } + + #endregion } } From 300203f73b204c07e8626bf8161db58ed16e2eb9 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 27 Apr 2014 09:39:30 +0200 Subject: [PATCH 05/51] [Mac] Use On* methods from base class --- .../Platform/MacOS/CocoaNativeWindow.cs | 53 +++++++++---------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs index dfdbbf92..05011daa 100644 --- a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs +++ b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs @@ -45,6 +45,7 @@ namespace OpenTK.Platform.MacOS static readonly IntPtr selNextEventMatchingMask = Selector.Get("nextEventMatchingMask:untilDate:inMode:dequeue:"); static readonly IntPtr selSendEvent = Selector.Get("sendEvent:"); //static readonly IntPtr selUpdateWindows = Selector.Get("updateWindows"); + static readonly IntPtr selContentView = Selector.Get("contentView"); static readonly IntPtr selConvertRectFromScreen = Selector.Get("convertRectFromScreen:"); static readonly IntPtr selConvertRectToScreen = Selector.Get("convertRectToScreen:"); static readonly IntPtr selPerformClose = Selector.Get("performClose:"); @@ -243,7 +244,7 @@ namespace OpenTK.Platform.MacOS GraphicsContext.CurrentContext.Update(windowInfo); if (suppressResize == 0) - Resize(this, EventArgs.Empty); + OnResize(EventArgs.Empty); } private void ApplicationQuit(object sender, CancelEventArgs e) @@ -256,17 +257,17 @@ namespace OpenTK.Platform.MacOS { // Problem: Called only when you stop moving for a brief moment, // not each frame as it is on PC. - Move(this, EventArgs.Empty); + OnMove(EventArgs.Empty); } private void WindowDidBecomeKey(IntPtr self, IntPtr cmd, IntPtr notification) { - FocusedChanged(this, EventArgs.Empty); + OnFocusedChanged(EventArgs.Empty); } private void WindowDidResignKey(IntPtr self, IntPtr cmd, IntPtr notification) { - FocusedChanged(this, EventArgs.Empty); + OnFocusedChanged(EventArgs.Empty); } private void WindowWillMiniaturize(IntPtr self, IntPtr cmd, IntPtr notification) @@ -280,14 +281,14 @@ namespace OpenTK.Platform.MacOS private void WindowDidMiniaturize(IntPtr self, IntPtr cmd, IntPtr notification) { windowState = WindowState.Minimized; - WindowStateChanged(this, EventArgs.Empty); + OnWindowStateChanged(EventArgs.Empty); OnResize(false); // Don't set tracking area when we minimize } private void WindowDidDeminiaturize(IntPtr self, IntPtr cmd, IntPtr notification) { windowState = WindowState.Normal; - WindowStateChanged(this, EventArgs.Empty); + OnWindowStateChanged(EventArgs.Empty); OnResize(true); } @@ -305,7 +306,7 @@ namespace OpenTK.Platform.MacOS InternalBounds = toFrame; windowState = WindowState.Maximized; - WindowStateChanged(this, EventArgs.Empty); + OnWindowStateChanged(EventArgs.Empty); } return false; } @@ -313,11 +314,11 @@ namespace OpenTK.Platform.MacOS private bool WindowShouldClose(IntPtr self, IntPtr cmd, IntPtr sender) { var cancelArgs = new CancelEventArgs(); - Closing(this, cancelArgs); + OnClosing(cancelArgs); if (!cancelArgs.Cancel) { - Closed(this, EventArgs.Empty); + OnClosed(EventArgs.Empty); return true; } @@ -415,7 +416,7 @@ namespace OpenTK.Platform.MacOS if (!isARepeat || InputDriver.Keyboard[0].KeyRepeat) { - KeyDown(this, keyArgs); + OnKeyDown(keyArgs); } var s = Cocoa.FromNSString(Cocoa.SendIntPtr(e, selCharactersIgnoringModifiers)); @@ -427,7 +428,7 @@ namespace OpenTK.Platform.MacOS // For some reason, arrow keys (mapped 63232-63235) are seen as non-control characters, so get rid of those. keyPressArgs.KeyChar = c; - KeyPress(this, keyPressArgs); + OnKeyPress(keyPressArgs); } } } @@ -441,7 +442,7 @@ namespace OpenTK.Platform.MacOS GetKey(keyCode, modifierFlags, keyArgs); InputDriver.Keyboard[0].SetKey(keyArgs.Key, keyArgs.ScanCode, false); - KeyUp(this, keyArgs); + OnKeyUp(keyArgs); } break; @@ -456,8 +457,7 @@ namespace OpenTK.Platform.MacOS //SetCursor(selectedCursor); } - cursorInsideWindow = true; - MouseEnter(this, EventArgs.Empty); + OnMouseEnter(EventArgs.Empty); } } break; @@ -473,8 +473,7 @@ namespace OpenTK.Platform.MacOS SetCursor(MouseCursor.Default); } - cursorInsideWindow = false; - MouseLeave(this, EventArgs.Empty); + OnMouseLeave(EventArgs.Empty); } } break; @@ -507,8 +506,8 @@ namespace OpenTK.Platform.MacOS var dy = Cocoa.SendFloat(e, selDeltaY); p = new Point( - MathHelper.Clamp((int)Math.Round(p.X + dx)), - MathHelper.Clamp((int)Math.Round(p.Y - dy))); + MathHelper.Clamp((int)Math.Round(p.X + dx), 0, Width), + MathHelper.Clamp((int)Math.Round(p.Y - dy), 0, Height)); } InputDriver.Mouse[0].Position = p; @@ -598,7 +597,7 @@ namespace OpenTK.Platform.MacOS IntPtr nsimg = Cocoa.ToNSImage(img); Cocoa.SendVoid(NSApplication.Handle, selSetApplicationIconImage, nsimg); } - IconChanged(this, EventArgs.Empty); + OnIconChanged(EventArgs.Empty); } } @@ -631,7 +630,7 @@ namespace OpenTK.Platform.MacOS set { Cocoa.SendVoid(windowInfo.Handle, selSetIsVisible, value); - VisibleChanged(this, EventArgs.Empty); + OnVisibleChanged(EventArgs.Empty); } } @@ -735,7 +734,7 @@ namespace OpenTK.Platform.MacOS InternalBounds = GetCurrentScreenFrame(); windowState = value; - WindowStateChanged(this, EventArgs.Empty); + OnWindowStateChanged(EventArgs.Empty); } else if (value == WindowState.Maximized) { @@ -748,8 +747,8 @@ namespace OpenTK.Platform.MacOS else if (value == WindowState.Normal) { windowState = value; - WindowStateChanged(this, EventArgs.Empty); - Resize(this, EventArgs.Empty); + OnWindowStateChanged(EventArgs.Empty); + OnResize(EventArgs.Empty); } } } @@ -773,7 +772,7 @@ namespace OpenTK.Platform.MacOS return; SetWindowBorder(value); - WindowBorderChanged(this, EventArgs.Empty); + OnWindowBorderChanged(EventArgs.Empty); } } @@ -826,7 +825,7 @@ namespace OpenTK.Platform.MacOS { var contentViewBounds = Cocoa.SendRect(windowInfo.ViewHandle, selBounds); var bounds = Cocoa.SendRect(windowInfo.Handle, selConvertRectToBacking, contentViewBounds); - return new Size(0, 0, (int)bounds.Width, (int)bounds.Height); + return new Size((int)bounds.Width, (int)bounds.Height); } set { @@ -999,7 +998,7 @@ namespace OpenTK.Platform.MacOS Cocoa.SendVoid(windowInfo.Handle, Selector.Release); } - Disposed(this, EventArgs.Empty); + OnDisposed(EventArgs.Empty); } public static IntPtr GetView(IntPtr windowHandle) @@ -1072,7 +1071,7 @@ namespace OpenTK.Platform.MacOS Cocoa.SendIntPtr(windowInfo.Handle, selSetTitle, Cocoa.ToNSString(title)); if (callEvent) { - TitleChanged(this, EventArgs.Empty); + OnTitleChanged(EventArgs.Empty); } } From 612652910fe9f4f5bc16308b889c2cc6f3846e97 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Fri, 2 May 2014 14:18:40 +0200 Subject: [PATCH 06/51] [Platform] Inherit NativeWindowBase --- Source/OpenTK/Input/MouseDevice.cs | 51 ++++++ Source/OpenTK/NativeWindow.cs | 20 +++ .../Platform/MacOS/CocoaNativeWindow.cs | 4 +- Source/OpenTK/Platform/NativeWindowBase.cs | 30 +++- .../OpenTK/Platform/SDL2/Sdl2NativeWindow.cs | 166 ++++-------------- Source/OpenTK/Platform/Windows/WinGLNative.cs | 43 ++--- Source/OpenTK/Platform/X11/X11GLNative.cs | 47 ++--- 7 files changed, 177 insertions(+), 184 deletions(-) diff --git a/Source/OpenTK/Input/MouseDevice.cs b/Source/OpenTK/Input/MouseDevice.cs index 16a81c97..9abae909 100644 --- a/Source/OpenTK/Input/MouseDevice.cs +++ b/Source/OpenTK/Input/MouseDevice.cs @@ -379,6 +379,7 @@ namespace OpenTK.Input #region Fields int x, y; + float wheel_x, wheel_y; #endregion @@ -411,6 +412,14 @@ namespace OpenTK.Input { } + internal MouseEventArgs(float x, float y, float wx, float wy) + { + X = (int)Math.Round(x); + Y = (int)Math.Round(y); + WheelX = wx; + WheelY = wy; + } + #endregion #region Public Members @@ -425,6 +434,48 @@ namespace OpenTK.Input /// public int Y { get { return y; } internal set { y = value; } } + /// + /// Gets the offset of the horizontal wheel, if one exists. + /// + public float WheelX { get; internal set; } + + /// + /// Gets the offset of the vertical wheel, if one exists. + /// + public float WheelY { get; internal set; } + + /// + /// Gets the offset of the vertical wheel, if one exists. + /// This is an alias to + /// + /// The wheel. + public float Wheel { get { return WheelY; } } + + /// + /// Gets the of the left mouse button. + /// + public ButtonState LeftButton { get; internal set; } + + /// + /// Gets the of the right mouse button. + /// + public ButtonState RightButton { get; internal set; } + + /// + /// Gets the of the middle mouse button. + /// + public ButtonState MiddleButton { get; internal set; } + + /// + /// Gets the of the first extra mouse button. + /// + public ButtonState X1Button { get; internal set; } + + /// + /// Gets the of the second extra mouse button. + /// + public ButtonState X2Button { get; internal set; } + /// /// Gets a System.Drawing.Points representing the location of the mouse for the event. /// diff --git a/Source/OpenTK/NativeWindow.cs b/Source/OpenTK/NativeWindow.cs index 795b67ef..dade7d81 100644 --- a/Source/OpenTK/NativeWindow.cs +++ b/Source/OpenTK/NativeWindow.cs @@ -680,6 +680,26 @@ namespace OpenTK /// public event EventHandler WindowStateChanged = delegate { }; + /// + /// Occurs when a is pressed. + /// + public event EventHandler MouseDown = delegate { }; + + /// + /// Occurs when a is released. + /// + public event EventHandler MouseUp = delegate { }; + + /// + /// Occurs whenever the mouse is moved. + /// + public event EventHandler MouseMove = delegate { }; + + /// + /// Occurs whenever a mouse wheel is moved; + /// + public event EventHandler MouseWheel = delegate { }; + #endregion #endregion diff --git a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs index 05011daa..8027e3f9 100644 --- a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs +++ b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs @@ -835,7 +835,7 @@ namespace OpenTK.Platform.MacOS } } - public MouseCursor Cursor + public override MouseCursor Cursor { get { @@ -957,7 +957,7 @@ namespace OpenTK.Platform.MacOS Cocoa.SendVoid(windowInfo.Handle, selInvalidateCursorRectsForView, windowInfo.ViewHandle); } - public bool CursorVisible + public override bool CursorVisible { get { return cursorVisible; } set diff --git a/Source/OpenTK/Platform/NativeWindowBase.cs b/Source/OpenTK/Platform/NativeWindowBase.cs index ee974391..20440b05 100644 --- a/Source/OpenTK/Platform/NativeWindowBase.cs +++ b/Source/OpenTK/Platform/NativeWindowBase.cs @@ -35,7 +35,7 @@ using OpenTK.Input; namespace OpenTK.Platform { - + // Common base class for all INativeWindow implementations abstract class NativeWindowBase : INativeWindow { readonly LegacyInputDriver LegacyInputDriver = @@ -123,6 +123,26 @@ namespace OpenTK.Platform MouseEnter(this, e); } + protected void OnMouseDown(MouseButtonEventArgs e) + { + MouseDown(this, e); + } + + protected void OnMouseUp(MouseButtonEventArgs e) + { + MouseUp(this, e); + } + + protected void OnMouseDown(MouseMoveEventArgs e) + { + MouseMove(this, e); + } + + protected void OnMouseWheel(MouseWheelEventArgs e) + { + MouseWheel(this, e); + } + #endregion #region INativeWindow Members @@ -143,6 +163,10 @@ namespace OpenTK.Platform public event EventHandler KeyUp = delegate { }; public event EventHandler MouseLeave = delegate { }; public event EventHandler MouseEnter = delegate { }; + public event EventHandler MouseDown = delegate { }; + public event EventHandler MouseUp = delegate { }; + public event EventHandler MouseMove = delegate { }; + public event EventHandler MouseWheel = delegate { }; public abstract void Close(); @@ -182,7 +206,7 @@ namespace OpenTK.Platform } } - public Size Size + public virtual Size Size { get { @@ -270,6 +294,8 @@ namespace OpenTK.Platform public abstract bool CursorVisible { get; set; } + public abstract MouseCursor Cursor { get; set; } + #endregion #region IDisposable Members diff --git a/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs b/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs index 62e3c0ce..efd4d21e 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs @@ -41,7 +41,7 @@ using System.Text; namespace OpenTK.Platform.SDL2 { - class Sdl2NativeWindow : INativeWindow, IInputDriver + class Sdl2NativeWindow : NativeWindowBase, IInputDriver { readonly object sync = new object(); @@ -236,11 +236,11 @@ namespace OpenTK.Platform.SDL2 window.key_args.Modifiers = window.input_driver.Keyboard[0].GetModifiers(); if (key_pressed) { - window.KeyDown(window, window.key_args); + window.OnKeyDown(window.key_args); } else { - window.KeyUp(window, window.key_args); + window.OnKeyUp(window.key_args); } //window.keyboard.SetKey(TranslateKey(key.scancode), (uint)key.scancode, key_pressed); } @@ -274,7 +274,7 @@ namespace OpenTK.Platform.SDL2 for (int i = 0; i < decoded_length; i++) { window.keypress_args.KeyChar = window.DecodeTextBuffer[i]; - window.KeyPress(window, window.keypress_args); + window.OnKeyPress(window.keypress_args); } } @@ -299,7 +299,7 @@ namespace OpenTK.Platform.SDL2 try { window.is_in_closing_event = true; - window.Closing(window, close_args); + window.OnClosing(close_args); } finally { @@ -308,17 +308,17 @@ namespace OpenTK.Platform.SDL2 if (!close_args.Cancel) { - window.Closed(window, EventArgs.Empty); + window.OnClosed(EventArgs.Empty); window.must_destroy = true; } break; case WindowEventID.ENTER: - window.MouseEnter(window, EventArgs.Empty); + window.OnMouseEnter(EventArgs.Empty); break; case WindowEventID.LEAVE: - window.MouseLeave(window, EventArgs.Empty); + window.OnMouseLeave(EventArgs.Empty); break; case WindowEventID.EXPOSED: @@ -327,47 +327,47 @@ namespace OpenTK.Platform.SDL2 case WindowEventID.FOCUS_GAINED: window.is_focused = true; - window.FocusedChanged(window, EventArgs.Empty); + window.OnFocusedChanged(EventArgs.Empty); break; case WindowEventID.FOCUS_LOST: window.is_focused = false; - window.FocusedChanged(window, EventArgs.Empty); + window.OnFocusedChanged(EventArgs.Empty); break; case WindowEventID.HIDDEN: window.is_visible = false; - window.VisibleChanged(window, EventArgs.Empty); + window.OnVisibleChanged(EventArgs.Empty); break; case WindowEventID.SHOWN: window.is_visible = true; - window.VisibleChanged(window, EventArgs.Empty); + window.OnVisibleChanged(EventArgs.Empty); break; case WindowEventID.MAXIMIZED: window.window_state = WindowState.Maximized; - window.WindowStateChanged(window, EventArgs.Empty); + window.OnWindowStateChanged(EventArgs.Empty); break; case WindowEventID.MINIMIZED: window.previous_window_state = window.window_state; window.window_state = WindowState.Minimized; - window.WindowStateChanged(window, EventArgs.Empty); + window.OnWindowStateChanged(EventArgs.Empty); break; case WindowEventID.RESTORED: window.window_state = window.previous_window_state; - window.WindowStateChanged(window, EventArgs.Empty); + window.OnWindowStateChanged(EventArgs.Empty); break; case WindowEventID.MOVED: - window.Move(window, EventArgs.Empty); + window.OnMove(EventArgs.Empty); break; case WindowEventID.RESIZED: case WindowEventID.SIZE_CHANGED: - window.Resize(window, EventArgs.Empty); + window.OnResize(EventArgs.Empty); break; default: @@ -443,24 +443,7 @@ namespace OpenTK.Platform.SDL2 #region INativeWindow Members - public event EventHandler Move = delegate { }; - public event EventHandler Resize = delegate { }; - public event EventHandler Closing = delegate { }; - public event EventHandler Closed = delegate { }; - public event EventHandler Disposed = delegate { }; - public event EventHandler IconChanged = delegate { }; - public event EventHandler TitleChanged = delegate { }; - public event EventHandler VisibleChanged = delegate { }; - public event EventHandler FocusedChanged = delegate { }; - public event EventHandler WindowBorderChanged = delegate { }; - public event EventHandler WindowStateChanged = delegate { }; - public event EventHandler KeyDown = delegate { }; - public event EventHandler KeyPress = delegate { }; - public event EventHandler KeyUp = delegate { }; - public event EventHandler MouseEnter = delegate { }; - public event EventHandler MouseLeave = delegate { }; - - public MouseCursor Cursor + public override MouseCursor Cursor { get { @@ -540,7 +523,7 @@ namespace OpenTK.Platform.SDL2 } } - public void Close() + public override void Close() { lock (sync) { @@ -560,7 +543,7 @@ namespace OpenTK.Platform.SDL2 } } - public void ProcessEvents() + public override void ProcessEvents() { lock (sync) { @@ -581,21 +564,21 @@ namespace OpenTK.Platform.SDL2 } } - public Point PointToClient(Point point) + public override Point PointToClient(Point point) { var origin = DisplayDevice.Default.Bounds.Location; var client = Location; return new Point(point.X + client.X - origin.X, point.Y + client.Y - origin.Y); } - public Point PointToScreen(Point point) + public override Point PointToScreen(Point point) { var origin = DisplayDevice.Default.Bounds.Location; var client = Location; return new Point(point.X + origin.X - client.X, point.Y + origin.Y - client.Y); } - public Icon Icon + public override Icon Icon { get { @@ -639,13 +622,13 @@ namespace OpenTK.Platform.SDL2 } icon = value; - IconChanged(this, EventArgs.Empty); + OnIconChanged(EventArgs.Empty); } } } } - public string Title + public override string Title { get { @@ -671,7 +654,7 @@ namespace OpenTK.Platform.SDL2 } } - public bool Focused + public override bool Focused { get { @@ -679,7 +662,7 @@ namespace OpenTK.Platform.SDL2 } } - public bool Visible + public override bool Visible { get { @@ -700,7 +683,7 @@ namespace OpenTK.Platform.SDL2 } } - public bool Exists + public override bool Exists { get { @@ -708,7 +691,7 @@ namespace OpenTK.Platform.SDL2 } } - public IWindowInfo WindowInfo + public override IWindowInfo WindowInfo { get { @@ -716,7 +699,7 @@ namespace OpenTK.Platform.SDL2 } } - public WindowState WindowState + public override WindowState WindowState { get { @@ -774,7 +757,7 @@ namespace OpenTK.Platform.SDL2 } } - public WindowBorder WindowBorder + public override WindowBorder WindowBorder { get { @@ -810,13 +793,13 @@ namespace OpenTK.Platform.SDL2 if (Exists) { - WindowBorderChanged(this, EventArgs.Empty); + OnWindowBorderChanged(EventArgs.Empty); } } } } - public Rectangle Bounds + public override Rectangle Bounds { get { @@ -829,7 +812,7 @@ namespace OpenTK.Platform.SDL2 } } - public Point Location + public override Point Location { get { @@ -856,7 +839,7 @@ namespace OpenTK.Platform.SDL2 } } - public Size Size + public override Size Size { get { @@ -883,67 +866,7 @@ namespace OpenTK.Platform.SDL2 } } - public int X - { - get - { - return Location.X; - } - set - { - Location = new Point(value, Y); - } - } - - public int Y - { - get - { - return Location.Y; - } - set - { - Location = new Point(X, value); - } - } - - public int Width - { - get - { - return ClientSize.Width; - } - set - { - ClientSize = new Size(value, Height); - } - } - - public int Height - { - get - { - return ClientSize.Height; - } - set - { - ClientSize = new Size(Width, value); - } - } - - public Rectangle ClientRectangle - { - get - { - return new Rectangle(new Point(), ClientSize); - } - set - { - ClientSize = value.Size; - } - } - - public Size ClientSize + public override Size ClientSize { get { @@ -967,7 +890,7 @@ namespace OpenTK.Platform.SDL2 } } - public IInputDriver InputDriver + public override IInputDriver InputDriver { get { @@ -975,7 +898,7 @@ namespace OpenTK.Platform.SDL2 } } - public bool CursorVisible + public override bool CursorVisible { get { @@ -1043,7 +966,7 @@ namespace OpenTK.Platform.SDL2 #region IDisposable implementation - void Dispose(bool manual) + protected override void Dispose(bool manual) { if (!disposed) { @@ -1082,17 +1005,6 @@ namespace OpenTK.Platform.SDL2 } } - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - ~Sdl2NativeWindow() - { - Dispose(true); - } - #endregion } } diff --git a/Source/OpenTK/Platform/Windows/WinGLNative.cs b/Source/OpenTK/Platform/Windows/WinGLNative.cs index 9cca4c44..fdf615cf 100644 --- a/Source/OpenTK/Platform/Windows/WinGLNative.cs +++ b/Source/OpenTK/Platform/Windows/WinGLNative.cs @@ -44,7 +44,7 @@ namespace OpenTK.Platform.Windows /// Drives GameWindow on Windows. /// This class supports OpenTK, and is not intended for use by OpenTK programs. /// - internal sealed class WinGLNative : INativeWindow, IInputDriver + internal sealed class WinGLNative : NativeWindowBase, IInputDriver { #region Fields @@ -977,7 +977,7 @@ namespace OpenTK.Platform.Windows #region Bounds - public Rectangle Bounds + public override Rectangle Bounds { get { return bounds; } set @@ -1042,7 +1042,7 @@ namespace OpenTK.Platform.Windows #region ClientSize - public Size ClientSize + public override Size ClientSize { get { @@ -1101,7 +1101,7 @@ namespace OpenTK.Platform.Windows #region Icon - public Icon Icon + public override Icon Icon { get { @@ -1126,7 +1126,7 @@ namespace OpenTK.Platform.Windows #region Focused - public bool Focused + public override bool Focused { get { return focused; } } @@ -1136,7 +1136,7 @@ namespace OpenTK.Platform.Windows #region Title StringBuilder sb_title = new StringBuilder(256); - public string Title + public override string Title { get { @@ -1160,7 +1160,7 @@ namespace OpenTK.Platform.Windows #region Visible - public bool Visible + public override bool Visible { get { @@ -1193,13 +1193,13 @@ namespace OpenTK.Platform.Windows #region Exists - public bool Exists { get { return exists; } } + public override bool Exists { get { return exists; } } #endregion #region Cursor - public MouseCursor Cursor + public override MouseCursor Cursor { get { @@ -1270,8 +1270,8 @@ namespace OpenTK.Platform.Windows #endregion #region CursorVisible - - public bool CursorVisible + + public override bool CursorVisible { get { return cursor_visible_count >= 0; } // Not used set @@ -1303,7 +1303,7 @@ namespace OpenTK.Platform.Windows #region Close - public void Close() + public override void Close() { Functions.PostMessage(window.Handle, WindowMessage.CLOSE, IntPtr.Zero, IntPtr.Zero); } @@ -1312,7 +1312,7 @@ namespace OpenTK.Platform.Windows #region public WindowState WindowState - public WindowState WindowState + public override WindowState WindowState { get { @@ -1406,7 +1406,7 @@ namespace OpenTK.Platform.Windows #region public WindowBorder WindowBorder - public WindowBorder WindowBorder + public override WindowBorder WindowBorder { get { @@ -1500,7 +1500,7 @@ namespace OpenTK.Platform.Windows #region PointToClient - public Point PointToClient(Point point) + public override Point PointToClient(Point point) { if (!Functions.ScreenToClient(window.Handle, ref point)) throw new InvalidOperationException(String.Format( @@ -1514,7 +1514,7 @@ namespace OpenTK.Platform.Windows #region PointToScreen - public Point PointToScreen(Point point) + public override Point PointToScreen(Point point) { if (!Functions.ClientToScreen(window.Handle, ref point)) throw new InvalidOperationException(String.Format( @@ -1554,7 +1554,7 @@ namespace OpenTK.Platform.Windows #region public void ProcessEvents() MSG msg; - public void ProcessEvents() + public override void ProcessEvents() { while (Functions.PeekMessage(ref msg, IntPtr.Zero, 0, 0, PeekMessageFlags.Remove)) { @@ -1576,7 +1576,7 @@ namespace OpenTK.Platform.Windows #region public IWindowInfo WindowInfo - public IWindowInfo WindowInfo + public override IWindowInfo WindowInfo { get { return child_window; } } @@ -1640,7 +1640,7 @@ namespace OpenTK.Platform.Windows GC.SuppressFinalize(this); } - void Dispose(bool calledManually) + protected override void Dispose(bool calledManually) { if (!disposed) { @@ -1667,11 +1667,6 @@ namespace OpenTK.Platform.Windows } } - ~WinGLNative() - { - Dispose(false); - } - #endregion } } diff --git a/Source/OpenTK/Platform/X11/X11GLNative.cs b/Source/OpenTK/Platform/X11/X11GLNative.cs index a3cc9126..e5e032c8 100644 --- a/Source/OpenTK/Platform/X11/X11GLNative.cs +++ b/Source/OpenTK/Platform/X11/X11GLNative.cs @@ -45,7 +45,7 @@ namespace OpenTK.Platform.X11 /// Drives GameWindow on X11. /// This class supports OpenTK, and is not intended for use by OpenTK programs. /// - internal sealed class X11GLNative : INativeWindow, IDisposable + internal sealed class X11GLNative : NativeWindowBase { // TODO: Disable screensaver. // TODO: What happens if we can't disable decorations through motif? @@ -785,7 +785,7 @@ namespace OpenTK.Platform.X11 #region ProcessEvents - public void ProcessEvents() + public override void ProcessEvents() { // Process all pending events while (Exists && window != null) @@ -1015,7 +1015,7 @@ namespace OpenTK.Platform.X11 #region Bounds - public Rectangle Bounds + public override Rectangle Bounds { get { @@ -1116,7 +1116,7 @@ namespace OpenTK.Platform.X11 #region ClientSize - public Size ClientSize + public override Size ClientSize { get { @@ -1172,7 +1172,7 @@ namespace OpenTK.Platform.X11 #region Icon - public Icon Icon + public override Icon Icon { get { @@ -1246,7 +1246,7 @@ namespace OpenTK.Platform.X11 #region Focused - public bool Focused + public override bool Focused { get { @@ -1258,7 +1258,7 @@ namespace OpenTK.Platform.X11 #region WindowState - public OpenTK.WindowState WindowState + public override OpenTK.WindowState WindowState { get { @@ -1395,7 +1395,7 @@ namespace OpenTK.Platform.X11 #region WindowBorder - public OpenTK.WindowBorder WindowBorder + public override OpenTK.WindowBorder WindowBorder { get { @@ -1465,7 +1465,7 @@ namespace OpenTK.Platform.X11 #region Cursor - public MouseCursor Cursor + public override MouseCursor Cursor { get { @@ -1505,7 +1505,7 @@ namespace OpenTK.Platform.X11 #region CursorVisible - public bool CursorVisible + public override bool CursorVisible { get { return cursor_visible; } set @@ -1552,7 +1552,7 @@ namespace OpenTK.Platform.X11 /// /// Returns true if a render window/context exists. /// - public bool Exists + public override bool Exists { get { return exists; } } @@ -1586,7 +1586,7 @@ namespace OpenTK.Platform.X11 /// TODO: Use atoms for this property. /// Gets or sets the GameWindow title. /// - public string Title + public override string Title { get { @@ -1618,7 +1618,7 @@ namespace OpenTK.Platform.X11 #region public bool Visible - public bool Visible + public override bool Visible { get { @@ -1647,14 +1647,14 @@ namespace OpenTK.Platform.X11 #region public IWindowInfo WindowInfo - public IWindowInfo WindowInfo + public override IWindowInfo WindowInfo { get { return window; } } #endregion - public void Close() { Exit(); } + public override void Close() { Exit(); } #region public void Exit() @@ -1691,7 +1691,7 @@ namespace OpenTK.Platform.X11 #region PointToClient - public Point PointToClient(Point point) + public override Point PointToClient(Point point) { int ox, oy; IntPtr child; @@ -1711,7 +1711,7 @@ namespace OpenTK.Platform.X11 #region PointToScreen - public Point PointToScreen(Point point) + public override Point PointToScreen(Point point) { int ox, oy; IntPtr child; @@ -1733,13 +1733,7 @@ namespace OpenTK.Platform.X11 #region IDisposable Members - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool manuallyCalled) + protected override void Dispose(bool manuallyCalled) { if (!disposed) { @@ -1775,11 +1769,6 @@ namespace OpenTK.Platform.X11 } } - ~X11GLNative() - { - this.Dispose(false); - } - #endregion } } From 89fc36bde12e55f7012327a02e471a49877a1218 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Fri, 2 May 2014 14:18:52 +0200 Subject: [PATCH 07/51] [Platform] Added INativeWindow.Mouse* events --- Source/OpenTK/INativeWindow.cs | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/Source/OpenTK/INativeWindow.cs b/Source/OpenTK/INativeWindow.cs index 5cad3d6b..925a2b86 100644 --- a/Source/OpenTK/INativeWindow.cs +++ b/Source/OpenTK/INativeWindow.cs @@ -260,10 +260,26 @@ namespace OpenTK /// event EventHandler MouseEnter; - //event EventHandler MouseMove; - //event EventHandler MouseWheel; - //event EventHandler MouseDown; - //event EventHandler MouseUp; + /// + /// Occurs whenever a is clicked. + /// + event EventHandler MouseDown; + + /// + /// Occurs whenever a is released. + /// + event EventHandler MouseUp; + + /// + /// Occurs whenever the mouse cursor is moved; + /// + event EventHandler MouseMove; + + /// + /// Occurs whenever a mouse wheel is moved; + /// + event EventHandler MouseWheel; + //event EventHandler MouseClick; //event EventHandler MouseDoubleClick; From 3824f55f183489b61f76bec6802d4b71b9adf2cc Mon Sep 17 00:00:00 2001 From: thefiddler Date: Fri, 2 May 2014 14:21:32 +0200 Subject: [PATCH 08/51] [Mac] Fixed cursor coords when CursorVisible=false --- Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs index 8027e3f9..53fb2962 100644 --- a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs +++ b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs @@ -507,7 +507,7 @@ namespace OpenTK.Platform.MacOS p = new Point( MathHelper.Clamp((int)Math.Round(p.X + dx), 0, Width), - MathHelper.Clamp((int)Math.Round(p.Y - dy), 0, Height)); + MathHelper.Clamp((int)Math.Round(p.Y + dy), 0, Height)); } InputDriver.Mouse[0].Position = p; From 674cd54c2942b40d230addf0a60df1d02e8d8abc Mon Sep 17 00:00:00 2001 From: thefiddler Date: Fri, 2 May 2014 15:13:38 +0200 Subject: [PATCH 09/51] [Mac] Fixed initial window position Cocoa sets the desktop origin at the bottom-left of the main screen, with +y going up. OpenTK is setting the origin at the top-left of the main screen, so we need to invert the y-axis. --- Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs index 53fb2962..fd662e42 100644 --- a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs +++ b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs @@ -173,7 +173,17 @@ namespace OpenTK.Platform.MacOS Class.RegisterClass(viewClass); // Create window instance - var contentRect = new System.Drawing.RectangleF(x, y, width, height); + // Note: The coordinate system of Cocoa places (0,0) at the bottom left. + // We need to get the height of the main screen and flip that in order + // to place the window at the correct position. + // Note: NSWindows are laid out relative to the main screen. + var screenRect = + Cocoa.SendRect( + Cocoa.SendIntPtr( + Cocoa.SendIntPtr(Class.Get("NSScreen"), Selector.Get("screens")), + Selector.Get("objectAtIndex:"), 0), + Selector.Get("frame")); + var contentRect = new System.Drawing.RectangleF(x, screenRect.Height - height - y, width, height); var style = GetStyleMask(windowBorder); var bufferingType = NSBackingStore.Buffered; From 3e33ac928032072f625c24440cf8332880c91d75 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Fri, 2 May 2014 16:52:11 +0200 Subject: [PATCH 10/51] [Platform] Refactored INativeWindow backends All INativeWindow implementations are now derived from NativeWindowBase. They no longer implement legacy IInputDriver themselves, but rather rely on LegacyInputDriver provided by NativeWindowBase for compatibility. They also implement the new Mouse* events. --- Source/OpenTK/Input/KeyboardDevice.cs | 6 +- Source/OpenTK/Input/MouseDevice.cs | 94 ++++++-- Source/OpenTK/Platform/LegacyInputDriver.cs | 65 +++++- .../Platform/MacOS/CocoaNativeWindow.cs | 3 - Source/OpenTK/Platform/NativeWindowBase.cs | 19 +- Source/OpenTK/Platform/SDL2/Sdl2Factory.cs | 2 +- .../OpenTK/Platform/SDL2/Sdl2InputDriver.cs | 47 +--- Source/OpenTK/Platform/SDL2/Sdl2KeyMap.cs | 9 + Source/OpenTK/Platform/SDL2/Sdl2Keyboard.cs | 24 +-- .../OpenTK/Platform/SDL2/Sdl2NativeWindow.cs | 64 +----- Source/OpenTK/Platform/Windows/WinGLNative.cs | 203 +++++------------- Source/OpenTK/Platform/X11/X11GLNative.cs | 3 - 12 files changed, 224 insertions(+), 315 deletions(-) diff --git a/Source/OpenTK/Input/KeyboardDevice.cs b/Source/OpenTK/Input/KeyboardDevice.cs index 9544f420..87fde900 100644 --- a/Source/OpenTK/Input/KeyboardDevice.cs +++ b/Source/OpenTK/Input/KeyboardDevice.cs @@ -197,7 +197,7 @@ namespace OpenTK.Input #endregion - internal void SetKey(Key key, uint scancode, bool state) + internal void SetKey(Key key, uint scancode, KeyModifiers mods, bool state) { if (keys[(int)key] != state || KeyRepeat) { @@ -211,14 +211,14 @@ namespace OpenTK.Input { args.Key = key; args.ScanCode = scancode; - args.Modifiers = GetModifiers(); + args.Modifiers = mods; KeyDown(this, args); } else if (!state && KeyUp != null) { args.Key = key; args.ScanCode = scancode; - args.Modifiers = GetModifiers(); + args.Modifiers = mods; KeyUp(this, args); } } diff --git a/Source/OpenTK/Input/MouseDevice.cs b/Source/OpenTK/Input/MouseDevice.cs index 9abae909..2e922ff3 100644 --- a/Source/OpenTK/Input/MouseDevice.cs +++ b/Source/OpenTK/Input/MouseDevice.cs @@ -379,7 +379,7 @@ namespace OpenTK.Input #region Fields int x, y; - float wheel_x, wheel_y; + int buttons; #endregion @@ -412,12 +412,35 @@ namespace OpenTK.Input { } - internal MouseEventArgs(float x, float y, float wx, float wy) + #endregion + + #region Protected Members + + protected internal void SetButton(MouseButton button, ButtonState state) { - X = (int)Math.Round(x); - Y = (int)Math.Round(y); - WheelX = wx; - WheelY = wy; + if (button < 0 || button > MouseButton.LastButton) + throw new ArgumentOutOfRangeException(); + + switch (state) + { + case ButtonState.Pressed: + buttons |= 1 << (int)button; + break; + + case ButtonState.Released: + buttons &= ~(1 << (int)button); + break; + } + } + + protected internal ButtonState GetButton(MouseButton button) + { + if (button < 0 || button > MouseButton.LastButton) + throw new ArgumentOutOfRangeException(); + + return + (buttons & (1 << (int)button)) != 0 ? + ButtonState.Pressed : ButtonState.Released; } #endregion @@ -449,37 +472,65 @@ namespace OpenTK.Input /// This is an alias to /// /// The wheel. - public float Wheel { get { return WheelY; } } + public float Wheel { get { return WheelY; } internal set { WheelY = value; } } /// /// Gets the of the left mouse button. /// - public ButtonState LeftButton { get; internal set; } + public ButtonState LeftButton + { + get { return GetButton(MouseButton.Left); } + internal set { SetButton(MouseButton.Left, value); } + } /// /// Gets the of the right mouse button. /// - public ButtonState RightButton { get; internal set; } + public ButtonState RightButton + { + get { return GetButton(MouseButton.Right); } + internal set { SetButton(MouseButton.Right, value); } + } /// /// Gets the of the middle mouse button. /// - public ButtonState MiddleButton { get; internal set; } + public ButtonState MiddleButton + { + get { return GetButton(MouseButton.Middle); } + internal set { SetButton(MouseButton.Middle, value); } + } /// /// Gets the of the first extra mouse button. /// - public ButtonState X1Button { get; internal set; } + public ButtonState X1Button + { + get { return GetButton(MouseButton.Button1); } + internal set { SetButton(MouseButton.Button1, value); } + } /// /// Gets the of the second extra mouse button. /// - public ButtonState X2Button { get; internal set; } + public ButtonState X2Button + { + get { return GetButton(MouseButton.Button2); } + internal set { SetButton(MouseButton.Button2, value); } + } /// - /// Gets a System.Drawing.Points representing the location of the mouse for the event. + /// Gets a representing the location of the mouse for the event. /// - public Point Position { get { return new Point(x, y); } } + public Point Position + { + get { return new Point(x, y); } + set + { + X = value.X; + Y = value.Y; + } + } #endregion } @@ -603,14 +654,18 @@ namespace OpenTK.Input #region Public Members /// - /// The mouse button for the event. + /// Gets the that triggered this event. /// public MouseButton Button { get { return button; } internal set { button = value; } } /// /// Gets a System.Boolean representing the state of the mouse button for the event. /// - public bool IsPressed { get { return pressed; } internal set { pressed = value; } } + public bool IsPressed + { + get { return GetButton(Button) == ButtonState.Pressed; } + internal set { SetButton(Button, value ? ButtonState.Pressed : ButtonState.Released); } + } #endregion } @@ -629,7 +684,6 @@ namespace OpenTK.Input { #region Fields - float value; float delta; #endregion @@ -651,7 +705,7 @@ namespace OpenTK.Input public MouseWheelEventArgs(int x, int y, int value, int delta) : base(x, y) { - this.value = value; + WheelY = value; this.delta = delta; } @@ -672,7 +726,7 @@ namespace OpenTK.Input /// Gets the value of the wheel in integer units. /// To support high-precision mice, it is recommended to use instead. /// - public int Value { get { return (int)Math.Round(value, MidpointRounding.AwayFromZero); } } + public int Value { get { return (int)Math.Round(WheelY, MidpointRounding.AwayFromZero); } } /// /// Gets the change in value of the wheel for this event in integer units. @@ -683,7 +737,7 @@ namespace OpenTK.Input /// /// Gets the precise value of the wheel in floating-point units. /// - public float ValuePrecise { get { return value; } internal set { this.value = value; } } + public float ValuePrecise { get { return WheelY; } internal set { WheelY = value; } } /// /// Gets the precise change in value of the wheel for this event in floating-point units. diff --git a/Source/OpenTK/Platform/LegacyInputDriver.cs b/Source/OpenTK/Platform/LegacyInputDriver.cs index 67461587..290f6321 100644 --- a/Source/OpenTK/Platform/LegacyInputDriver.cs +++ b/Source/OpenTK/Platform/LegacyInputDriver.cs @@ -42,18 +42,63 @@ namespace OpenTK.Platform readonly LegacyJoystickDriver JoystickDriver = new LegacyJoystickDriver(); - internal LegacyInputDriver() + internal LegacyInputDriver(INativeWindow window) { - dummy_mice_list.Add(new MouseDevice()); - Mouse[0].Description = "Standard Mouse"; - Mouse[0].NumberOfButtons = 3; - Mouse[0].NumberOfWheels = 1; + if (window == null) + throw new ArgumentNullException(); - dummy_keyboard_list.Add(new KeyboardDevice()); - Keyboard[0].Description = "Standard Keyboard"; - Keyboard[0].NumberOfKeys = 101; - Keyboard[0].NumberOfLeds = 3; - Keyboard[0].NumberOfFunctionKeys = 12; + var mouse = new MouseDevice(); + mouse.Description = "Standard Mouse"; + mouse.NumberOfButtons = 3; + mouse.NumberOfWheels = 1; + dummy_mice_list.Add(mouse); + + var keyboard = new KeyboardDevice(); + keyboard.Description = "Standard Keyboard"; + keyboard.NumberOfKeys = 101; + keyboard.NumberOfLeds = 3; + keyboard.NumberOfFunctionKeys = 12; + dummy_keyboard_list.Add(keyboard); + + // Hook mouse events + window.MouseDown += (sender, e) => + { + mouse[e.Button] = true; + }; + + window.MouseUp += (sender, e) => + { + mouse[e.Button] = false; + }; + + window.MouseMove += (sender, e) => + { + mouse.Position = e.Position; + }; + + window.MouseWheel += (sender, e) => + { + mouse.WheelPrecise = e.WheelY; + }; + + // Hook keyboard events + window.KeyDown += (sender, e) => + { + keyboard.SetKey(e.Key, e.ScanCode, e.Modifiers, true); + }; + + window.KeyUp += (sender, e) => + { + keyboard.SetKey(e.Key, e.ScanCode, e.Modifiers, false); + }; + + window.FocusedChanged += (sender, e) => + { + if (!window.Focused) + { + keyboard.ClearKeys(); + } + }; } #region IInputDriver Members diff --git a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs index fd662e42..2d95a330 100644 --- a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs +++ b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs @@ -422,7 +422,6 @@ namespace OpenTK.Platform.MacOS var modifierFlags = (NSEventModifierMask)Cocoa.SendUint(e, selModifierFlags); var isARepeat = Cocoa.SendBool(e, selIsARepeat); GetKey(keyCode, modifierFlags, keyArgs); - InputDriver.Keyboard[0].SetKey(keyArgs.Key, keyArgs.ScanCode, true); if (!isARepeat || InputDriver.Keyboard[0].KeyRepeat) { @@ -450,8 +449,6 @@ namespace OpenTK.Platform.MacOS var modifierFlags = (NSEventModifierMask)Cocoa.SendUint(e, selModifierFlags); GetKey(keyCode, modifierFlags, keyArgs); - InputDriver.Keyboard[0].SetKey(keyArgs.Key, keyArgs.ScanCode, false); - OnKeyUp(keyArgs); } break; diff --git a/Source/OpenTK/Platform/NativeWindowBase.cs b/Source/OpenTK/Platform/NativeWindowBase.cs index 20440b05..ed7f32ea 100644 --- a/Source/OpenTK/Platform/NativeWindowBase.cs +++ b/Source/OpenTK/Platform/NativeWindowBase.cs @@ -38,8 +38,21 @@ namespace OpenTK.Platform // Common base class for all INativeWindow implementations abstract class NativeWindowBase : INativeWindow { - readonly LegacyInputDriver LegacyInputDriver = - new LegacyInputDriver(); + readonly LegacyInputDriver LegacyInputDriver; + + readonly protected MouseButtonEventArgs MouseDownArgs = new MouseButtonEventArgs(); + readonly protected MouseButtonEventArgs MouseUpArgs = new MouseButtonEventArgs(); + readonly protected MouseMoveEventArgs MouseMoveArgs = new MouseMoveEventArgs(); + readonly protected MouseWheelEventArgs MouseWheelArgs = new MouseWheelEventArgs(); + + readonly protected KeyboardKeyEventArgs KeyDownArgs = new KeyboardKeyEventArgs(); + readonly protected KeyboardKeyEventArgs KeyUpArgs = new KeyboardKeyEventArgs(); + readonly protected KeyPressEventArgs KeyPressArgs = new KeyPressEventArgs((char)0); + + internal NativeWindowBase() + { + LegacyInputDriver = new LegacyInputDriver(this); + } #region Protected Members @@ -133,7 +146,7 @@ namespace OpenTK.Platform MouseUp(this, e); } - protected void OnMouseDown(MouseMoveEventArgs e) + protected void OnMouseMove(MouseMoveEventArgs e) { MouseMove(this, e); } diff --git a/Source/OpenTK/Platform/SDL2/Sdl2Factory.cs b/Source/OpenTK/Platform/SDL2/Sdl2Factory.cs index 25a3c6d9..66654243 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2Factory.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2Factory.cs @@ -57,7 +57,7 @@ namespace OpenTK.Platform.SDL2 public override INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device) { - return new Sdl2NativeWindow(x, y, width, height, title, options, device, InputDriver); + return new Sdl2NativeWindow(x, y, width, height, title, options, device); } public override IDisplayDeviceDriver CreateDisplayDeviceDriver() diff --git a/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs index 5e88fd6d..73972acb 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs @@ -34,7 +34,7 @@ using OpenTK.Input; namespace OpenTK.Platform.SDL2 { - class Sdl2InputDriver : IInputDriver2, IInputDriver + class Sdl2InputDriver : IInputDriver2 { readonly static Dictionary DriverHandles = new Dictionary(); @@ -154,51 +154,6 @@ namespace OpenTK.Platform.SDL2 #endregion - #region IInputDriver Members - - public void Poll() - { - joystick_driver.Poll(); - } - - #endregion - - #region IJoystickDriver Members - - public IList Joysticks - { - get - { - return joystick_driver.Joysticks; - } - } - - #endregion - - #region IMouseDriver Members - - public IList Mouse - { - get - { - return mouse_driver.Mouse; - } - } - - #endregion - - #region IKeyboardDriver Members - - public IList Keyboard - { - get - { - return keyboard_driver.Keyboard; - } - } - - #endregion - #region IInputDriver2 Members public IMouseDriver2 MouseDriver diff --git a/Source/OpenTK/Platform/SDL2/Sdl2KeyMap.cs b/Source/OpenTK/Platform/SDL2/Sdl2KeyMap.cs index a73ca8dc..c5ad1d14 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2KeyMap.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2KeyMap.cs @@ -298,6 +298,15 @@ namespace OpenTK.Platform.SDL2 return Key.Unknown; } } + + public static KeyModifiers GetModifiers(Keymod mod) + { + KeyModifiers result = 0; + result |= (mod & Keymod.ALT) != 0 ? KeyModifiers.Alt : 0; + result |= (mod & Keymod.CTRL) != 0 ? KeyModifiers.Control : 0; + result |= (mod & Keymod.SHIFT) != 0 ? KeyModifiers.Shift : 0; + return result; + } } } diff --git a/Source/OpenTK/Platform/SDL2/Sdl2Keyboard.cs b/Source/OpenTK/Platform/SDL2/Sdl2Keyboard.cs index e4a18d86..21c7d85a 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2Keyboard.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2Keyboard.cs @@ -31,7 +31,7 @@ using OpenTK.Input; namespace OpenTK.Platform.SDL2 { - class Sdl2Keyboard : IKeyboardDriver2, IKeyboardDriver + class Sdl2Keyboard : IKeyboardDriver2 { KeyboardState state; @@ -42,13 +42,6 @@ namespace OpenTK.Platform.SDL2 public Sdl2Keyboard() { state.IsConnected = true; - - keyboards.Add(new KeyboardDevice()); - keyboards[0].Description = "Standard keyboard"; - keyboards[0].NumberOfFunctionKeys = 12; - keyboards[0].NumberOfKeys = 101; - keyboards[0].NumberOfLeds = 3; - keyboards_readonly = keyboards.AsReadOnly(); } #region Private Members @@ -86,22 +79,11 @@ namespace OpenTK.Platform.SDL2 bool pressed = e.State != 0; var scancode = e.Keysym.Scancode; Key key = Sdl2KeyMap.GetKey(scancode); + KeyModifiers mods = Sdl2KeyMap.GetModifiers(e.Keysym.Mod); + if (key != Key.Unknown) { state.SetKeyState(key, (byte)scancode, pressed); - keyboards[0].SetKey(key, (byte)scancode, pressed); - } - } - - #endregion - - #region IKeyboardDriver Members - - public IList Keyboard - { - get - { - return keyboards_readonly; } } diff --git a/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs b/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs index efd4d21e..f30ba51d 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs @@ -41,7 +41,7 @@ using System.Text; namespace OpenTK.Platform.SDL2 { - class Sdl2NativeWindow : NativeWindowBase, IInputDriver + class Sdl2NativeWindow : NativeWindowBase { readonly object sync = new object(); @@ -72,19 +72,14 @@ namespace OpenTK.Platform.SDL2 // Argument for KeyDown and KeyUp events (allocated once to avoid runtime allocations) readonly KeyboardKeyEventArgs key_args = new KeyboardKeyEventArgs(); - readonly IInputDriver input_driver; - static readonly Dictionary windows = new Dictionary(); public Sdl2NativeWindow(int x, int y, int width, int height, - string title, GameWindowFlags options, DisplayDevice device, - IInputDriver input_driver) + string title, GameWindowFlags options, DisplayDevice device) { lock (sync) { - this.input_driver = input_driver; - var bounds = device.Bounds; var flags = TranslateFlags(options); flags |= WindowFlags.OPENGL; @@ -233,7 +228,7 @@ namespace OpenTK.Platform.SDL2 var key = ev.Key.Keysym; window.key_args.Key = TranslateKey(key.Scancode); window.key_args.ScanCode = (uint)key.Scancode; - window.key_args.Modifiers = window.input_driver.Keyboard[0].GetModifiers(); + window.key_args.Modifiers = Sdl2KeyMap.GetModifiers(key.Mod); if (key_pressed) { window.OnKeyDown(window.key_args); @@ -890,14 +885,6 @@ namespace OpenTK.Platform.SDL2 } } - public override IInputDriver InputDriver - { - get - { - return input_driver; - } - } - public override bool CursorVisible { get @@ -919,51 +906,6 @@ namespace OpenTK.Platform.SDL2 #endregion - #region IInputDriver Members - - public void Poll() - { - InputDriver.Poll(); - } - - #endregion - - #region IJoystickDriver Members - - public IList Joysticks - { - get - { - return InputDriver.Joysticks; - } - } - - #endregion - - #region IMouseDriver Members - - public IList Mouse - { - get - { - return InputDriver.Mouse; - } - } - - #endregion - - #region IKeyboardDriver Members - - public IList Keyboard - { - get - { - return InputDriver.Keyboard; - } - } - - #endregion - #region IDisposable implementation protected override void Dispose(bool manual) diff --git a/Source/OpenTK/Platform/Windows/WinGLNative.cs b/Source/OpenTK/Platform/Windows/WinGLNative.cs index fdf615cf..4e8f7328 100644 --- a/Source/OpenTK/Platform/Windows/WinGLNative.cs +++ b/Source/OpenTK/Platform/Windows/WinGLNative.cs @@ -44,7 +44,7 @@ namespace OpenTK.Platform.Windows /// Drives GameWindow on Windows. /// This class supports OpenTK, and is not intended for use by OpenTK programs. /// - internal sealed class WinGLNative : NativeWindowBase, IInputDriver + internal sealed class WinGLNative : NativeWindowBase { #region Fields @@ -82,12 +82,6 @@ namespace OpenTK.Platform.Windows const ClassStyle DefaultClassStyle = ClassStyle.OwnDC; - // Used for IInputDriver implementation - IJoystickDriver joystick_driver = Factory.Default.CreateLegacyJoystickDriver(); - KeyboardDevice keyboard = new KeyboardDevice(); - MouseDevice mouse = new MouseDevice(); - IList keyboards = new List(1); - IList mice = new List(1); const long ExtendedBit = 1 << 24; // Used to distinguish left and right control, alt and enter keys. public static readonly uint ShiftLeftScanCode = Functions.MapVirtualKey(VirtualKeys.LSHIFT, 0); @@ -97,10 +91,6 @@ namespace OpenTK.Platform.Windows public static readonly uint AltLeftScanCode = Functions.MapVirtualKey(VirtualKeys.LMENU, 0); public static readonly uint AltRightScanCode = Functions.MapVirtualKey(VirtualKeys.RMENU, 0); - KeyboardKeyEventArgs key_down = new KeyboardKeyEventArgs(); - KeyboardKeyEventArgs key_up = new KeyboardKeyEventArgs(); - KeyPressEventArgs key_press = new KeyPressEventArgs((char)0); - MouseCursor cursor = MouseCursor.Default; IntPtr cursor_handle = Functions.LoadCursor(CursorName.Arrow); int cursor_visible_count = 0; @@ -160,18 +150,6 @@ namespace OpenTK.Platform.Windows window); exists = true; - - keyboard.Description = "Standard Windows keyboard"; - keyboard.NumberOfFunctionKeys = 12; - keyboard.NumberOfKeys = 101; - keyboard.NumberOfLeds = 3; - - mouse.Description = "Standard Windows mouse"; - mouse.NumberOfButtons = 3; - mouse.NumberOfWheels = 1; - - keyboards.Add(keyboard); - mice.Add(mouse); } } @@ -254,7 +232,7 @@ namespace OpenTK.Platform.Windows focused = (wParam.ToInt64() & 0xFFFF) != 0; if (new_focused_state != Focused) - FocusedChanged(this, EventArgs.Empty); + OnFocusedChanged(EventArgs.Empty); } void HandleEnterModalLoop(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) @@ -292,7 +270,7 @@ namespace OpenTK.Platform.Windows if (Location != new_location) { bounds.Location = new_location; - Move(this, EventArgs.Empty); + OnMove(EventArgs.Empty); } Size new_size = new Size(pos->cx, pos->cy); @@ -310,7 +288,7 @@ namespace OpenTK.Platform.Windows SetWindowPosFlags.NOACTIVATE | SetWindowPosFlags.NOSENDCHANGING); if (suppress_resize <= 0) - Resize(this, EventArgs.Empty); + OnResize(EventArgs.Empty); } if (!is_in_modal_loop) @@ -352,7 +330,7 @@ namespace OpenTK.Platform.Windows GrabCursor(); windowBorder = new_border; - WindowBorderChanged(this, EventArgs.Empty); + OnWindowBorderChanged(EventArgs.Empty); } } @@ -380,7 +358,7 @@ namespace OpenTK.Platform.Windows if (new_state != windowState) { windowState = new_state; - WindowStateChanged(this, EventArgs.Empty); + OnWindowStateChanged(EventArgs.Empty); // Ensure cursor remains grabbed if (!CursorVisible) @@ -415,8 +393,8 @@ namespace OpenTK.Platform.Windows if (!Char.IsControl(c)) { - key_press.KeyChar = c; - KeyPress(this, key_press); + KeyPressArgs.KeyChar = c; + OnKeyPress(KeyPressArgs); } } @@ -459,7 +437,8 @@ namespace OpenTK.Platform.Windows if (points == 0 || (points == -1 && lastError == Constants.ERROR_POINT_NOT_FOUND)) { // Just use the mouse move position - mouse.Position = point; + MouseMoveArgs.Position = point; + OnMouseMove(MouseMoveArgs); } else if (points == -1) { @@ -468,7 +447,7 @@ namespace OpenTK.Platform.Windows else { // Exclude the current position. - Point currentScreenPosition = new Point(mouse.X, mouse.Y); + Point currentScreenPosition = new Point(InputDriver.Mouse[0].X, InputDriver.Mouse[0].Y); Functions.ClientToScreen(handle, ref currentScreenPosition); // Find the first move point we've already seen. @@ -497,7 +476,8 @@ namespace OpenTK.Platform.Windows position.Y -= 65536; } Functions.ScreenToClient(handle, ref position); - mouse.Position = position; + MouseMoveArgs.Position = position; + OnMouseMove(MouseMoveArgs); } } mouse_last_timestamp = timestamp; @@ -510,7 +490,7 @@ namespace OpenTK.Platform.Windows mouse_outside_window = false; EnableMouseTracking(); - MouseEnter(this, EventArgs.Empty); + OnMouseEnter(EventArgs.Empty); } } @@ -519,64 +499,85 @@ namespace OpenTK.Platform.Windows mouse_outside_window = true; // Mouse tracking is disabled automatically by the OS - MouseLeave(this, EventArgs.Empty); + OnMouseLeave(EventArgs.Empty); } void HandleMouseWheel(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { // This is due to inconsistent behavior of the WParam value on 64bit arch, whese // wparam = 0xffffffffff880000 or wparam = 0x00000000ff100000 - mouse.WheelPrecise += ((long)wParam << 32 >> 48) / 120.0f; + MouseWheelArgs.Wheel += ((long)wParam << 32 >> 48) / 120.0f; + OnMouseWheel(MouseWheelArgs); } void HandleLButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { Functions.SetCapture(window.Handle); - mouse[MouseButton.Left] = true; + MouseDownArgs.Button = MouseButton.Left; + MouseDownArgs.IsPressed = true; + OnMouseDown(MouseDownArgs); } void HandleMButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { Functions.SetCapture(window.Handle); - mouse[MouseButton.Middle] = true; + MouseDownArgs.Button = MouseButton.Middle; + MouseDownArgs.IsPressed = true; + OnMouseDown(MouseDownArgs); } void HandleRButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { Functions.SetCapture(window.Handle); - mouse[MouseButton.Right] = true; + MouseDownArgs.Button = MouseButton.Right; + MouseDownArgs.IsPressed = true; + OnMouseDown(MouseDownArgs); } void HandleXButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { Functions.SetCapture(window.Handle); - mouse[((wParam.ToInt32() & 0xFFFF0000) >> 16) == 1 ? - MouseButton.Button1 : MouseButton.Button2] = true; + MouseButton button = + ((wParam.ToInt32() & 0xFFFF0000) >> 16) == 1 ? + MouseButton.Button1 : MouseButton.Button2; + MouseDownArgs.Button = button; + MouseDownArgs.IsPressed = true; + OnMouseDown(MouseDownArgs); } void HandleLButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { Functions.ReleaseCapture(); - mouse[MouseButton.Left] = false; + MouseDownArgs.Button = MouseButton.Left; + MouseDownArgs.IsPressed = false; + OnMouseUp(MouseUpArgs); } void HandleMButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { Functions.ReleaseCapture(); - mouse[MouseButton.Middle] = false; + MouseDownArgs.Button = MouseButton.Middle; + MouseDownArgs.IsPressed = false; + OnMouseUp(MouseUpArgs); } void HandleRButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { Functions.ReleaseCapture(); - mouse[MouseButton.Right] = false; + MouseDownArgs.Button = MouseButton.Right; + MouseDownArgs.IsPressed = false; + OnMouseUp(MouseUpArgs); } void HandleXButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { Functions.ReleaseCapture(); - mouse[((wParam.ToInt32() & 0xFFFF0000) >> 16) == 1 ? - MouseButton.Button1 : MouseButton.Button2] = false; + MouseButton button = + ((wParam.ToInt32() & 0xFFFF0000) >> 16) == 1 ? + MouseButton.Button1 : MouseButton.Button2; + MouseDownArgs.Button = button; + MouseDownArgs.IsPressed = false; + OnMouseUp(MouseUpArgs); } void HandleKeyboard(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) @@ -600,26 +601,23 @@ namespace OpenTK.Platform.Windows if (is_valid) { - keyboard.SetKey(key, (byte)scancode, pressed); - if (pressed) { - key_down.Key = key; - key_down.Modifiers = keyboard.GetModifiers(); - KeyDown(this, key_down); + KeyDownArgs.Key = key; + KeyDownArgs.Modifiers = InputDriver.Keyboard[0].GetModifiers(); + OnKeyDown(KeyDownArgs); } else { - key_up.Key = key; - key_up.Modifiers = keyboard.GetModifiers(); - KeyUp(this, key_up); + KeyUpArgs.Key = key; + KeyUpArgs.Modifiers = InputDriver.Keyboard[0].GetModifiers(); + OnKeyUp(KeyUpArgs); } } } void HandleKillFocus(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { - keyboard.ClearKeys(); } void HandleCreate(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) @@ -644,7 +642,7 @@ namespace OpenTK.Platform.Windows { System.ComponentModel.CancelEventArgs e = new System.ComponentModel.CancelEventArgs(); - Closing(this, e); + OnClosing(e); if (!e.Cancel) { @@ -663,7 +661,7 @@ namespace OpenTK.Platform.Windows window.Dispose(); child_window.Dispose(); - Closed(this, EventArgs.Empty); + OnClosed(EventArgs.Empty); } #endregion @@ -1117,7 +1115,7 @@ namespace OpenTK.Platform.Windows Functions.SendMessage(window.Handle, WindowMessage.SETICON, (IntPtr)0, icon == null ? IntPtr.Zero : value.Handle); Functions.SendMessage(window.Handle, WindowMessage.SETICON, (IntPtr)1, icon == null ? IntPtr.Zero : value.Handle); } - IconChanged(this, EventArgs.Empty); + OnIconChanged(EventArgs.Empty); } } } @@ -1151,7 +1149,7 @@ namespace OpenTK.Platform.Windows { if (!Functions.SetWindowText(window.Handle, value)) Debug.Print("Failed to change window title (window:{0}, new title:{1}, reason:{2}).", window.Handle, value, Marshal.GetLastWin32Error()); - TitleChanged(this, EventArgs.Empty); + OnTitleChanged(EventArgs.Empty); } } } @@ -1184,7 +1182,7 @@ namespace OpenTK.Platform.Windows Functions.ShowWindow(window.Handle, ShowWindowCommand.HIDE); } - VisibleChanged(this, EventArgs.Empty); + OnVisibleChanged(EventArgs.Empty); } } } @@ -1526,27 +1524,6 @@ namespace OpenTK.Platform.Windows #endregion - #region Events - - public event EventHandler Move = delegate { }; - public event EventHandler Resize = delegate { }; - public event EventHandler Closing = delegate { }; - public event EventHandler Closed = delegate { }; - public event EventHandler Disposed = delegate { }; - public event EventHandler IconChanged = delegate { }; - public event EventHandler TitleChanged = delegate { }; - public event EventHandler VisibleChanged = delegate { }; - public event EventHandler FocusedChanged = delegate { }; - public event EventHandler WindowBorderChanged = delegate { }; - public event EventHandler WindowStateChanged = delegate { }; - public event EventHandler KeyDown = delegate { }; - public event EventHandler KeyPress = delegate { }; - public event EventHandler KeyUp = delegate { }; - public event EventHandler MouseEnter = delegate { }; - public event EventHandler MouseLeave = delegate { }; - - #endregion - #endregion #region INativeGLWindow Members @@ -1565,15 +1542,6 @@ namespace OpenTK.Platform.Windows #endregion - #region public IInputDriver InputDriver - - public IInputDriver InputDriver - { - get { return this; } - } - - #endregion - #region public IWindowInfo WindowInfo public override IWindowInfo WindowInfo @@ -1585,61 +1553,8 @@ namespace OpenTK.Platform.Windows #endregion - #region IInputDriver Members - - public void Poll() - { - if (joystick_driver is WinMMJoystick) - (joystick_driver as WinMMJoystick).Poll(); - } - - #endregion - - #region IKeyboardDriver Members - - public IList Keyboard - { - get { return keyboards; } - } - - public KeyboardState GetState() - { - throw new NotImplementedException(); - } - - public KeyboardState GetState(int index) - { - throw new NotImplementedException(); - } - - #endregion - - #region IMouseDriver Members - - public IList Mouse - { - get { return mice; } - } - - #endregion - - #region IJoystickDriver Members - - public IList Joysticks - { - get { return joystick_driver.Joysticks; } - } - - #endregion - #region IDisposable Members - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - protected override void Dispose(bool calledManually) { if (!disposed) @@ -1662,7 +1577,7 @@ namespace OpenTK.Platform.Windows Debug.Print("[Warning] INativeWindow leaked ({0}). Did you forget to call INativeWindow.Dispose()?", this); } - Disposed(this, EventArgs.Empty); + OnDisposed(EventArgs.Empty); disposed = true; } } diff --git a/Source/OpenTK/Platform/X11/X11GLNative.cs b/Source/OpenTK/Platform/X11/X11GLNative.cs index e5e032c8..4109da30 100644 --- a/Source/OpenTK/Platform/X11/X11GLNative.cs +++ b/Source/OpenTK/Platform/X11/X11GLNative.cs @@ -862,9 +862,6 @@ namespace OpenTK.Platform.X11 Key key; if (driver.TranslateKey(ref e.KeyEvent, out key)) { - // Update legacy GameWindow.Keyboard API: - keyboard.SetKey(key, (uint)e.KeyEvent.keycode, pressed); - if (pressed) { // Raise KeyDown event From 842c0499b9e3fe98ca97bd9f7a7ac880c0d46179 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Fri, 2 May 2014 17:39:00 +0200 Subject: [PATCH 11/51] [SDL] Corrected SDL_Mouse* event structs --- Source/OpenTK/Platform/SDL2/Sdl2.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2.cs b/Source/OpenTK/Platform/SDL2/Sdl2.cs index 11c8c468..28a348de 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2.cs @@ -1565,8 +1565,8 @@ namespace OpenTK.Platform.SDL2 public UInt32 Which; public Button Button; public State State; + public byte Clicks; byte padding1; - byte padding2; public Int32 X; public Int32 Y; } @@ -1577,10 +1577,7 @@ namespace OpenTK.Platform.SDL2 public uint Timestamp; public uint WindowID; public uint Which; - public State State; - byte padding1; - byte padding2; - byte padding3; + public ButtonFlags State; public Int32 X; public Int32 Y; public Int32 Xrel; @@ -1610,10 +1607,6 @@ namespace OpenTK.Platform.SDL2 } public const uint TouchMouseID = 0xffffffff; - - public static class GL - { - } } struct Rect From e85377c35074ae39980386fb2ee9b126639ca411 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sat, 3 May 2014 16:47:03 +0200 Subject: [PATCH 12/51] [Platform] Added support for horizontal wheel --- .../Examples/OpenTK/Test/GameWindowStates.cs | 61 ++++++++++++++++++- Source/OpenTK/Input/MouseDevice.cs | 45 ++++++++------ Source/OpenTK/Platform/LegacyInputDriver.cs | 2 +- Source/OpenTK/Platform/NativeWindowBase.cs | 4 ++ Source/OpenTK/Platform/SDL2/Sdl2Mouse.cs | 29 +-------- .../OpenTK/Platform/SDL2/Sdl2NativeWindow.cs | 55 +++++++++++++---- Source/OpenTK/Platform/Windows/API.cs | 5 +- Source/OpenTK/Platform/Windows/WinGLNative.cs | 14 ++++- 8 files changed, 154 insertions(+), 61 deletions(-) diff --git a/Source/Examples/OpenTK/Test/GameWindowStates.cs b/Source/Examples/OpenTK/Test/GameWindowStates.cs index 2e5ce406..e75f8977 100644 --- a/Source/Examples/OpenTK/Test/GameWindowStates.cs +++ b/Source/Examples/OpenTK/Test/GameWindowStates.cs @@ -26,6 +26,10 @@ namespace Examples.Tests bool mouse_in_window = false; bool viewport_changed = true; + // mouse information + Vector4 mouse_pos; + int mouse_buttons; + // time drift Stopwatch watch = new Stopwatch(); double update_time, render_time; @@ -57,6 +61,7 @@ namespace Examples.Tests MouseLeave += delegate { mouse_in_window = false; }; Mouse.Move += MouseMoveHandler; + Mouse.WheelChanged += MouseWheelHandler; Mouse.ButtonDown += MouseButtonHandler; Mouse.ButtonUp += MouseButtonHandler; } @@ -119,6 +124,10 @@ namespace Examples.Tests void MouseMoveHandler(object sender, MouseMoveEventArgs e) { + mouse_pos.X = e.X; + mouse_pos.Y = e.Y; + mouse_pos.Z = e.Wheel.X; + mouse_pos.W = e.Wheel.Y; } void MouseButtonHandler(object sender, MouseButtonEventArgs e) @@ -127,6 +136,21 @@ namespace Examples.Tests { CursorVisible = false; } + + if (e.IsPressed) + { + mouse_buttons |= 1 << (int)e.Button; + } + else + { + mouse_buttons &= ~(1 << (int)e.Button); + } + } + + void MouseWheelHandler(object sender, MouseWheelEventArgs e) + { + mouse_pos.Z += e.Wheel.Y; + mouse_pos.W += e.Wheel.X; } static int Clamp(int val, int min, int max) @@ -201,6 +225,40 @@ namespace Examples.Tests return line; } + int DrawMouseDevice(Graphics gfx, int line) + { + StringBuilder sb = new StringBuilder(); + sb.Append("MouseDevice: "); + sb.Append(new Vector3(Mouse.X, Mouse.Y, Mouse.Wheel)); + sb.Append(" "); + for (var i = MouseButton.Left; i < MouseButton.LastButton; i++) + { + if (Mouse[i]) + { + sb.Append(i); + sb.Append(" "); + } + } + sb.AppendLine(); + DrawString(gfx, sb.ToString(), line++); + + sb.Remove(0, sb.Length); + sb.Append("Mouse events: "); + sb.Append(mouse_pos); + sb.Append(" "); + for (var i = MouseButton.Left; i < MouseButton.LastButton; i++) + { + if ((mouse_buttons & (1 << (int)i)) != 0) + { + sb.Append(i); + sb.Append(" "); + } + } + sb.AppendLine(); + DrawString(gfx, sb.ToString(), line++); + return line; + } + static int DrawLegacyJoysticks(Graphics gfx, IList joysticks, int line) { line++; @@ -267,7 +325,8 @@ namespace Examples.Tests mouse_in_window ? "inside" : "outside", CursorVisible ? "visible" : "hidden", Focused ? "Focused" : "Not focused"), line++); - DrawString(gfx, String.Format("Mouse coordinates: {0}", new Vector3(Mouse.X, Mouse.Y, Mouse.WheelPrecise)), line++); + + line = DrawMouseDevice(gfx, line); // Timing information line++; diff --git a/Source/OpenTK/Input/MouseDevice.cs b/Source/OpenTK/Input/MouseDevice.cs index 2e922ff3..ebcd7f74 100644 --- a/Source/OpenTK/Input/MouseDevice.cs +++ b/Source/OpenTK/Input/MouseDevice.cs @@ -362,6 +362,24 @@ namespace OpenTK.Input #endregion } + /// + /// Represents a mouse wheel. + /// + public sealed class MouseWheel + { + /// + /// Gets the X offset of the wheel. + /// + /// The x. + public float X { get; internal set; } + + /// + /// Gets the Y offset of the wheel. + /// + /// The y. + public float Y { get; internal set; } + } + #region Event Arguments /// @@ -390,6 +408,7 @@ namespace OpenTK.Input /// public MouseEventArgs() { + Wheel = new MouseWheel(); } /// @@ -416,7 +435,7 @@ namespace OpenTK.Input #region Protected Members - protected internal void SetButton(MouseButton button, ButtonState state) + internal void SetButton(MouseButton button, ButtonState state) { if (button < 0 || button > MouseButton.LastButton) throw new ArgumentOutOfRangeException(); @@ -433,7 +452,7 @@ namespace OpenTK.Input } } - protected internal ButtonState GetButton(MouseButton button) + internal ButtonState GetButton(MouseButton button) { if (button < 0 || button > MouseButton.LastButton) throw new ArgumentOutOfRangeException(); @@ -458,21 +477,9 @@ namespace OpenTK.Input public int Y { get { return y; } internal set { y = value; } } /// - /// Gets the offset of the horizontal wheel, if one exists. + /// Gets the status of the mouse wheel. /// - public float WheelX { get; internal set; } - - /// - /// Gets the offset of the vertical wheel, if one exists. - /// - public float WheelY { get; internal set; } - - /// - /// Gets the offset of the vertical wheel, if one exists. - /// This is an alias to - /// - /// The wheel. - public float Wheel { get { return WheelY; } internal set { WheelY = value; } } + public MouseWheel Wheel { get; private set; } /// /// Gets the of the left mouse button. @@ -705,7 +712,7 @@ namespace OpenTK.Input public MouseWheelEventArgs(int x, int y, int value, int delta) : base(x, y) { - WheelY = value; + Wheel.Y = value; this.delta = delta; } @@ -726,7 +733,7 @@ namespace OpenTK.Input /// Gets the value of the wheel in integer units. /// To support high-precision mice, it is recommended to use instead. /// - public int Value { get { return (int)Math.Round(WheelY, MidpointRounding.AwayFromZero); } } + public int Value { get { return (int)Math.Round(Wheel.Y, MidpointRounding.AwayFromZero); } } /// /// Gets the change in value of the wheel for this event in integer units. @@ -737,7 +744,7 @@ namespace OpenTK.Input /// /// Gets the precise value of the wheel in floating-point units. /// - public float ValuePrecise { get { return WheelY; } internal set { WheelY = value; } } + public float ValuePrecise { get { return Wheel.Y; } internal set { Wheel.Y = value; } } /// /// Gets the precise change in value of the wheel for this event in floating-point units. diff --git a/Source/OpenTK/Platform/LegacyInputDriver.cs b/Source/OpenTK/Platform/LegacyInputDriver.cs index 290f6321..96d0a02a 100644 --- a/Source/OpenTK/Platform/LegacyInputDriver.cs +++ b/Source/OpenTK/Platform/LegacyInputDriver.cs @@ -78,7 +78,7 @@ namespace OpenTK.Platform window.MouseWheel += (sender, e) => { - mouse.WheelPrecise = e.WheelY; + mouse.WheelPrecise = e.Wheel.Y; }; // Hook keyboard events diff --git a/Source/OpenTK/Platform/NativeWindowBase.cs b/Source/OpenTK/Platform/NativeWindowBase.cs index ed7f32ea..64e6bee0 100644 --- a/Source/OpenTK/Platform/NativeWindowBase.cs +++ b/Source/OpenTK/Platform/NativeWindowBase.cs @@ -49,6 +49,10 @@ namespace OpenTK.Platform readonly protected KeyboardKeyEventArgs KeyUpArgs = new KeyboardKeyEventArgs(); readonly protected KeyPressEventArgs KeyPressArgs = new KeyPressEventArgs((char)0); + // In order to simplify mouse event implementation, + // we can store the current mouse state here. + protected MouseState MouseState = new MouseState(); + internal NativeWindowBase() { LegacyInputDriver = new LegacyInputDriver(this); diff --git a/Source/OpenTK/Platform/SDL2/Sdl2Mouse.cs b/Source/OpenTK/Platform/SDL2/Sdl2Mouse.cs index ffd8cc38..887ad7af 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2Mouse.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2Mouse.cs @@ -33,28 +33,18 @@ using OpenTK.Input; namespace OpenTK.Platform.SDL2 { - class Sdl2Mouse : IMouseDriver2, IMouseDriver + class Sdl2Mouse : IMouseDriver2 { MouseState state; - readonly List mice = - new List(); - readonly IList mice_readonly; - public Sdl2Mouse() { state.IsConnected = true; - - mice.Add(new MouseDevice()); - mice[0].Description = "Standard mouse"; - mice[0].NumberOfButtons = 3; - mice[0].NumberOfWheels = 1; - mice_readonly = mice.AsReadOnly(); } #region Private Members - MouseButton TranslateButton(Button button) + static internal MouseButton TranslateButton(Button button) { switch (button) { @@ -98,33 +88,18 @@ namespace OpenTK.Platform.SDL2 public void ProcessWheelEvent(MouseWheelEvent wheel) { state.WheelPrecise += wheel.Y; - mice[0].WheelPrecise += wheel.Y; } public void ProcessMouseEvent(MouseMotionEvent motion) { state.X += motion.Xrel; state.Y += motion.Yrel; - mice[0].Position = new Point(motion.X, motion.Y); } public void ProcessMouseEvent(MouseButtonEvent button) { bool pressed = button.State == State.Pressed; SetButtonState(TranslateButton(button.Button), pressed); - mice[0][TranslateButton(button.Button)] = pressed; - } - - #endregion - - #region IMouseDriver Members - - public IList Mouse - { - get - { - return mice_readonly; - } } #endregion diff --git a/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs b/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs index f30ba51d..8c3367fd 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs @@ -175,7 +175,7 @@ namespace OpenTK.Platform.SDL2 case EventType.MOUSEBUTTONUP: if (windows.TryGetValue(ev.Button.WindowID, out window)) { - ProcessButtonEvent(window, ev); + ProcessMouseButtonEvent(window, ev.Button); processed = true; } break; @@ -183,7 +183,7 @@ namespace OpenTK.Platform.SDL2 case EventType.MOUSEMOTION: if (windows.TryGetValue(ev.Motion.WindowID, out window)) { - ProcessMotionEvent(window, ev); + ProcessMouseMotionEvent(window, ev.Motion); processed = true; } break; @@ -191,7 +191,7 @@ namespace OpenTK.Platform.SDL2 case EventType.MOUSEWHEEL: if (windows.TryGetValue(ev.Wheel.WindowID, out window)) { - ProcessWheelEvent(window, ev); + ProcessMouseWheelEvent(window, ev.Wheel); processed = true; } break; @@ -209,9 +209,9 @@ namespace OpenTK.Platform.SDL2 return processed ? 0 : 1; } - static void ProcessButtonEvent(Sdl2NativeWindow window, Event ev) + static void ProcessMouseButtonEvent(Sdl2NativeWindow window, MouseButtonEvent ev) { - bool button_pressed = ev.Button.State == State.Pressed; + bool button_pressed = ev.State == State.Pressed; // We need MouseUp events to be reported even if they occur // outside the window. SetWindowGrab ensures we get them. @@ -220,6 +220,23 @@ namespace OpenTK.Platform.SDL2 SDL.SetWindowGrab(window.window.Handle, button_pressed ? true : false); } + + var e = button_pressed ? window.MouseDownArgs : window.MouseUpArgs; + e.Button = Sdl2Mouse.TranslateButton(ev.Button); + e.IsPressed = button_pressed; + e.X = ev.X; + e.Y = ev.Y; + e.Wheel.X = window.MouseWheelArgs.Wheel.X; + e.Wheel.Y = window.MouseWheelArgs.Wheel.Y; + + if (button_pressed) + { + window.OnMouseDown(e); + } + else + { + window.OnMouseUp(e); + } } static void ProcessKeyEvent(Sdl2NativeWindow window, Event ev) @@ -273,16 +290,32 @@ namespace OpenTK.Platform.SDL2 } } - static void ProcessMotionEvent(Sdl2NativeWindow window, Event ev) + static void ProcessMouseMotionEvent(Sdl2NativeWindow window, MouseMotionEvent ev) { - float scale = window.ClientSize.Width / (float)window.Size.Width; - //window.mouse.Position = new Point( - // (int)(ev.motion.x * scale), (int)(ev.motion.y * scale)); + //float scale = window.ClientSize.Width / (float)window.Size.Width; + var e = window.MouseMoveArgs; + e.X = ev.X; + e.Y = ev.Y; + SetMouseButtons(e, ev.State); + window.OnMouseMove(e); } - static void ProcessWheelEvent(Sdl2NativeWindow window, Event ev) + static void SetMouseButtons(MouseEventArgs e, ButtonFlags buttons) { - //window.mouse.Wheel += ev.wheel.y; + for (int i = 0; i < 5; i++) + { + // Note: OpenTK MouseButton is identical to SDL2 Button + bool pressed = ((int)buttons & (1 << i)) != 0; + e.SetButton((MouseButton)i, pressed ? ButtonState.Pressed : ButtonState.Released); + } + } + + static void ProcessMouseWheelEvent(Sdl2NativeWindow window, MouseWheelEvent ev) + { + var e = window.MouseWheelArgs; + e.Wheel.Y = ev.Y; + e.Wheel.X = ev.X; + window.OnMouseWheel(e); } static void ProcessWindowEvent(Sdl2NativeWindow window, WindowEvent e) diff --git a/Source/OpenTK/Platform/Windows/API.cs b/Source/OpenTK/Platform/Windows/API.cs index 454d2aa0..3a0fd548 100644 --- a/Source/OpenTK/Platform/Windows/API.cs +++ b/Source/OpenTK/Platform/Windows/API.cs @@ -4197,7 +4197,6 @@ namespace OpenTK.Platform.Windows MBUTTONUP = 0x0208, MBUTTONDBLCLK = 0x0209, MOUSEWHEEL = 0x020A, - MOUSELAST = 0x020D, /// /// Windows 2000 and higher only. /// @@ -4210,6 +4209,10 @@ namespace OpenTK.Platform.Windows /// Windows 2000 and higher only. /// XBUTTONDBLCLK = 0x020D, + /// + /// Windows Vista and higher only. + /// + MOUSEHWHEEL = 0x020E, PARENTNOTIFY = 0x0210, ENTERMENULOOP = 0x0211, EXITMENULOOP = 0x0212, diff --git a/Source/OpenTK/Platform/Windows/WinGLNative.cs b/Source/OpenTK/Platform/Windows/WinGLNative.cs index 4e8f7328..da560894 100644 --- a/Source/OpenTK/Platform/Windows/WinGLNative.cs +++ b/Source/OpenTK/Platform/Windows/WinGLNative.cs @@ -506,7 +506,15 @@ namespace OpenTK.Platform.Windows { // This is due to inconsistent behavior of the WParam value on 64bit arch, whese // wparam = 0xffffffffff880000 or wparam = 0x00000000ff100000 - MouseWheelArgs.Wheel += ((long)wParam << 32 >> 48) / 120.0f; + MouseWheelArgs.Wheel.Y += ((long)wParam << 32 >> 48) / 120.0f; + OnMouseWheel(MouseWheelArgs); + } + + void HandleMouseHWheel(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + // This is due to inconsistent behavior of the WParam value on 64bit arch, whese + // wparam = 0xffffffffff880000 or wparam = 0x00000000ff100000 + MouseWheelArgs.Wheel.X += ((long)wParam << 32 >> 48) / 120.0f; OnMouseWheel(MouseWheelArgs); } @@ -729,6 +737,10 @@ namespace OpenTK.Platform.Windows HandleMouseWheel(handle, message, wParam, lParam); break; + case WindowMessage.MOUSEHWHEEL: + HandleMouseHWheel(handle, message, wParam, lParam); + break; + case WindowMessage.LBUTTONDOWN: HandleLButtonDown(handle, message, wParam, lParam); break; From 94fdf1881c65f753a71d67e171eecc5dae057cf2 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 4 May 2014 01:29:55 +0200 Subject: [PATCH 13/51] [Platform] Improved mouse interface - The complete mouse state is now available in mouse events - Horizontal wheels are now supported - MouseState now takes up less memory and has a simpler internal implementation. --- .../Examples/OpenTK/Test/GameWindowStates.cs | 8 +- Source/OpenTK/Input/MouseDevice.cs | 394 ------------------ Source/OpenTK/Input/MouseEventArgs.cs | 370 ++++++++++++++++ Source/OpenTK/Input/MouseScrollWheel.cs | 119 ++++++ Source/OpenTK/Input/MouseState.cs | 111 ++--- Source/OpenTK/OpenTK.csproj | 2 + Source/OpenTK/Platform/LegacyInputDriver.cs | 2 +- .../Platform/MacOS/CocoaNativeWindow.cs | 13 +- Source/OpenTK/Platform/MacOS/HIDInput.cs | 17 +- Source/OpenTK/Platform/NativeWindowBase.cs | 81 +++- Source/OpenTK/Platform/SDL2/Sdl2Mouse.cs | 2 +- .../OpenTK/Platform/SDL2/Sdl2NativeWindow.cs | 38 +- Source/OpenTK/Platform/Windows/API.cs | 3 +- Source/OpenTK/Platform/Windows/WinGLNative.cs | 58 ++- Source/OpenTK/Platform/Windows/WinRawMouse.cs | 5 +- Source/OpenTK/Platform/X11/XI2Mouse.cs | 4 +- 16 files changed, 675 insertions(+), 552 deletions(-) create mode 100644 Source/OpenTK/Input/MouseEventArgs.cs create mode 100644 Source/OpenTK/Input/MouseScrollWheel.cs diff --git a/Source/Examples/OpenTK/Test/GameWindowStates.cs b/Source/Examples/OpenTK/Test/GameWindowStates.cs index e75f8977..703c608e 100644 --- a/Source/Examples/OpenTK/Test/GameWindowStates.cs +++ b/Source/Examples/OpenTK/Test/GameWindowStates.cs @@ -126,8 +126,8 @@ namespace Examples.Tests { mouse_pos.X = e.X; mouse_pos.Y = e.Y; - mouse_pos.Z = e.Wheel.X; - mouse_pos.W = e.Wheel.Y; + mouse_pos.Z = e.Mouse.Scroll.X; + mouse_pos.W = e.Mouse.Scroll.Y; } void MouseButtonHandler(object sender, MouseButtonEventArgs e) @@ -149,8 +149,8 @@ namespace Examples.Tests void MouseWheelHandler(object sender, MouseWheelEventArgs e) { - mouse_pos.Z += e.Wheel.Y; - mouse_pos.W += e.Wheel.X; + mouse_pos.Z += e.Mouse.Scroll.Y; + mouse_pos.W += e.Mouse.Scroll.X; } static int Clamp(int val, int min, int max) diff --git a/Source/OpenTK/Input/MouseDevice.cs b/Source/OpenTK/Input/MouseDevice.cs index ebcd7f74..85b9f837 100644 --- a/Source/OpenTK/Input/MouseDevice.cs +++ b/Source/OpenTK/Input/MouseDevice.cs @@ -361,398 +361,4 @@ namespace OpenTK.Input #endregion } - - /// - /// Represents a mouse wheel. - /// - public sealed class MouseWheel - { - /// - /// Gets the X offset of the wheel. - /// - /// The x. - public float X { get; internal set; } - - /// - /// Gets the Y offset of the wheel. - /// - /// The y. - public float Y { get; internal set; } - } - - #region Event Arguments - - /// - /// Defines the event data for events. - /// - /// - /// - /// Do not cache instances of this type outside their event handler. - /// If necessary, you can clone an instance using the - /// constructor. - /// - /// - public class MouseEventArgs : EventArgs - { - #region Fields - - int x, y; - int buttons; - - #endregion - - #region Constructors - - /// - /// Constructs a new instance. - /// - public MouseEventArgs() - { - Wheel = new MouseWheel(); - } - - /// - /// Constructs a new instance. - /// - /// The X position. - /// The Y position. - public MouseEventArgs(int x, int y) - { - this.x = x; - this.y = y; - } - - /// - /// Constructs a new instance. - /// - /// The instance to clone. - public MouseEventArgs(MouseEventArgs args) - : this(args.x, args.y) - { - } - - #endregion - - #region Protected Members - - internal void SetButton(MouseButton button, ButtonState state) - { - if (button < 0 || button > MouseButton.LastButton) - throw new ArgumentOutOfRangeException(); - - switch (state) - { - case ButtonState.Pressed: - buttons |= 1 << (int)button; - break; - - case ButtonState.Released: - buttons &= ~(1 << (int)button); - break; - } - } - - internal ButtonState GetButton(MouseButton button) - { - if (button < 0 || button > MouseButton.LastButton) - throw new ArgumentOutOfRangeException(); - - return - (buttons & (1 << (int)button)) != 0 ? - ButtonState.Pressed : ButtonState.Released; - } - - #endregion - - #region Public Members - - /// - /// Gets the X position of the mouse for the event. - /// - public int X { get { return x; } internal set { x = value; } } - - /// - /// Gets the Y position of the mouse for the event. - /// - public int Y { get { return y; } internal set { y = value; } } - - /// - /// Gets the status of the mouse wheel. - /// - public MouseWheel Wheel { get; private set; } - - /// - /// Gets the of the left mouse button. - /// - public ButtonState LeftButton - { - get { return GetButton(MouseButton.Left); } - internal set { SetButton(MouseButton.Left, value); } - } - - /// - /// Gets the of the right mouse button. - /// - public ButtonState RightButton - { - get { return GetButton(MouseButton.Right); } - internal set { SetButton(MouseButton.Right, value); } - } - - /// - /// Gets the of the middle mouse button. - /// - public ButtonState MiddleButton - { - get { return GetButton(MouseButton.Middle); } - internal set { SetButton(MouseButton.Middle, value); } - } - - /// - /// Gets the of the first extra mouse button. - /// - public ButtonState X1Button - { - get { return GetButton(MouseButton.Button1); } - internal set { SetButton(MouseButton.Button1, value); } - } - - /// - /// Gets the of the second extra mouse button. - /// - public ButtonState X2Button - { - get { return GetButton(MouseButton.Button2); } - internal set { SetButton(MouseButton.Button2, value); } - } - - /// - /// Gets a representing the location of the mouse for the event. - /// - public Point Position - { - get { return new Point(x, y); } - set - { - X = value.X; - Y = value.Y; - } - } - - #endregion - } - - /// - /// Defines the event data for events. - /// - /// - /// - /// Do not cache instances of this type outside their event handler. - /// If necessary, you can clone an instance using the - /// constructor. - /// - /// - public class MouseMoveEventArgs : MouseEventArgs - { - #region Fields - - int x_delta, y_delta; - - #endregion - - #region Constructors - - /// - /// Constructs a new instance. - /// - public MouseMoveEventArgs() { } - - /// - /// Constructs a new instance. - /// - /// The X position. - /// The Y position. - /// The change in X position produced by this event. - /// The change in Y position produced by this event. - public MouseMoveEventArgs(int x, int y, int xDelta, int yDelta) - : base(x, y) - { - XDelta = xDelta; - YDelta = yDelta; - } - - /// - /// Constructs a new instance. - /// - /// The instance to clone. - public MouseMoveEventArgs(MouseMoveEventArgs args) - : this(args.X, args.Y, args.XDelta, args.YDelta) - { - } - - #endregion - - #region Public Members - - /// - /// Gets the change in X position produced by this event. - /// - public int XDelta { get { return x_delta; } internal set { x_delta = value; } } - - /// - /// Gets the change in Y position produced by this event. - /// - public int YDelta { get { return y_delta; } internal set { y_delta = value; } } - - #endregion - } - - /// - /// Defines the event data for and events. - /// - /// - /// - /// Do not cache instances of this type outside their event handler. - /// If necessary, you can clone an instance using the - /// constructor. - /// - /// - public class MouseButtonEventArgs : MouseEventArgs - { - #region Fields - - MouseButton button; - bool pressed; - - #endregion - - #region Constructors - - /// - /// Constructs a new instance. - /// - public MouseButtonEventArgs() { } - - /// - /// Constructs a new instance. - /// - /// The X position. - /// The Y position. - /// The mouse button for the event. - /// The current state of the button. - public MouseButtonEventArgs(int x, int y, MouseButton button, bool pressed) - : base(x, y) - { - this.button = button; - this.pressed = pressed; - } - - /// - /// Constructs a new instance. - /// - /// The instance to clone. - public MouseButtonEventArgs(MouseButtonEventArgs args) - : this(args.X, args.Y, args.Button, args.IsPressed) - { - } - - #endregion - - #region Public Members - - /// - /// Gets the that triggered this event. - /// - public MouseButton Button { get { return button; } internal set { button = value; } } - - /// - /// Gets a System.Boolean representing the state of the mouse button for the event. - /// - public bool IsPressed - { - get { return GetButton(Button) == ButtonState.Pressed; } - internal set { SetButton(Button, value ? ButtonState.Pressed : ButtonState.Released); } - } - - #endregion - } - - /// - /// Defines the event data for events. - /// - /// - /// - /// Do not cache instances of this type outside their event handler. - /// If necessary, you can clone an instance using the - /// constructor. - /// - /// - public class MouseWheelEventArgs : MouseEventArgs - { - #region Fields - - float delta; - - #endregion - - #region Constructors - - /// - /// Constructs a new instance. - /// - public MouseWheelEventArgs() { } - - /// - /// Constructs a new instance. - /// - /// The X position. - /// The Y position. - /// The value of the wheel. - /// The change in value of the wheel for this event. - public MouseWheelEventArgs(int x, int y, int value, int delta) - : base(x, y) - { - Wheel.Y = value; - this.delta = delta; - } - - /// - /// Constructs a new instance. - /// - /// The instance to clone. - public MouseWheelEventArgs(MouseWheelEventArgs args) - : this(args.X, args.Y, args.Value, args.Delta) - { - } - - #endregion - - #region Public Members - - /// - /// Gets the value of the wheel in integer units. - /// To support high-precision mice, it is recommended to use instead. - /// - public int Value { get { return (int)Math.Round(Wheel.Y, MidpointRounding.AwayFromZero); } } - - /// - /// Gets the change in value of the wheel for this event in integer units. - /// To support high-precision mice, it is recommended to use instead. - /// - public int Delta { get { return (int)Math.Round(delta, MidpointRounding.AwayFromZero); } } - - /// - /// Gets the precise value of the wheel in floating-point units. - /// - public float ValuePrecise { get { return Wheel.Y; } internal set { Wheel.Y = value; } } - - /// - /// Gets the precise change in value of the wheel for this event in floating-point units. - /// - public float DeltaPrecise { get { return delta; } internal set { delta = value; } } - - #endregion - } - - #endregion } diff --git a/Source/OpenTK/Input/MouseEventArgs.cs b/Source/OpenTK/Input/MouseEventArgs.cs new file mode 100644 index 00000000..9770e43d --- /dev/null +++ b/Source/OpenTK/Input/MouseEventArgs.cs @@ -0,0 +1,370 @@ +#region License +// +// MouseEventArgs.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2014 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; +using System.Drawing; + +namespace OpenTK.Input +{ + /// + /// Defines the event data for events. + /// + /// + /// + /// Do not cache instances of this type outside their event handler. + /// If necessary, you can clone an instance using the + /// constructor. + /// + /// + public class MouseEventArgs : EventArgs + { + #region Fields + + MouseState state; + + #endregion + + #region Constructors + + /// + /// Constructs a new instance. + /// + public MouseEventArgs() + { + state.SetIsConnected(true); + } + + /// + /// Constructs a new instance. + /// + /// The X position. + /// The Y position. + public MouseEventArgs(int x, int y) + : this() + { + state.X = x; + state.Y = y; + } + + /// + /// Constructs a new instance. + /// + /// The instance to clone. + public MouseEventArgs(MouseEventArgs args) + : this(args.X, args.Y) + { + } + + #endregion + + #region Protected Members + + internal void SetButton(MouseButton button, ButtonState state) + { + if (button < 0 || button > MouseButton.LastButton) + throw new ArgumentOutOfRangeException(); + + switch (state) + { + case ButtonState.Pressed: + this.state.EnableBit((int)button); + break; + + case ButtonState.Released: + this.state.DisableBit((int)button); + break; + } + } + + internal ButtonState GetButton(MouseButton button) + { + if (button < 0 || button > MouseButton.LastButton) + throw new ArgumentOutOfRangeException(); + + return + state.ReadBit((int)button) ? + ButtonState.Pressed : ButtonState.Released; + } + + #endregion + + #region Public Members + + /// + /// Gets the X position of the mouse for the event. + /// + public int X { get { return state.X; } internal set { state.X = value; } } + + /// + /// Gets the Y position of the mouse for the event. + /// + public int Y { get { return state.Y; } internal set { state.Y = value; } } + + /// + /// Gets a representing the location of the mouse for the event. + /// + public Point Position + { + get { return new Point(state.X, state.Y); } + set + { + X = value.X; + Y = value.Y; + } + } + + /// + /// Gets the current . + /// + public MouseState Mouse + { + get { return state; } + internal set { state = value; } + } + + #endregion + } + + /// + /// Defines the event data for events. + /// + /// + /// + /// Do not cache instances of this type outside their event handler. + /// If necessary, you can clone an instance using the + /// constructor. + /// + /// + public class MouseMoveEventArgs : MouseEventArgs + { + #region Fields + + int x_delta, y_delta; + + #endregion + + #region Constructors + + /// + /// Constructs a new instance. + /// + public MouseMoveEventArgs() { } + + /// + /// Constructs a new instance. + /// + /// The X position. + /// The Y position. + /// The change in X position produced by this event. + /// The change in Y position produced by this event. + public MouseMoveEventArgs(int x, int y, int xDelta, int yDelta) + : base(x, y) + { + XDelta = xDelta; + YDelta = yDelta; + } + + /// + /// Constructs a new instance. + /// + /// The instance to clone. + public MouseMoveEventArgs(MouseMoveEventArgs args) + : this(args.X, args.Y, args.XDelta, args.YDelta) + { + } + + #endregion + + #region Public Members + + /// + /// Gets the change in X position produced by this event. + /// + public int XDelta { get { return x_delta; } internal set { x_delta = value; } } + + /// + /// Gets the change in Y position produced by this event. + /// + public int YDelta { get { return y_delta; } internal set { y_delta = value; } } + + #endregion + } + + /// + /// Defines the event data for and events. + /// + /// + /// + /// Do not cache instances of this type outside their event handler. + /// If necessary, you can clone an instance using the + /// constructor. + /// + /// + public class MouseButtonEventArgs : MouseEventArgs + { + #region Fields + + MouseButton button; + bool pressed; + + #endregion + + #region Constructors + + /// + /// Constructs a new instance. + /// + public MouseButtonEventArgs() { } + + /// + /// Constructs a new instance. + /// + /// The X position. + /// The Y position. + /// The mouse button for the event. + /// The current state of the button. + public MouseButtonEventArgs(int x, int y, MouseButton button, bool pressed) + : base(x, y) + { + this.button = button; + this.pressed = pressed; + } + + /// + /// Constructs a new instance. + /// + /// The instance to clone. + public MouseButtonEventArgs(MouseButtonEventArgs args) + : this(args.X, args.Y, args.Button, args.IsPressed) + { + } + + #endregion + + #region Public Members + + /// + /// Gets the that triggered this event. + /// + public MouseButton Button { get { return button; } internal set { button = value; } } + + /// + /// Gets a System.Boolean representing the state of the mouse button for the event. + /// + public bool IsPressed + { + get { return GetButton(Button) == ButtonState.Pressed; } + internal set { SetButton(Button, value ? ButtonState.Pressed : ButtonState.Released); } + } + + #endregion + } + + /// + /// Defines the event data for events. + /// + /// + /// + /// Do not cache instances of this type outside their event handler. + /// If necessary, you can clone an instance using the + /// constructor. + /// + /// + public class MouseWheelEventArgs : MouseEventArgs + { + #region Fields + + float delta; + + #endregion + + #region Constructors + + /// + /// Constructs a new instance. + /// + public MouseWheelEventArgs() { } + + /// + /// Constructs a new instance. + /// + /// The X position. + /// The Y position. + /// The value of the wheel. + /// The change in value of the wheel for this event. + public MouseWheelEventArgs(int x, int y, int value, int delta) + : base(x, y) + { + Mouse.SetScrollAbsolute(Mouse.Scroll.X, value); + this.delta = delta; + } + + /// + /// Constructs a new instance. + /// + /// The instance to clone. + public MouseWheelEventArgs(MouseWheelEventArgs args) + : this(args.X, args.Y, args.Value, args.Delta) + { + } + + #endregion + + #region Public Members + + /// + /// Gets the value of the wheel in integer units. + /// To support high-precision mice, it is recommended to use instead. + /// + public int Value { get { return (int)Math.Round(Mouse.Scroll.Y, MidpointRounding.AwayFromZero); } } + + /// + /// Gets the change in value of the wheel for this event in integer units. + /// To support high-precision mice, it is recommended to use instead. + /// + public int Delta { get { return (int)Math.Round(delta, MidpointRounding.AwayFromZero); } } + + /// + /// Gets the precise value of the wheel in floating-point units. + /// + public float ValuePrecise + { + get { return Mouse.Scroll.Y; } + internal set { Mouse.SetScrollAbsolute(Mouse.Scroll.X, value); } + } + + /// + /// Gets the precise change in value of the wheel for this event in floating-point units. + /// + public float DeltaPrecise { get { return delta; } internal set { delta = value; } } + + #endregion + } +} + diff --git a/Source/OpenTK/Input/MouseScrollWheel.cs b/Source/OpenTK/Input/MouseScrollWheel.cs new file mode 100644 index 00000000..69fd17f3 --- /dev/null +++ b/Source/OpenTK/Input/MouseScrollWheel.cs @@ -0,0 +1,119 @@ +#region License +// +// MouseWheel.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2014 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; + +namespace OpenTK.Input +{ + /// + /// Represents the state of a mouse wheel. + /// + public struct MouseScrollWheel : IEquatable + { + #region Public Members + + /// + /// Gets the absolute horizontal offset of the wheel, + /// or 0 if no horizontal scroll wheel exists. + /// + /// The x. + public float X { get; internal set; } + + /// + /// Gets the absolute vertical offset of the wheel, + /// or 0 if no vertical scroll wheel exists. + /// + /// The y. + public float Y { get; internal set; } + + /// A instance to test for equality. + /// A instance to test for equality. + public static bool operator ==(MouseScrollWheel left, MouseScrollWheel right) + { + return left.Equals(right); + } + + /// A instance to test for inequality. + /// A instance to test for inequality. + public static bool operator !=(MouseScrollWheel left, MouseScrollWheel right) + { + return !left.Equals(right); + } + + /// + /// Returns a that represents the current . + /// + /// A that represents the current . + public override string ToString() + { + return string.Format("[MouseScrollWheel: X={0}, Y={1}]", X, Y); + } + + /// + /// Serves as a hash function for a object. + /// + /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a + /// hash table. + public override int GetHashCode() + { + return X.GetHashCode() ^ Y.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. + public override bool Equals(object obj) + { + return + obj is MouseScrollWheel && + Equals((MouseScrollWheel)obj); + } + + #endregion + + #region IEquatable Members + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. + public bool Equals(MouseScrollWheel other) + { + return X == other.X && Y == other.Y; + } + + #endregion + } +} + diff --git a/Source/OpenTK/Input/MouseState.cs b/Source/OpenTK/Input/MouseState.cs index 7fa48a8f..5b12fe2d 100644 --- a/Source/OpenTK/Input/MouseState.cs +++ b/Source/OpenTK/Input/MouseState.cs @@ -38,13 +38,9 @@ namespace OpenTK.Input { #region Fields - // Allocate enough ints to store all mouse buttons - const int IntSize = sizeof(int); - const int NumInts = ((int)MouseButton.LastButton + IntSize - 1) / IntSize; - // The following line triggers bogus CS0214 in gmcs 2.0.1, sigh... - unsafe fixed int Buttons[NumInts]; int x, y; - float wheel; + MouseScrollWheel scroll; + ushort buttons; bool is_connected; #endregion @@ -93,7 +89,7 @@ namespace OpenTK.Input /// public int Wheel { - get { return (int)Math.Round(wheel, MidpointRounding.AwayFromZero); } + get { return (int)Math.Round(scroll.Y, MidpointRounding.AwayFromZero); } } /// @@ -101,11 +97,16 @@ namespace OpenTK.Input /// public float WheelPrecise { - get { return wheel; } - internal set - { - wheel = value; - } + get { return scroll.Y; } + } + + /// + /// Gets a instance, + /// representing the current state of the mouse scroll wheel. + /// + public MouseScrollWheel Scroll + { + get { return scroll; } } /// @@ -253,13 +254,7 @@ namespace OpenTK.Input /// public override int GetHashCode() { - unsafe - { - fixed (int* b = Buttons) - { - return b->GetHashCode() ^ X.GetHashCode() ^ Y.GetHashCode() ^ WheelPrecise.GetHashCode(); - } - } + return buttons.GetHashCode() ^ X.GetHashCode() ^ Y.GetHashCode() ^ scroll.GetHashCode(); } #endregion @@ -269,60 +264,27 @@ namespace OpenTK.Input internal bool ReadBit(int offset) { ValidateOffset(offset); - - int int_offset = offset / 32; - int bit_offset = offset % 32; - unsafe - { - fixed (int* b = Buttons) - { - return (*(b + int_offset) & (1 << bit_offset)) != 0u; - } - } + return (buttons & (1 << offset)) != 0; } internal void EnableBit(int offset) { ValidateOffset(offset); - - int int_offset = offset / 32; - int bit_offset = offset % 32; - unsafe - { - fixed (int* b = Buttons) - { - *(b + int_offset) |= 1 << bit_offset; - } - } + buttons |= unchecked((ushort)(1 << offset)); } internal void DisableBit(int offset) { ValidateOffset(offset); - - int int_offset = offset / 32; - int bit_offset = offset % 32; - unsafe - { - fixed (int* b = Buttons) - { - *(b + int_offset) &= ~(1 << bit_offset); - } - } + buttons &= unchecked((ushort)(~(1 << offset))); } internal void MergeBits(MouseState other) { unsafe { - int* b2 = other.Buttons; - fixed (int* b1 = Buttons) - { - for (int i = 0; i < NumInts; i++) - *(b1 + i) |= *(b2 + i); - } - - WheelPrecise += other.WheelPrecise; + buttons |= other.buttons; + SetScrollRelative(other.scroll.X, other.scroll.Y); X += other.X; Y += other.Y; IsConnected |= other.IsConnected; @@ -334,13 +296,29 @@ namespace OpenTK.Input IsConnected = value; } + #region Internal Members + + internal void SetScrollAbsolute(float x, float y) + { + scroll.X = x; + scroll.Y = y; + } + + internal void SetScrollRelative(float x, float y) + { + scroll.X += x; + scroll.Y += y; + } + + #endregion + #endregion #region Private Members static void ValidateOffset(int offset) { - if (offset < 0 || offset >= NumInts * IntSize) + if (offset < 0 || offset >= 16) throw new ArgumentOutOfRangeException("offset"); } @@ -355,18 +333,11 @@ namespace OpenTK.Input /// True, if both instances are equal; false otherwise. public bool Equals(MouseState other) { - bool equal = true; - unsafe - { - int* b2 = other.Buttons; - fixed (int* b1 = Buttons) - { - for (int i = 0; equal && i < NumInts; i++) - equal &= *(b1 + i) == *(b2 + i); - } - equal &= X == other.X && Y == other.Y && WheelPrecise == other.WheelPrecise; - } - return equal; + return + buttons == other.buttons && + X == other.X && + Y == other.Y && + Scroll == other.Scroll; } #endregion diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index 7d2cbda8..f82f6998 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -798,6 +798,8 @@ + + diff --git a/Source/OpenTK/Platform/LegacyInputDriver.cs b/Source/OpenTK/Platform/LegacyInputDriver.cs index 96d0a02a..96596e54 100644 --- a/Source/OpenTK/Platform/LegacyInputDriver.cs +++ b/Source/OpenTK/Platform/LegacyInputDriver.cs @@ -78,7 +78,7 @@ namespace OpenTK.Platform window.MouseWheel += (sender, e) => { - mouse.WheelPrecise = e.Wheel.Y; + mouse.WheelPrecise = e.Mouse.Scroll.Y; }; // Hook keyboard events diff --git a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs index 2d95a330..e6324157 100644 --- a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs +++ b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs @@ -517,7 +517,9 @@ namespace OpenTK.Platform.MacOS MathHelper.Clamp((int)Math.Round(p.Y + dy), 0, Height)); } - InputDriver.Mouse[0].Position = p; + MouseState.X = p.X; + MouseState.Y = p.Y; + OnMouseMove(); } break; @@ -534,7 +536,8 @@ namespace OpenTK.Platform.MacOS factor = 1.0f / scrollFactor; // Problem: Don't know what factor to use here, but this seems to work. } - InputDriver.Mouse[0].WheelPrecise += scrollingDelta * factor; + MouseState.SetScrollRelative(0, scrollingDelta * factor); + OnMouseWheel(); } break; @@ -543,7 +546,8 @@ namespace OpenTK.Platform.MacOS case NSEventType.OtherMouseDown: { var buttonNumber = Cocoa.SendInt(e, selButtonNumber); - InputDriver.Mouse[0][GetMouseButton(buttonNumber)] = true; + MouseState[GetMouseButton(buttonNumber)] = true; + OnMouseDown(); } break; @@ -552,7 +556,8 @@ namespace OpenTK.Platform.MacOS case NSEventType.OtherMouseUp: { var buttonNumber = Cocoa.SendInt(e, selButtonNumber); - InputDriver.Mouse[0][GetMouseButton(buttonNumber)] = false; + MouseState[GetMouseButton(buttonNumber)] = false; + OnMouseUp(); } break; } diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index 49103d56..64a185ce 100755 --- a/Source/OpenTK/Platform/MacOS/HIDInput.cs +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -324,7 +324,7 @@ namespace OpenTK.Platform.MacOS break; case HIDUsageGD.Wheel: - mouse.State.WheelPrecise += v_int; + mouse.State.SetScrollRelative(0, v_int); break; } break; @@ -332,6 +332,15 @@ namespace OpenTK.Platform.MacOS case HIDPage.Button: mouse.State[OpenTK.Input.MouseButton.Left + usage - 1] = v_int == 1; break; + + case HIDPage.Consumer: + switch ((HIDUsageCD)usage) + { + case HIDUsageCD.ACPan: + mouse.State.SetScrollRelative(v_int, 0); + break; + } + break; } } @@ -1107,6 +1116,12 @@ namespace OpenTK.Platform.MacOS VendorDefinedStart = 0xFF00 } + // Consumer electronic devices + enum HIDUsageCD + { + ACPan = 0x0238 + } + // Generic desktop usage enum HIDUsageGD { diff --git a/Source/OpenTK/Platform/NativeWindowBase.cs b/Source/OpenTK/Platform/NativeWindowBase.cs index 64e6bee0..da7e7b08 100644 --- a/Source/OpenTK/Platform/NativeWindowBase.cs +++ b/Source/OpenTK/Platform/NativeWindowBase.cs @@ -40,22 +40,25 @@ namespace OpenTK.Platform { readonly LegacyInputDriver LegacyInputDriver; - readonly protected MouseButtonEventArgs MouseDownArgs = new MouseButtonEventArgs(); - readonly protected MouseButtonEventArgs MouseUpArgs = new MouseButtonEventArgs(); - readonly protected MouseMoveEventArgs MouseMoveArgs = new MouseMoveEventArgs(); - readonly protected MouseWheelEventArgs MouseWheelArgs = new MouseWheelEventArgs(); + readonly MouseButtonEventArgs MouseDownArgs = new MouseButtonEventArgs(); + readonly MouseButtonEventArgs MouseUpArgs = new MouseButtonEventArgs(); + readonly MouseMoveEventArgs MouseMoveArgs = new MouseMoveEventArgs(); + readonly MouseWheelEventArgs MouseWheelArgs = new MouseWheelEventArgs(); - readonly protected KeyboardKeyEventArgs KeyDownArgs = new KeyboardKeyEventArgs(); - readonly protected KeyboardKeyEventArgs KeyUpArgs = new KeyboardKeyEventArgs(); - readonly protected KeyPressEventArgs KeyPressArgs = new KeyPressEventArgs((char)0); + protected readonly KeyboardKeyEventArgs KeyDownArgs = new KeyboardKeyEventArgs(); + protected readonly KeyboardKeyEventArgs KeyUpArgs = new KeyboardKeyEventArgs(); + protected readonly KeyPressEventArgs KeyPressArgs = new KeyPressEventArgs((char)0); // In order to simplify mouse event implementation, // we can store the current mouse state here. protected MouseState MouseState = new MouseState(); + MouseState PreviousMouseState = new MouseState(); internal NativeWindowBase() { LegacyInputDriver = new LegacyInputDriver(this); + MouseState.SetIsConnected(true); + PreviousMouseState.SetIsConnected(true); } #region Protected Members @@ -140,23 +143,75 @@ namespace OpenTK.Platform MouseEnter(this, e); } - protected void OnMouseDown(MouseButtonEventArgs e) + protected void OnMouseDown() { - MouseDown(this, e); + var e = MouseDownArgs; + e.Mouse = MouseState; + + // Find which button caused this event + for (MouseButton b = MouseButton.Left; b < MouseButton.LastButton; b++) + { + if (!PreviousMouseState[b] && MouseState[b]) + { + e.Button = b; + PreviousMouseState = MouseState; + MouseDown(this, e); + return; + } + } + + Debug.WriteLine("OnMouseDown called without pressing a button"); } - protected void OnMouseUp(MouseButtonEventArgs e) + protected void OnMouseUp() { - MouseUp(this, e); + var e = MouseUpArgs; + e.Mouse = MouseState; + + // Find which button caused this event + for (MouseButton b = MouseButton.Left; b < MouseButton.LastButton; b++) + { + if (PreviousMouseState[b] && !MouseState[b]) + { + e.Button = b; + PreviousMouseState = MouseState; + MouseUp(this, e); + return; + } + } + + Debug.WriteLine("OnMouseUp called without pressing a button"); } - protected void OnMouseMove(MouseMoveEventArgs e) + protected void OnMouseMove() { + var e = MouseMoveArgs; + e.Mouse = MouseState; + e.XDelta = MouseState.X - PreviousMouseState.X; + e.YDelta = MouseState.Y - PreviousMouseState.Y; + + if (e.XDelta == 0 && e.YDelta == 0) + { + Debug.WriteLine("OnMouseMove called without moving the mouse"); + } + + PreviousMouseState = MouseState; MouseMove(this, e); } - protected void OnMouseWheel(MouseWheelEventArgs e) + protected void OnMouseWheel() { + var e = MouseWheelArgs; + e.Mouse = MouseState; + e.ValuePrecise = MouseState.Scroll.Y; + e.DeltaPrecise = MouseState.Scroll.Y - PreviousMouseState.Scroll.Y; + + if (e.DeltaPrecise == 0) + { + Debug.WriteLine("OnMouseWheel called without moving the mouse wheel."); + } + + PreviousMouseState = MouseState; MouseWheel(this, e); } diff --git a/Source/OpenTK/Platform/SDL2/Sdl2Mouse.cs b/Source/OpenTK/Platform/SDL2/Sdl2Mouse.cs index 887ad7af..934096ef 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2Mouse.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2Mouse.cs @@ -87,7 +87,7 @@ namespace OpenTK.Platform.SDL2 public void ProcessWheelEvent(MouseWheelEvent wheel) { - state.WheelPrecise += wheel.Y; + state.SetScrollRelative(0, wheel.Y); } public void ProcessMouseEvent(MouseMotionEvent motion) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs b/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs index 8c3367fd..1283e4c1 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs @@ -221,21 +221,17 @@ namespace OpenTK.Platform.SDL2 button_pressed ? true : false); } - var e = button_pressed ? window.MouseDownArgs : window.MouseUpArgs; - e.Button = Sdl2Mouse.TranslateButton(ev.Button); - e.IsPressed = button_pressed; - e.X = ev.X; - e.Y = ev.Y; - e.Wheel.X = window.MouseWheelArgs.Wheel.X; - e.Wheel.Y = window.MouseWheelArgs.Wheel.Y; + window.MouseState[Sdl2Mouse.TranslateButton(ev.Button)] = button_pressed; + window.MouseState.X = ev.X; + window.MouseState.Y = ev.Y; if (button_pressed) { - window.OnMouseDown(e); + window.OnMouseDown(); } else { - window.OnMouseUp(e); + window.OnMouseUp(); } } @@ -293,29 +289,15 @@ namespace OpenTK.Platform.SDL2 static void ProcessMouseMotionEvent(Sdl2NativeWindow window, MouseMotionEvent ev) { //float scale = window.ClientSize.Width / (float)window.Size.Width; - var e = window.MouseMoveArgs; - e.X = ev.X; - e.Y = ev.Y; - SetMouseButtons(e, ev.State); - window.OnMouseMove(e); - } - - static void SetMouseButtons(MouseEventArgs e, ButtonFlags buttons) - { - for (int i = 0; i < 5; i++) - { - // Note: OpenTK MouseButton is identical to SDL2 Button - bool pressed = ((int)buttons & (1 << i)) != 0; - e.SetButton((MouseButton)i, pressed ? ButtonState.Pressed : ButtonState.Released); - } + window.MouseState.X = ev.X; + window.MouseState.Y = ev.Y; + window.OnMouseMove(); } static void ProcessMouseWheelEvent(Sdl2NativeWindow window, MouseWheelEvent ev) { - var e = window.MouseWheelArgs; - e.Wheel.Y = ev.Y; - e.Wheel.X = ev.X; - window.OnMouseWheel(e); + window.MouseState.SetScrollRelative(ev.X, ev.Y); + window.OnMouseWheel(); } static void ProcessWindowEvent(Sdl2NativeWindow window, WindowEvent e) diff --git a/Source/OpenTK/Platform/Windows/API.cs b/Source/OpenTK/Platform/Windows/API.cs index 3a0fd548..167dcb5c 100644 --- a/Source/OpenTK/Platform/Windows/API.cs +++ b/Source/OpenTK/Platform/Windows/API.cs @@ -3591,7 +3591,8 @@ namespace OpenTK.Platform.Windows BUTTON_5_DOWN = 0x0100, BUTTON_5_UP = 0x0200, - WHEEL = 0x0400 + WHEEL = 0x0400, + HWHEEL = 0x0800, } #endregion diff --git a/Source/OpenTK/Platform/Windows/WinGLNative.cs b/Source/OpenTK/Platform/Windows/WinGLNative.cs index da560894..fdf11665 100644 --- a/Source/OpenTK/Platform/Windows/WinGLNative.cs +++ b/Source/OpenTK/Platform/Windows/WinGLNative.cs @@ -437,8 +437,9 @@ namespace OpenTK.Platform.Windows if (points == 0 || (points == -1 && lastError == Constants.ERROR_POINT_NOT_FOUND)) { // Just use the mouse move position - MouseMoveArgs.Position = point; - OnMouseMove(MouseMoveArgs); + MouseState.X = point.X; + MouseState.Y = point.Y; + OnMouseMove(); } else if (points == -1) { @@ -476,8 +477,9 @@ namespace OpenTK.Platform.Windows position.Y -= 65536; } Functions.ScreenToClient(handle, ref position); - MouseMoveArgs.Position = position; - OnMouseMove(MouseMoveArgs); + MouseState.X = position.X; + MouseState.Y = position.Y; + OnMouseMove(); } } mouse_last_timestamp = timestamp; @@ -506,40 +508,37 @@ namespace OpenTK.Platform.Windows { // This is due to inconsistent behavior of the WParam value on 64bit arch, whese // wparam = 0xffffffffff880000 or wparam = 0x00000000ff100000 - MouseWheelArgs.Wheel.Y += ((long)wParam << 32 >> 48) / 120.0f; - OnMouseWheel(MouseWheelArgs); + MouseState.SetScrollRelative(0, ((long)wParam << 32 >> 48) / 120.0f); + OnMouseWheel(); } void HandleMouseHWheel(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { // This is due to inconsistent behavior of the WParam value on 64bit arch, whese // wparam = 0xffffffffff880000 or wparam = 0x00000000ff100000 - MouseWheelArgs.Wheel.X += ((long)wParam << 32 >> 48) / 120.0f; - OnMouseWheel(MouseWheelArgs); + MouseState.SetScrollRelative(((long)wParam << 32 >> 48) / 120.0f, 0); + OnMouseWheel(); } void HandleLButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { Functions.SetCapture(window.Handle); - MouseDownArgs.Button = MouseButton.Left; - MouseDownArgs.IsPressed = true; - OnMouseDown(MouseDownArgs); + MouseState[MouseButton.Left] = true; + OnMouseDown(); } void HandleMButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { Functions.SetCapture(window.Handle); - MouseDownArgs.Button = MouseButton.Middle; - MouseDownArgs.IsPressed = true; - OnMouseDown(MouseDownArgs); + MouseState[MouseButton.Middle] = true; + OnMouseDown(); } void HandleRButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { Functions.SetCapture(window.Handle); - MouseDownArgs.Button = MouseButton.Right; - MouseDownArgs.IsPressed = true; - OnMouseDown(MouseDownArgs); + MouseState[MouseButton.Right] = true; + OnMouseDown(); } void HandleXButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) @@ -548,33 +547,29 @@ namespace OpenTK.Platform.Windows MouseButton button = ((wParam.ToInt32() & 0xFFFF0000) >> 16) == 1 ? MouseButton.Button1 : MouseButton.Button2; - MouseDownArgs.Button = button; - MouseDownArgs.IsPressed = true; - OnMouseDown(MouseDownArgs); + MouseState[button] = true; + OnMouseDown(); } void HandleLButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { Functions.ReleaseCapture(); - MouseDownArgs.Button = MouseButton.Left; - MouseDownArgs.IsPressed = false; - OnMouseUp(MouseUpArgs); + MouseState[MouseButton.Left] = false; + OnMouseUp(); } void HandleMButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { Functions.ReleaseCapture(); - MouseDownArgs.Button = MouseButton.Middle; - MouseDownArgs.IsPressed = false; - OnMouseUp(MouseUpArgs); + MouseState[MouseButton.Middle] = false; + OnMouseUp(); } void HandleRButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { Functions.ReleaseCapture(); - MouseDownArgs.Button = MouseButton.Right; - MouseDownArgs.IsPressed = false; - OnMouseUp(MouseUpArgs); + MouseState[MouseButton.Right] = false; + OnMouseUp(); } void HandleXButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) @@ -583,9 +578,8 @@ namespace OpenTK.Platform.Windows MouseButton button = ((wParam.ToInt32() & 0xFFFF0000) >> 16) == 1 ? MouseButton.Button1 : MouseButton.Button2; - MouseDownArgs.Button = button; - MouseDownArgs.IsPressed = false; - OnMouseUp(MouseUpArgs); + MouseState[button] = false; + OnMouseUp(); } void HandleKeyboard(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) diff --git a/Source/OpenTK/Platform/Windows/WinRawMouse.cs b/Source/OpenTK/Platform/Windows/WinRawMouse.cs index 15ae0430..c904ea1f 100644 --- a/Source/OpenTK/Platform/Windows/WinRawMouse.cs +++ b/Source/OpenTK/Platform/Windows/WinRawMouse.cs @@ -227,7 +227,10 @@ namespace OpenTK.Platform.Windows } if ((raw.ButtonFlags & RawInputMouseState.WHEEL) != 0) - mouse.WheelPrecise += (short)raw.ButtonData / 120.0f; + mouse.SetScrollRelative(0, (short)raw.ButtonData / 120.0f); + + if ((raw.ButtonFlags & RawInputMouseState.HWHEEL) != 0) + mouse.SetScrollRelative((short)raw.ButtonData / 120.0f, 0); if ((raw.Flags & RawMouseFlags.MOUSE_MOVE_ABSOLUTE) != 0) { diff --git a/Source/OpenTK/Platform/X11/XI2Mouse.cs b/Source/OpenTK/Platform/X11/XI2Mouse.cs index bb93c754..96090e55 100644 --- a/Source/OpenTK/Platform/X11/XI2Mouse.cs +++ b/Source/OpenTK/Platform/X11/XI2Mouse.cs @@ -204,8 +204,8 @@ namespace OpenTK.Platform.X11 case 1: state.EnableBit((int)MouseButton.Left); break; case 2: state.EnableBit((int)MouseButton.Middle); break; case 3: state.EnableBit((int)MouseButton.Right); break; - case 4: state.WheelPrecise++; break; - case 5: state.WheelPrecise--; break; + case 4: state.SetScrollRelative(0, 1); break; + case 5: state.SetScrollRelative(0, -1); break; case 6: state.EnableBit((int)MouseButton.Button1); break; case 7: state.EnableBit((int)MouseButton.Button2); break; case 8: state.EnableBit((int)MouseButton.Button3); break; From 39eb3b189242945e505a4036f9a9109a2755bbcf Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 4 May 2014 08:31:11 +0200 Subject: [PATCH 14/51] [Input] Improved MouseState.ToString() --- Source/OpenTK/Input/MouseScrollWheel.cs | 2 +- Source/OpenTK/Input/MouseState.cs | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Source/OpenTK/Input/MouseScrollWheel.cs b/Source/OpenTK/Input/MouseScrollWheel.cs index 69fd17f3..2be06b67 100644 --- a/Source/OpenTK/Input/MouseScrollWheel.cs +++ b/Source/OpenTK/Input/MouseScrollWheel.cs @@ -72,7 +72,7 @@ namespace OpenTK.Input /// A that represents the current . public override string ToString() { - return string.Format("[MouseScrollWheel: X={0}, Y={1}]", X, Y); + return string.Format("[X={0:0.00}, Y={1:0.00}]", X, Y); } /// diff --git a/Source/OpenTK/Input/MouseState.cs b/Source/OpenTK/Input/MouseState.cs index 5b12fe2d..557aa1c1 100644 --- a/Source/OpenTK/Input/MouseState.cs +++ b/Source/OpenTK/Input/MouseState.cs @@ -257,6 +257,17 @@ namespace OpenTK.Input return buttons.GetHashCode() ^ X.GetHashCode() ^ Y.GetHashCode() ^ scroll.GetHashCode(); } + /// + /// Returns a that represents the current . + /// + /// A that represents the current . + public override string ToString() + { + string b = Convert.ToString(buttons, 2).PadLeft(10, '0'); + return String.Format("[MouseState: X={0}, Y={1}, Scroll={2}, Buttons={3}, IsConnected={4}]", + X, Y, Scroll, b, IsConnected); + } + #endregion #region Internal Members From ff46455e562b09c7698f2f55e33c21b2c5090eb8 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 4 May 2014 08:32:08 +0200 Subject: [PATCH 15/51] [X11] Removed legacy X11Input driver Its functionality has been moved directly into X11GLNative and X11KeyMap. --- Source/OpenTK/OpenTK.csproj | 3 - Source/OpenTK/Platform/X11/X11GLNative.cs | 107 ++++++---- Source/OpenTK/Platform/X11/X11Input.cs | 235 ---------------------- Source/OpenTK/Platform/X11/X11KeyMap.cs | 17 ++ 4 files changed, 81 insertions(+), 281 deletions(-) delete mode 100644 Source/OpenTK/Platform/X11/X11Input.cs diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index f82f6998..2d033d82 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -366,9 +366,6 @@ Code - - Code - Code diff --git a/Source/OpenTK/Platform/X11/X11GLNative.cs b/Source/OpenTK/Platform/X11/X11GLNative.cs index 4109da30..f52ea305 100644 --- a/Source/OpenTK/Platform/X11/X11GLNative.cs +++ b/Source/OpenTK/Platform/X11/X11GLNative.cs @@ -57,11 +57,6 @@ namespace OpenTK.Platform.X11 X11WindowInfo window = new X11WindowInfo(); - // Legacy input support - X11Input driver; - KeyboardDevice keyboard; - MouseDevice mouse; - // Window manager hints for fullscreen windows. // Not used right now (the code is written, but is not 64bit-correct), but could be useful for older WMs which // are not ICCM compliant, but may support MOTIF hints. @@ -124,9 +119,6 @@ namespace OpenTK.Platform.X11 // Keyboard input readonly byte[] ascii = new byte[16]; readonly char[] chars = new char[16]; - readonly KeyPressEventArgs KPEventArgs = new KeyPressEventArgs('\0'); - readonly KeyboardKeyEventArgs KeyDownEventArgs = new KeyboardKeyEventArgs(); - readonly KeyboardKeyEventArgs KeyUpEventArgs = new KeyboardKeyEventArgs(); readonly IntPtr EmptyCursor; @@ -224,15 +216,18 @@ namespace OpenTK.Platform.X11 e.ConfigureEvent.height = height; RefreshWindowBounds(ref e); - driver = new X11Input(window); - keyboard = driver.Keyboard[0]; - mouse = driver.Mouse[0]; - EmptyCursor = CreateEmptyCursor(window); Debug.WriteLine(String.Format("X11GLNative window created successfully (id: {0}).", Handle)); Debug.Unindent(); + // Request that auto-repeat is only set on devices that support it physically. + // This typically means that it's turned off for keyboards (which is what we want). + // We prefer this method over XAutoRepeatOff/On, because the latter needs to + // be reset before the program exits. + bool supported; + Functions.XkbSetDetectableAutoRepeat(window.Display, true, out supported); + exists = true; } @@ -768,7 +763,7 @@ namespace OpenTK.Platform.X11 return cursor; } - static void SetMouseClamped(MouseDevice mouse, int x, int y, + void SetMouseClamped(int x, int y, int left, int top, int width, int height) { // Clamp mouse to the specified rectangle. @@ -776,7 +771,8 @@ namespace OpenTK.Platform.X11 x = Math.Min(x, width); y = Math.Max(y, top); y = Math.Min(y, height); - mouse.Position = new Point(x, y); + MouseState.X = x; + MouseState.Y = y; } #endregion @@ -860,23 +856,23 @@ namespace OpenTK.Platform.X11 case XEventName.KeyRelease: bool pressed = e.type == XEventName.KeyPress; Key key; - if (driver.TranslateKey(ref e.KeyEvent, out key)) + if (X11KeyMap.TranslateKey(ref e.KeyEvent, out key)) { if (pressed) { // Raise KeyDown event - KeyDownEventArgs.Key = key; - KeyDownEventArgs.ScanCode = (uint)e.KeyEvent.keycode; - KeyDownEventArgs.Modifiers = keyboard.GetModifiers(); - KeyDown(this, KeyDownEventArgs); + KeyDownArgs.Key = key; + KeyDownArgs.ScanCode = (uint)e.KeyEvent.keycode; + //KeyDownArgs.Modifiers = keyboard.GetModifiers(); + OnKeyDown(KeyDownArgs); } else { // Raise KeyUp event - KeyUpEventArgs.Key = key; - KeyUpEventArgs.ScanCode = (uint)e.KeyEvent.keycode; - KeyUpEventArgs.Modifiers = keyboard.GetModifiers(); - KeyUp(this, KeyUpEventArgs); + KeyUpArgs.Key = key; + KeyUpArgs.ScanCode = (uint)e.KeyEvent.keycode; + //KeyUpArgs.Modifiers = keyboard.GetModifiers(); + OnKeyUp(KeyUpArgs); } if (pressed) @@ -892,8 +888,8 @@ namespace OpenTK.Platform.X11 { if (!Char.IsControl(chars[i])) { - KPEventArgs.KeyChar = chars[i]; - KeyPress(this, KPEventArgs); + KeyPressArgs.KeyChar = chars[i]; + OnKeyPress(KeyPressArgs); } } } @@ -907,7 +903,7 @@ namespace OpenTK.Platform.X11 // to the dead center of the window. Fortunately, this situation // is very very uncommon. Todo: Can this be remedied? int x = e.MotionEvent.x; - int y =e.MotionEvent.y; + int y = e.MotionEvent.y; // TODO: Have offset as a stored field, only update it when the window moves // The middle point cannot be the average of the Bounds.left/right/top/bottom, // because these fields take into account window decoration (borders, etc), @@ -926,9 +922,9 @@ namespace OpenTK.Platform.X11 } else if (!CursorVisible) { - SetMouseClamped(mouse, - mouse.X + x - mouse_rel_x, - mouse.Y + y - mouse_rel_y, + SetMouseClamped( + MouseState.X + x - mouse_rel_x, + MouseState.Y + y - mouse_rel_y, 0, 0, Width, Height); mouse_rel_x = x; mouse_rel_y = y; @@ -939,16 +935,53 @@ namespace OpenTK.Platform.X11 } else { - SetMouseClamped(mouse, x, y, 0, 0, Width, Height); + SetMouseClamped(x, y, 0, 0, Width, Height); mouse_rel_x = x; mouse_rel_y = y; } + + OnMouseMove(); break; } case XEventName.ButtonPress: + switch (e.ButtonEvent.button) + { + case 1: MouseState.EnableBit((int)MouseButton.Left); break; + case 2: MouseState.EnableBit((int)MouseButton.Middle); break; + case 3: MouseState.EnableBit((int)MouseButton.Right); break; + case 4: MouseState.SetScrollRelative(0, 1); break; + case 5: MouseState.SetScrollRelative(0, -1); break; + case 6: MouseState.EnableBit((int)MouseButton.Button1); break; + case 7: MouseState.EnableBit((int)MouseButton.Button2); break; + case 8: MouseState.EnableBit((int)MouseButton.Button3); break; + case 9: MouseState.EnableBit((int)MouseButton.Button4); break; + case 10: MouseState.EnableBit((int)MouseButton.Button5); break; + case 11: MouseState.EnableBit((int)MouseButton.Button6); break; + case 12: MouseState.EnableBit((int)MouseButton.Button7); break; + case 13: MouseState.EnableBit((int)MouseButton.Button8); break; + case 14: MouseState.EnableBit((int)MouseButton.Button9); break; + } + OnMouseDown(); + break; + case XEventName.ButtonRelease: - driver.ProcessEvent(ref e); + switch (e.ButtonEvent.button) + { + case 1: MouseState.DisableBit((int)MouseButton.Left); break; + case 2: MouseState.DisableBit((int)MouseButton.Middle); break; + case 3: MouseState.DisableBit((int)MouseButton.Right); break; + case 6: MouseState.DisableBit((int)MouseButton.Button1); break; + case 7: MouseState.DisableBit((int)MouseButton.Button2); break; + case 8: MouseState.DisableBit((int)MouseButton.Button3); break; + case 9: MouseState.DisableBit((int)MouseButton.Button4); break; + case 10: MouseState.DisableBit((int)MouseButton.Button5); break; + case 11: MouseState.DisableBit((int)MouseButton.Button6); break; + case 12: MouseState.DisableBit((int)MouseButton.Button7); break; + case 13: MouseState.DisableBit((int)MouseButton.Button8); break; + case 14: MouseState.DisableBit((int)MouseButton.Button9); break; + } + OnMouseUp(); break; case XEventName.FocusIn: @@ -1532,18 +1565,6 @@ namespace OpenTK.Platform.X11 #region --- INativeGLWindow Members --- - #region public IInputDriver InputDriver - - public IInputDriver InputDriver - { - get - { - return driver; - } - } - - #endregion - #region public bool Exists /// diff --git a/Source/OpenTK/Platform/X11/X11Input.cs b/Source/OpenTK/Platform/X11/X11Input.cs deleted file mode 100644 index 2a2371a3..00000000 --- a/Source/OpenTK/Platform/X11/X11Input.cs +++ /dev/null @@ -1,235 +0,0 @@ -#region --- License --- -/* Copyright (c) 2006, 2007 Stefanos Apostolopoulos - * See license.txt for license info - */ -#endregion - -using System; -using System.Collections.Generic; -#if !MINIMAL -using System.Drawing; -#endif -using System.Text; -using System.Diagnostics; -using System.Runtime.InteropServices; - -using OpenTK.Input; - -namespace OpenTK.Platform.X11 -{ - /// \internal - /// - /// Drives the InputDriver on X11. - /// This class supports OpenTK, and is not intended for users of OpenTK. - /// - internal sealed class X11Input : IInputDriver - { - KeyboardDevice keyboard = new KeyboardDevice(); - MouseDevice mouse = new MouseDevice(); - List dummy_keyboard_list = new List(1); - List dummy_mice_list = new List(1); - - int firstKeyCode, lastKeyCode; // The smallest and largest KeyCode supported by the X server. - int keysyms_per_keycode; // The number of KeySyms for each KeyCode. - IntPtr[] keysyms; - - //bool disposed; - - #region --- Constructors --- - - /// - /// Constructs a new X11Input driver. Creates a hidden InputOnly window, child to - /// the main application window, which selects input events and routes them to - /// the device specific drivers (Keyboard, Mouse, Hid). - /// - /// The window which the InputDriver will attach itself on. - public X11Input(IWindowInfo attach) - { - Debug.WriteLine("Initalizing X11 input driver."); - Debug.Indent(); - - if (attach == null) - throw new ArgumentException("A valid parent window must be defined, in order to create an X11Input driver."); - - //window = new X11WindowInfo(attach); - X11WindowInfo window = (X11WindowInfo)attach; - - // Init mouse - mouse.Description = "Default X11 mouse"; - mouse.DeviceID = IntPtr.Zero; - mouse.NumberOfButtons = 5; - mouse.NumberOfWheels = 1; - dummy_mice_list.Add(mouse); - - using (new XLock(window.Display)) - { - // Init keyboard - API.DisplayKeycodes(window.Display, ref firstKeyCode, ref lastKeyCode); - Debug.Print("First keycode: {0}, last {1}", firstKeyCode, lastKeyCode); - - IntPtr keysym_ptr = API.GetKeyboardMapping(window.Display, (byte)firstKeyCode, - lastKeyCode - firstKeyCode + 1, ref keysyms_per_keycode); - Debug.Print("{0} keysyms per keycode.", keysyms_per_keycode); - - keysyms = new IntPtr[(lastKeyCode - firstKeyCode + 1) * keysyms_per_keycode]; - Marshal.PtrToStructure(keysym_ptr, keysyms); - API.Free(keysym_ptr); - - keyboard.Description = "Default X11 keyboard"; - keyboard.NumberOfKeys = lastKeyCode - firstKeyCode + 1; - keyboard.DeviceID = IntPtr.Zero; - dummy_keyboard_list.Add(keyboard); - - // Request that auto-repeat is only set on devices that support it physically. - // This typically means that it's turned off for keyboards (which is what we want). - // We prefer this method over XAutoRepeatOff/On, because the latter needs to - // be reset before the program exits. - bool supported; - Functions.XkbSetDetectableAutoRepeat(window.Display, true, out supported); - } - - Debug.Unindent(); - } - - #endregion - - #region TranslateKey - - internal bool TranslateKey(ref XKeyEvent e, out Key key) - { - XKey keysym = (XKey)API.LookupKeysym(ref e, 0); - XKey keysym2 = (XKey)API.LookupKeysym(ref e, 1); - key = X11KeyMap.GetKey(keysym); - if (key == Key.Unknown) - { - key = X11KeyMap.GetKey(keysym2); - } - if (key == Key.Unknown) - { - Debug.Print("KeyCode {0} (Keysym: {1}, {2}) not mapped.", e.keycode, (XKey)keysym, (XKey)keysym2); - } - - return key != Key.Unknown; - } - - #endregion - - #region internal void ProcessEvent(ref XEvent e) - - internal void ProcessEvent(ref XEvent e) - { - switch (e.type) - { - case XEventName.ButtonPress: - if (e.ButtonEvent.button == 1) mouse[OpenTK.Input.MouseButton.Left] = true; - else if (e.ButtonEvent.button == 2) mouse[OpenTK.Input.MouseButton.Middle] = true; - else if (e.ButtonEvent.button == 3) mouse[OpenTK.Input.MouseButton.Right] = true; - else if (e.ButtonEvent.button == 4) mouse.Wheel++; - else if (e.ButtonEvent.button == 5) mouse.Wheel--; - else if (e.ButtonEvent.button == 6) mouse[OpenTK.Input.MouseButton.Button1] = true; - else if (e.ButtonEvent.button == 7) mouse[OpenTK.Input.MouseButton.Button2] = true; - else if (e.ButtonEvent.button == 8) mouse[OpenTK.Input.MouseButton.Button3] = true; - else if (e.ButtonEvent.button == 9) mouse[OpenTK.Input.MouseButton.Button4] = true; - else if (e.ButtonEvent.button == 10) mouse[OpenTK.Input.MouseButton.Button5] = true; - else if (e.ButtonEvent.button == 11) mouse[OpenTK.Input.MouseButton.Button6] = true; - else if (e.ButtonEvent.button == 12) mouse[OpenTK.Input.MouseButton.Button7] = true; - else if (e.ButtonEvent.button == 13) mouse[OpenTK.Input.MouseButton.Button8] = true; - else if (e.ButtonEvent.button == 14) mouse[OpenTK.Input.MouseButton.Button9] = true; - //if ((e.state & (int)X11.MouseMask.Button4Mask) != 0) m.Wheel++; - //if ((e.state & (int)X11.MouseMask.Button5Mask) != 0) m.Wheel--; - //Debug.Print("Button pressed: {0}", e.ButtonEvent.button); - break; - - case XEventName.ButtonRelease: - if (e.ButtonEvent.button == 1) mouse[OpenTK.Input.MouseButton.Left] = false; - else if (e.ButtonEvent.button == 2) mouse[OpenTK.Input.MouseButton.Middle] = false; - else if (e.ButtonEvent.button == 3) mouse[OpenTK.Input.MouseButton.Right] = false; - else if (e.ButtonEvent.button == 6) mouse[OpenTK.Input.MouseButton.Button1] = false; - else if (e.ButtonEvent.button == 7) mouse[OpenTK.Input.MouseButton.Button2] = false; - else if (e.ButtonEvent.button == 8) mouse[OpenTK.Input.MouseButton.Button3] = false; - else if (e.ButtonEvent.button == 9) mouse[OpenTK.Input.MouseButton.Button4] = false; - else if (e.ButtonEvent.button == 10) mouse[OpenTK.Input.MouseButton.Button5] = false; - else if (e.ButtonEvent.button == 11) mouse[OpenTK.Input.MouseButton.Button6] = false; - else if (e.ButtonEvent.button == 12) mouse[OpenTK.Input.MouseButton.Button7] = false; - else if (e.ButtonEvent.button == 13) mouse[OpenTK.Input.MouseButton.Button8] = false; - else if (e.ButtonEvent.button == 14) mouse[OpenTK.Input.MouseButton.Button9] = false; - break; - - case XEventName.MotionNotify: - mouse.Position = new Point(e.MotionEvent.x, e.MotionEvent.y); - break; - } - } - - #endregion - - #region --- IInputDriver Members --- - - #region public IList Keyboard - - public IList Keyboard - { - get { return dummy_keyboard_list; }//return keyboardDriver.Keyboard; - } - - #endregion - - #region public IList Mouse - - public IList Mouse - { - get { return (IList)dummy_mice_list; } //return mouseDriver.Mouse; - } - - #endregion - - public IList Joysticks - { - get { throw new NotImplementedException(); } - } - - #endregion - - #region public void Poll() - - /// - /// Polls and updates state of all keyboard, mouse and joystick devices. - /// - public void Poll() - { - } - - #endregion - - #region --- IDisposable Members --- - - public void Dispose() - { - //this.Dispose(true); - //GC.SuppressFinalize(this); - } - - //private void Dispose(bool manual) - //{ - // if (!disposed) - // { - // //disposing = true; - // if (pollingThread != null && pollingThread.IsAlive) - // pollingThread.Abort(); - - // if (manual) - // { - // } - - // disposed = true; - // } - //} - - //~X11Input() - //{ - // this.Dispose(false); - //} - - #endregion - } -} diff --git a/Source/OpenTK/Platform/X11/X11KeyMap.cs b/Source/OpenTK/Platform/X11/X11KeyMap.cs index e8cb1f21..d9325f8f 100644 --- a/Source/OpenTK/Platform/X11/X11KeyMap.cs +++ b/Source/OpenTK/Platform/X11/X11KeyMap.cs @@ -370,5 +370,22 @@ namespace OpenTK.Platform.X11 return Key.Unknown; } } + + internal static bool TranslateKey(ref XKeyEvent e, out Key key) + { + XKey keysym = (XKey)API.LookupKeysym(ref e, 0); + XKey keysym2 = (XKey)API.LookupKeysym(ref e, 1); + key = X11KeyMap.GetKey(keysym); + if (key == Key.Unknown) + { + key = X11KeyMap.GetKey(keysym2); + } + if (key == Key.Unknown) + { + Debug.Print("KeyCode {0} (Keysym: {1}, {2}) not mapped.", e.keycode, (XKey)keysym, (XKey)keysym2); + } + + return key != Key.Unknown; + } } } From e093fd23f6db5348d842e108d0c0208f413282a2 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 4 May 2014 08:32:26 +0200 Subject: [PATCH 16/51] [Mac] Avoid calling into LegacyInputDriver --- Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs index e6324157..0760e38d 100644 --- a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs +++ b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs @@ -490,7 +490,7 @@ namespace OpenTK.Platform.MacOS case NSEventType.OtherMouseDragged: case NSEventType.MouseMoved: { - Point p = InputDriver.Mouse[0].Position; + Point p = new Point(MouseState.X, MouseState.Y); if (CursorVisible) { // Use absolute coordinates From 67359a5d90b3f2e97e682f198cb76d3907a03362 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 4 May 2014 08:33:20 +0200 Subject: [PATCH 17/51] [Input] MouseDevice is now based on MouseState This way we have a single state representation (MouseState), shared by all mouse APIs and events. --- Source/OpenTK/Input/MouseDevice.cs | 102 +++++++++++------------------ 1 file changed, 39 insertions(+), 63 deletions(-) diff --git a/Source/OpenTK/Input/MouseDevice.cs b/Source/OpenTK/Input/MouseDevice.cs index 85b9f837..99114b50 100644 --- a/Source/OpenTK/Input/MouseDevice.cs +++ b/Source/OpenTK/Input/MouseDevice.cs @@ -48,12 +48,8 @@ namespace OpenTK.Input string description; IntPtr id; int numButtons, numWheels; - readonly bool[] button_state = new bool[Enum.GetValues(typeof(MouseButton)).Length]; - float wheel, last_wheel; - Point pos = new Point(), last_pos = new Point(); - MouseMoveEventArgs move_args = new MouseMoveEventArgs(); - MouseButtonEventArgs button_args = new MouseButtonEventArgs(); - MouseWheelEventArgs wheel_args = new MouseWheelEventArgs(); + + MouseState state; #if COMPAT_REV1519 int wheel_last_accessed = 0; Point pos_last_accessed = new Point(); @@ -139,8 +135,7 @@ namespace OpenTK.Input /// public int Wheel { - get { return (int)Math.Round(wheel, MidpointRounding.AwayFromZero); } - internal set { WheelPrecise = value; } + get { return state.Wheel; } } /// @@ -148,20 +143,7 @@ namespace OpenTK.Input /// public float WheelPrecise { - get { return wheel; } - internal set - { - wheel = value; - - wheel_args.X = pos.X; - wheel_args.Y = pos.Y; - wheel_args.ValuePrecise = wheel; - wheel_args.DeltaPrecise = wheel - last_wheel; - - WheelChanged(this, wheel_args); - - last_wheel = wheel; - } + get { return state.WheelPrecise; } } #endregion @@ -173,7 +155,7 @@ namespace OpenTK.Input /// public int X { - get { return pos.X; } + get { return state.X; } } #endregion @@ -185,7 +167,7 @@ namespace OpenTK.Input /// public int Y { - get { return pos.Y; } + get { return state.Y; } } #endregion @@ -201,21 +183,11 @@ namespace OpenTK.Input { get { - return button_state[(int)button]; + return state[button]; } internal set { - bool previous_state = button_state[(int)button]; - button_state[(int)button] = value; - - button_args.X = pos.X; - button_args.Y = pos.Y; - button_args.Button = button; - button_args.IsPressed = value; - if (value && !previous_state) - ButtonDown(this, button_args); - else if (!value && previous_state) - ButtonUp(this, button_args); + state[button] = value; } } @@ -225,30 +197,34 @@ namespace OpenTK.Input #region --- Internal Members --- - #region internal Point Position - - /// - /// Sets a System.Drawing.Point representing the absolute position of the pointer, in window pixel coordinates. - /// - internal Point Position + internal void SetState(MouseState state) { - get - { - return pos; - } - set - { - pos = value; - move_args.X = pos.X; - move_args.Y = pos.Y; - move_args.XDelta = pos.X - last_pos.X; - move_args.YDelta = pos.Y - last_pos.Y; - Move(this, move_args); - last_pos = pos; - } + this.state = state; } - #endregion + internal void HandleMouseDown(object sender, MouseButtonEventArgs e) + { + SetState(e.Mouse); + ButtonDown(this, e); + } + + internal void HandleMouseUp(object sender, MouseButtonEventArgs e) + { + SetState(e.Mouse); + ButtonUp(this, e); + } + + internal void HandleMouseMove(object sender, MouseMoveEventArgs e) + { + SetState(e.Mouse); + Move(this, e); + } + + internal void HandleMouseWheel(object sender, MouseWheelEventArgs e) + { + SetState(e.Mouse); + WheelChanged(this, e); + } #endregion @@ -313,8 +289,8 @@ namespace OpenTK.Input { get { - int result = (int)Math.Round(wheel - wheel_last_accessed, MidpointRounding.AwayFromZero); - wheel_last_accessed = (int)wheel; + int result = (int)Math.Round(state.WheelPrecise - wheel_last_accessed, MidpointRounding.AwayFromZero); + wheel_last_accessed = state.Wheel; return result; } } @@ -331,8 +307,8 @@ namespace OpenTK.Input { get { - int result = pos.X - pos_last_accessed.X; - pos_last_accessed.X = pos.X; + int result = state.X - pos_last_accessed.X; + pos_last_accessed.X = state.X; return result; } } @@ -349,8 +325,8 @@ namespace OpenTK.Input { get { - int result = pos.Y - pos_last_accessed.Y; - pos_last_accessed.Y = pos.Y; + int result = state.Y - pos_last_accessed.Y; + pos_last_accessed.Y = state.Y; return result; } } From 1457e4430907556764673b7512c63e68b715fd76 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 4 May 2014 08:33:51 +0200 Subject: [PATCH 18/51] [Examples] Hook both MouseDevice and Mouse events They should give identical results. --- .../Examples/OpenTK/Test/GameWindowStates.cs | 99 ++++++++++++++++--- 1 file changed, 87 insertions(+), 12 deletions(-) diff --git a/Source/Examples/OpenTK/Test/GameWindowStates.cs b/Source/Examples/OpenTK/Test/GameWindowStates.cs index 703c608e..d3d3fe26 100644 --- a/Source/Examples/OpenTK/Test/GameWindowStates.cs +++ b/Source/Examples/OpenTK/Test/GameWindowStates.cs @@ -26,9 +26,15 @@ namespace Examples.Tests bool mouse_in_window = false; bool viewport_changed = true; - // mouse information + // legacy NativeWindow.MouseDevice events + Vector4 mousedevice_pos; + int mousedevice_buttons; + MouseState mousedevice_state; + + // new NativeWindow.Mouse* events Vector4 mouse_pos; int mouse_buttons; + MouseState mouse_state; // time drift Stopwatch watch = new Stopwatch(); @@ -60,10 +66,15 @@ namespace Examples.Tests MouseEnter += delegate { mouse_in_window = true; }; MouseLeave += delegate { mouse_in_window = false; }; - Mouse.Move += MouseMoveHandler; - Mouse.WheelChanged += MouseWheelHandler; - Mouse.ButtonDown += MouseButtonHandler; - Mouse.ButtonUp += MouseButtonHandler; + Mouse.Move += MouseDeviceMoveHandler; + Mouse.WheelChanged += MouseDeviceWheelHandler; + Mouse.ButtonDown += MouseDeviceButtonHandler; + Mouse.ButtonUp += MouseDeviceButtonHandler; + + MouseMove += MouseMoveHandler; + MouseWheel += MouseWheelHandler; + MouseDown += MouseButtonHandler; + MouseUp += MouseButtonHandler; } private void KeyPressHandler(object sender, KeyPressEventArgs e) @@ -122,12 +133,53 @@ namespace Examples.Tests modifiers = e.Modifiers; } + #region MouseDevice events + + void MouseDeviceMoveHandler(object sender, MouseMoveEventArgs e) + { + mousedevice_pos.X = e.X; + mousedevice_pos.Y = e.Y; + mousedevice_pos.Z = e.Mouse.Scroll.X; + mousedevice_pos.W = e.Mouse.Scroll.Y; + mousedevice_state = e.Mouse; + } + + void MouseDeviceButtonHandler(object sender, MouseButtonEventArgs e) + { + if (e.Button == MouseButton.Left && e.IsPressed) + { + CursorVisible = false; + } + + if (e.IsPressed) + { + mousedevice_buttons |= 1 << (int)e.Button; + } + else + { + mousedevice_buttons &= ~(1 << (int)e.Button); + } + mousedevice_state = e.Mouse; + } + + void MouseDeviceWheelHandler(object sender, MouseWheelEventArgs e) + { + mousedevice_pos.Z = e.Mouse.Scroll.Y; + mousedevice_pos.W = e.Mouse.Scroll.X; + mousedevice_state = e.Mouse; + } + + #endregion + + #region Mouse events + void MouseMoveHandler(object sender, MouseMoveEventArgs e) { mouse_pos.X = e.X; mouse_pos.Y = e.Y; mouse_pos.Z = e.Mouse.Scroll.X; mouse_pos.W = e.Mouse.Scroll.Y; + mouse_state = e.Mouse; } void MouseButtonHandler(object sender, MouseButtonEventArgs e) @@ -145,14 +197,18 @@ namespace Examples.Tests { mouse_buttons &= ~(1 << (int)e.Button); } + mouse_state = e.Mouse; } void MouseWheelHandler(object sender, MouseWheelEventArgs e) { - mouse_pos.Z += e.Mouse.Scroll.Y; - mouse_pos.W += e.Mouse.Scroll.X; + mouse_pos.Z = e.Mouse.Scroll.Y; + mouse_pos.W = e.Mouse.Scroll.X; + mouse_state = e.Mouse; } + #endregion + static int Clamp(int val, int min, int max) { return val > max ? max : val < min ? min : val; @@ -229,8 +285,8 @@ namespace Examples.Tests { StringBuilder sb = new StringBuilder(); sb.Append("MouseDevice: "); - sb.Append(new Vector3(Mouse.X, Mouse.Y, Mouse.Wheel)); - sb.Append(" "); + sb.AppendFormat("[{0}, {1}, {2:0.00}] ", + Mouse.X, Mouse.Y, Mouse.WheelPrecise); for (var i = MouseButton.Left; i < MouseButton.LastButton; i++) { if (Mouse[i]) @@ -243,9 +299,27 @@ namespace Examples.Tests DrawString(gfx, sb.ToString(), line++); sb.Remove(0, sb.Length); - sb.Append("Mouse events: "); - sb.Append(mouse_pos); + sb.Append("MouseDevice events: "); + sb.AppendFormat("[{0}, {1}, {2:0.00}, {3:0.00}] ", + mousedevice_pos.X, mousedevice_pos.Y, + mousedevice_pos.Z, mousedevice_pos.W); + for (var i = MouseButton.Left; i < MouseButton.LastButton; i++) + { + if ((mousedevice_buttons & (1 << (int)i)) != 0) + { + sb.Append(i); + sb.Append(" "); + } + } sb.Append(" "); + sb.AppendLine(mousedevice_state.ToString()); + DrawString(gfx, sb.ToString(), line++); + + sb.Remove(0, sb.Length); + sb.Append("Mouse events: "); + sb.AppendFormat("[{0}, {1}, {2:0.00}, {3:0.00}] ", + mouse_pos.X, mouse_pos.Y, + mouse_pos.Z, mouse_pos.W); for (var i = MouseButton.Left; i < MouseButton.LastButton; i++) { if ((mouse_buttons & (1 << (int)i)) != 0) @@ -254,7 +328,8 @@ namespace Examples.Tests sb.Append(" "); } } - sb.AppendLine(); + sb.Append(" "); + sb.AppendLine(mouse_state.ToString()); DrawString(gfx, sb.ToString(), line++); return line; } From 15f5abe1e1d600aee00eb081327f4ed1032188b2 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 4 May 2014 08:34:13 +0200 Subject: [PATCH 19/51] [Platform] Remove lambdas --- Source/OpenTK/Platform/LegacyInputDriver.cs | 23 ++++----------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/Source/OpenTK/Platform/LegacyInputDriver.cs b/Source/OpenTK/Platform/LegacyInputDriver.cs index 96596e54..50d00deb 100644 --- a/Source/OpenTK/Platform/LegacyInputDriver.cs +++ b/Source/OpenTK/Platform/LegacyInputDriver.cs @@ -61,25 +61,10 @@ namespace OpenTK.Platform dummy_keyboard_list.Add(keyboard); // Hook mouse events - window.MouseDown += (sender, e) => - { - mouse[e.Button] = true; - }; - - window.MouseUp += (sender, e) => - { - mouse[e.Button] = false; - }; - - window.MouseMove += (sender, e) => - { - mouse.Position = e.Position; - }; - - window.MouseWheel += (sender, e) => - { - mouse.WheelPrecise = e.Mouse.Scroll.Y; - }; + window.MouseDown += mouse.HandleMouseDown; + window.MouseUp += mouse.HandleMouseUp; + window.MouseMove += mouse.HandleMouseMove; + window.MouseWheel += mouse.HandleMouseWheel; // Hook keyboard events window.KeyDown += (sender, e) => From 9dab96c5c1a410f1f93ba194bf686f5214947d73 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 4 May 2014 15:23:24 +0200 Subject: [PATCH 20/51] [Mac] Disable momentum scrolling and key pop-ups We might wish to add an option to re-enable momentum scrolling, as this might be useful to some applications for a more native feel. --- Source/OpenTK/Platform/MacOS/Cocoa/Class.cs | 5 +++++ Source/OpenTK/Platform/MacOS/Cocoa/Cocoa.cs | 6 ++++++ .../Platform/MacOS/Cocoa/NSApplication.cs | 19 ++++++++++++++++++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/Source/OpenTK/Platform/MacOS/Cocoa/Class.cs b/Source/OpenTK/Platform/MacOS/Cocoa/Class.cs index 61bec8ae..6c29bdcf 100644 --- a/Source/OpenTK/Platform/MacOS/Cocoa/Class.cs +++ b/Source/OpenTK/Platform/MacOS/Cocoa/Class.cs @@ -35,6 +35,11 @@ namespace OpenTK.Platform.MacOS { static class Class { + public static readonly IntPtr NSAutoreleasePool = Get("NSAutoreleasePool"); + public static readonly IntPtr NSDictionary = Get("NSDictionary"); + public static readonly IntPtr NSNumber = Get("NSNumber"); + public static readonly IntPtr NSUserDefaults = Get("NSUserDefaults"); + [DllImport (Cocoa.LibObjC)] extern static IntPtr class_getName(IntPtr handle); diff --git a/Source/OpenTK/Platform/MacOS/Cocoa/Cocoa.cs b/Source/OpenTK/Platform/MacOS/Cocoa/Cocoa.cs index 7874d858..5acd11b7 100644 --- a/Source/OpenTK/Platform/MacOS/Cocoa/Cocoa.cs +++ b/Source/OpenTK/Platform/MacOS/Cocoa/Cocoa.cs @@ -57,9 +57,15 @@ namespace OpenTK.Platform.MacOS [DllImport(LibObjC, EntryPoint="objc_msgSend")] public extern static IntPtr SendIntPtr(IntPtr receiver, IntPtr selector, IntPtr intPtr1, IntPtr intPtr2, IntPtr intPtr3); + [DllImport(LibObjC, EntryPoint="objc_msgSend")] + public extern static IntPtr SendIntPtr(IntPtr receiver, IntPtr selector, IntPtr intPtr1, IntPtr intPtr2, IntPtr intPtr3, IntPtr intPtr4, IntPtr intPtr5); + [DllImport(LibObjC, EntryPoint="objc_msgSend")] public extern static IntPtr SendIntPtr(IntPtr receiver, IntPtr selector, IntPtr p1, PointF p2); + [DllImport(LibObjC, EntryPoint="objc_msgSend")] + public extern static IntPtr SendIntPtr(IntPtr receiver, IntPtr selector, bool p1); + [DllImport(LibObjC, EntryPoint="objc_msgSend")] public extern static IntPtr SendIntPtr(IntPtr receiver, IntPtr selector, SizeF p1); diff --git a/Source/OpenTK/Platform/MacOS/Cocoa/NSApplication.cs b/Source/OpenTK/Platform/MacOS/Cocoa/NSApplication.cs index cc0a34f9..16738e46 100644 --- a/Source/OpenTK/Platform/MacOS/Cocoa/NSApplication.cs +++ b/Source/OpenTK/Platform/MacOS/Cocoa/NSApplication.cs @@ -44,7 +44,7 @@ namespace OpenTK.Platform.MacOS internal static void Initialize() { // Create the NSAutoreleasePool - AutoreleasePool = Cocoa.SendIntPtr(Cocoa.SendIntPtr(Class.Get("NSAutoreleasePool"), Selector.Alloc), Selector.Init); + AutoreleasePool = Cocoa.SendIntPtr(Cocoa.SendIntPtr(Class.NSAutoreleasePool, Selector.Alloc), Selector.Init); // Register a Quit method to be called on cmd-q IntPtr nsapp = Class.Get("NSApplication"); @@ -80,6 +80,23 @@ namespace OpenTK.Platform.MacOS // Tell cocoa we're ready to run the application (usually called by [NSApp run]). Cocoa.SendVoid(Handle, Selector.Get("finishLaunching")); + + // Disable momentum scrolling and long-press key pop-ups + IntPtr settings = Cocoa.SendIntPtr(Class.NSDictionary, Selector.Alloc); + IntPtr momentum_scrolling = Cocoa.SendIntPtr(Class.NSNumber, Selector.Get("numberWithBool:"), false); + IntPtr press_and_hold = Cocoa.SendIntPtr(Class.NSNumber, Selector.Get("numberWithBool:"), false); + + // Initialize and register the settings dictionary + settings = + Cocoa.SendIntPtr(settings, Selector.Get("initWithObjectsAndKeys:"), + momentum_scrolling, Cocoa.ToNSString("AppleMomentumScrollSupported"), + press_and_hold, Cocoa.ToNSString("ApplePressAndHoldEnabled"), + IntPtr.Zero); + Cocoa.SendVoid( + Cocoa.SendIntPtr(Class.NSUserDefaults, Selector.Get("standardUserDefaults")), + Selector.Get("registerDefaults:"), + settings); + Cocoa.SendVoid(settings, Selector.Release); } internal static event EventHandler Quit = delegate { }; From 30c73e8ead973c79b720b06a04be5de19d847ef4 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 4 May 2014 15:23:40 +0200 Subject: [PATCH 21/51] [OpenTK] Raise NativeWindow.Mouse* events --- Source/OpenTK/NativeWindow.cs | 61 +++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/Source/OpenTK/NativeWindow.cs b/Source/OpenTK/NativeWindow.cs index dade7d81..d88610ec 100644 --- a/Source/OpenTK/NativeWindow.cs +++ b/Source/OpenTK/NativeWindow.cs @@ -922,6 +922,54 @@ namespace OpenTK #endregion + /// + /// Raises the event. + /// + /// + /// A instance carrying mouse state information. + /// The information carried by this instance is only valid within this method body. + /// + protected virtual void OnMouseDown(MouseButtonEventArgs e) + { + MouseDown(this, e); + } + + /// + /// Raises the event. + /// + /// + /// A instance carrying mouse state information. + /// The information carried by this instance is only valid within this method body. + /// + protected virtual void OnMouseUp(MouseButtonEventArgs e) + { + MouseUp(this, e); + } + + /// + /// Raises the event. + /// + /// + /// A instance carrying mouse state information. + /// The information carried by this instance is only valid within this method body. + /// + protected virtual void OnMouseMove(MouseMoveEventArgs e) + { + MouseMove(this, e); + } + + /// + /// Raises the event. + /// + /// + /// A instance carrying mouse state information. + /// The information carried by this instance is only valid within this method body. + /// + protected virtual void OnMouseWheel(MouseWheelEventArgs e) + { + MouseWheel(this, e); + } + #region OnResize /// @@ -1074,6 +1122,11 @@ namespace OpenTK #endregion + private void OnMouseDownInternal(object sender, MouseButtonEventArgs e) { OnMouseDown(e); } + private void OnMouseUpInternal(object sender, MouseButtonEventArgs e) { OnMouseUp(e); } + private void OnMouseMoveInternal(object sender, MouseMoveEventArgs e) { OnMouseMove(e); } + private void OnMouseWheelInternal(object sender, MouseWheelEventArgs e) { OnMouseWheel(e); } + #region OnMoveInternal private void OnMoveInternal(object sender, EventArgs e) { OnMove(e); } @@ -1136,6 +1189,10 @@ namespace OpenTK implementation.KeyUp += OnKeyUpInternal; implementation.MouseEnter += OnMouseEnterInternal; implementation.MouseLeave += OnMouseLeaveInternal; + implementation.MouseDown += OnMouseDownInternal; + implementation.MouseUp += OnMouseUpInternal; + implementation.MouseMove += OnMouseMoveInternal; + implementation.MouseWheel += OnMouseWheelInternal; implementation.Move += OnMoveInternal; implementation.Resize += OnResizeInternal; implementation.TitleChanged += OnTitleChangedInternal; @@ -1156,6 +1213,10 @@ namespace OpenTK implementation.KeyUp -= OnKeyUpInternal; implementation.MouseEnter -= OnMouseEnterInternal; implementation.MouseLeave -= OnMouseLeaveInternal; + implementation.MouseDown -= OnMouseDownInternal; + implementation.MouseUp -= OnMouseUpInternal; + implementation.MouseMove -= OnMouseMoveInternal; + implementation.MouseWheel -= OnMouseWheelInternal; implementation.Move -= OnMoveInternal; implementation.Resize -= OnResizeInternal; implementation.TitleChanged -= OnTitleChangedInternal; From 4b115c443b9985e7a929416b125e7d68a9530033 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 4 May 2014 15:24:02 +0200 Subject: [PATCH 22/51] [Mac] Add horizontal scrolling --- .../Platform/MacOS/CocoaNativeWindow.cs | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs index 0760e38d..99441cf2 100644 --- a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs +++ b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs @@ -78,6 +78,7 @@ namespace OpenTK.Platform.MacOS static readonly IntPtr selLocationInWindowOwner = Selector.Get("locationInWindow"); static readonly IntPtr selHide = Selector.Get("hide"); static readonly IntPtr selUnhide = Selector.Get("unhide"); + static readonly IntPtr selScrollingDeltaX = Selector.Get("scrollingDeltaX"); static readonly IntPtr selScrollingDeltaY = Selector.Get("scrollingDeltaY"); static readonly IntPtr selDeltaX = Selector.Get("deltaX"); static readonly IntPtr selDeltaY = Selector.Get("deltaY"); @@ -145,7 +146,7 @@ namespace OpenTK.Platform.MacOS private bool cursorInsideWindow = true; private MouseCursor selectedCursor = MouseCursor.Default; // user-selected cursor - private const float scrollFactor = 120.0f; + private const float scrollFactor = 10.0f; private const bool exclusiveFullscreen = false; public CocoaNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device) @@ -517,9 +518,13 @@ namespace OpenTK.Platform.MacOS MathHelper.Clamp((int)Math.Round(p.Y + dy), 0, Height)); } - MouseState.X = p.X; - MouseState.Y = p.Y; - OnMouseMove(); + // Only raise events when the mouse has actually moved + if (MouseState.X != p.X || MouseState.Y != p.Y) + { + MouseState.X = p.X; + MouseState.Y = p.Y; + OnMouseMove(); + } } break; @@ -528,16 +533,24 @@ namespace OpenTK.Platform.MacOS case NSEventType.ScrollWheel: { - var scrollingDelta = Cocoa.SendFloat(e, selScrollingDeltaY); - var factor = 1.0f; - + float dx, dy; if (Cocoa.SendBool(e, selHasPreciseScrollingDeltas)) { - factor = 1.0f / scrollFactor; // Problem: Don't know what factor to use here, but this seems to work. + dx = Cocoa.SendFloat(e, selScrollingDeltaX) / scrollFactor; + dy = Cocoa.SendFloat(e, selScrollingDeltaY) / scrollFactor; + } + else + { + dx = Cocoa.SendFloat(e, selDeltaX); + dy = Cocoa.SendFloat(e, selDeltaY); } - MouseState.SetScrollRelative(0, scrollingDelta * factor); - OnMouseWheel(); + // Only raise wheel events when the user has actually scrolled + if (dx != 0 || dy != 0) + { + MouseState.SetScrollRelative(dx, dy); + OnMouseWheel(); + } } break; From 7af710f4dd5d024aa1f82a1f08a0415b57d3443e Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 4 May 2014 16:58:24 +0200 Subject: [PATCH 23/51] [Input] Removed pointless MouseDevice.SetState MouseDevice state should only be modified through its HandleMouse* methods, not directly. --- Source/OpenTK/Input/MouseDevice.cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/Source/OpenTK/Input/MouseDevice.cs b/Source/OpenTK/Input/MouseDevice.cs index 99114b50..f5b32071 100644 --- a/Source/OpenTK/Input/MouseDevice.cs +++ b/Source/OpenTK/Input/MouseDevice.cs @@ -197,32 +197,27 @@ namespace OpenTK.Input #region --- Internal Members --- - internal void SetState(MouseState state) - { - this.state = state; - } - internal void HandleMouseDown(object sender, MouseButtonEventArgs e) { - SetState(e.Mouse); + state = e.Mouse; ButtonDown(this, e); } internal void HandleMouseUp(object sender, MouseButtonEventArgs e) { - SetState(e.Mouse); + state = e.Mouse; ButtonUp(this, e); } internal void HandleMouseMove(object sender, MouseMoveEventArgs e) { - SetState(e.Mouse); + state = e.Mouse; Move(this, e); } internal void HandleMouseWheel(object sender, MouseWheelEventArgs e) { - SetState(e.Mouse); + state = e.Mouse; WheelChanged(this, e); } From 1096a8802af36d536421dba2d56987b9c8203f32 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 4 May 2014 16:58:59 +0200 Subject: [PATCH 24/51] [Input] Removed pointless ValuePrecise setter --- Source/OpenTK/Input/MouseEventArgs.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/OpenTK/Input/MouseEventArgs.cs b/Source/OpenTK/Input/MouseEventArgs.cs index 9770e43d..f9c6feb7 100644 --- a/Source/OpenTK/Input/MouseEventArgs.cs +++ b/Source/OpenTK/Input/MouseEventArgs.cs @@ -356,7 +356,6 @@ namespace OpenTK.Input public float ValuePrecise { get { return Mouse.Scroll.Y; } - internal set { Mouse.SetScrollAbsolute(Mouse.Scroll.X, value); } } /// From 268e8fccde3da78070093218af1a6acfbb6657c8 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 4 May 2014 17:01:34 +0200 Subject: [PATCH 25/51] [Input] Removed pointless unsafe region --- Source/OpenTK/Input/MouseState.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Source/OpenTK/Input/MouseState.cs b/Source/OpenTK/Input/MouseState.cs index 557aa1c1..f6e2259b 100644 --- a/Source/OpenTK/Input/MouseState.cs +++ b/Source/OpenTK/Input/MouseState.cs @@ -292,14 +292,11 @@ namespace OpenTK.Input internal void MergeBits(MouseState other) { - unsafe - { - buttons |= other.buttons; - SetScrollRelative(other.scroll.X, other.scroll.Y); - X += other.X; - Y += other.Y; - IsConnected |= other.IsConnected; - } + buttons |= other.buttons; + SetScrollRelative(other.scroll.X, other.scroll.Y); + X += other.X; + Y += other.Y; + IsConnected |= other.IsConnected; } internal void SetIsConnected(bool value) From 2d5afcf0e85447a4cf68102cef603e8174f7b1cd Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 4 May 2014 17:02:00 +0200 Subject: [PATCH 26/51] [Input] Update state through HandleKey* events --- Source/OpenTK/Platform/LegacyInputDriver.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Source/OpenTK/Platform/LegacyInputDriver.cs b/Source/OpenTK/Platform/LegacyInputDriver.cs index 50d00deb..2378b91b 100644 --- a/Source/OpenTK/Platform/LegacyInputDriver.cs +++ b/Source/OpenTK/Platform/LegacyInputDriver.cs @@ -67,15 +67,8 @@ namespace OpenTK.Platform window.MouseWheel += mouse.HandleMouseWheel; // Hook keyboard events - window.KeyDown += (sender, e) => - { - keyboard.SetKey(e.Key, e.ScanCode, e.Modifiers, true); - }; - - window.KeyUp += (sender, e) => - { - keyboard.SetKey(e.Key, e.ScanCode, e.Modifiers, false); - }; + window.KeyDown += keyboard.HandleKeyDown; + window.KeyUp += keyboard.HandleKeyUp; window.FocusedChanged += (sender, e) => { From 28a1849a7210c7940d978b711103b7b3f95eacc6 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 4 May 2014 17:02:37 +0200 Subject: [PATCH 27/51] [Mac] Moved MacOSKeys to MacOS namespace They are used by both Carbon and Cocoa, so there is little reason to keep them in MacOS.Carbon --- Source/OpenTK/Platform/MacOS/Carbon/MacOSKeys.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/OpenTK/Platform/MacOS/Carbon/MacOSKeys.cs b/Source/OpenTK/Platform/MacOS/Carbon/MacOSKeys.cs index cd7eaf96..780414e4 100644 --- a/Source/OpenTK/Platform/MacOS/Carbon/MacOSKeys.cs +++ b/Source/OpenTK/Platform/MacOS/Carbon/MacOSKeys.cs @@ -29,7 +29,7 @@ using System; using System.Collections.Generic; using System.Text; -namespace OpenTK.Platform.MacOS.Carbon +namespace OpenTK.Platform.MacOS { // // http://web.archive.org/web/20100501161453/http://www.classicteck.com/rbarticles/mackeyboard.php From e155d647decdc34768459cc15d524689e9f19798 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 4 May 2014 17:04:03 +0200 Subject: [PATCH 28/51] [X11] Added X11KeyMap.TranslateButton() This code was duplicated on four different places before. --- Source/OpenTK/Platform/X11/X11KeyMap.cs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Source/OpenTK/Platform/X11/X11KeyMap.cs b/Source/OpenTK/Platform/X11/X11KeyMap.cs index d9325f8f..29e96935 100644 --- a/Source/OpenTK/Platform/X11/X11KeyMap.cs +++ b/Source/OpenTK/Platform/X11/X11KeyMap.cs @@ -387,5 +387,30 @@ namespace OpenTK.Platform.X11 return key != Key.Unknown; } + + internal static MouseButton TranslateButton(int button, out int wheelx, out int wheely) + { + wheelx = 0; + wheely = 0; + + switch (button) + { + case 1: return MouseButton.Left; + case 2: return MouseButton.Middle; + case 3: return MouseButton.Right; + case 4: wheely = +1; return MouseButton.LastButton; + case 5: wheely = -1; return MouseButton.LastButton; + case 6: wheelx = +1; return MouseButton.LastButton; + case 7: wheelx = -1; return MouseButton.LastButton; + case 8: return MouseButton.Button1; + case 9: return MouseButton.Button2; + case 10: return MouseButton.Button3; + case 11: return MouseButton.Button4; + case 12: return MouseButton.Button5; + case 13: return MouseButton.Button6; + case 14: return MouseButton.Button7; + default: return MouseButton.LastButton; + } + } } } From d968281a1b97bab944bc315a5190877484b936d7 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 4 May 2014 17:05:08 +0200 Subject: [PATCH 29/51] [OpenTK] Refactor keyboard and mouse handling A lot of duplicated code is now moved to NativeWindowBase and LegacyInputDriver. --- Source/OpenTK/Input/KeyboardDevice.cs | 62 ++++++-------- Source/OpenTK/Input/KeyboardKeyEventArgs.cs | 28 ++++-- Source/OpenTK/Input/KeyboardState.cs | 53 ++++-------- .../Platform/MacOS/CocoaNativeWindow.cs | 48 ++++------- Source/OpenTK/Platform/MacOS/HIDInput.cs | 3 +- Source/OpenTK/Platform/NativeWindowBase.cs | 75 ++++++++-------- Source/OpenTK/Platform/SDL2/Sdl2Keyboard.cs | 22 ++--- .../OpenTK/Platform/SDL2/Sdl2NativeWindow.cs | 35 ++------ Source/OpenTK/Platform/Windows/WinGLNative.cs | 49 ++++------- .../OpenTK/Platform/Windows/WinRawKeyboard.cs | 2 +- Source/OpenTK/Platform/X11/X11GLNative.cs | 85 ++++++------------- 11 files changed, 182 insertions(+), 280 deletions(-) diff --git a/Source/OpenTK/Input/KeyboardDevice.cs b/Source/OpenTK/Input/KeyboardDevice.cs index 87fde900..4913dc41 100644 --- a/Source/OpenTK/Input/KeyboardDevice.cs +++ b/Source/OpenTK/Input/KeyboardDevice.cs @@ -21,13 +21,11 @@ namespace OpenTK.Input public sealed class KeyboardDevice : IInputDevice { //private IKeyboard keyboard; - private bool[] keys = new bool[(int)Key.LastKey]; - private bool[] scancodes = new bool[256]; private string description; private int numKeys, numFKeys, numLeds; private IntPtr devID; private bool repeat; - private KeyboardKeyEventArgs args = new KeyboardKeyEventArgs(); + private KeyboardState state; #region --- Constructors --- @@ -44,7 +42,7 @@ namespace OpenTK.Input /// True if the Key is pressed, false otherwise. public bool this[Key key] { - get { return keys[(int)key]; } + get { return state[key]; } } /// @@ -52,9 +50,10 @@ namespace OpenTK.Input /// /// The scancode to check. /// True if the scancode is pressed, false otherwise. + [CLSCompliant(false)] public bool this[uint scancode] { - get { return scancodes[scancode]; } + get { return scancode < (uint)Key.LastKey && state[(Key)scancode]; } } /// @@ -124,7 +123,7 @@ namespace OpenTK.Input /// /// Occurs when a key is pressed. /// - public event EventHandler KeyDown; + public event EventHandler KeyDown = delegate { }; #endregion @@ -133,7 +132,7 @@ namespace OpenTK.Input /// /// Occurs when a key is released. /// - public event EventHandler KeyUp; + public event EventHandler KeyUp = delegate { }; #endregion @@ -185,21 +184,28 @@ namespace OpenTK.Input #region --- Internal Methods --- - #region internal void ClearKeys() + internal void HandleKeyDown(object sender, KeyboardKeyEventArgs e) + { + state = e.Keyboard; + KeyDown(this, e); + } + + internal void HandleKeyUp(object sender, KeyboardKeyEventArgs e) + { + state = e.Keyboard; + KeyUp(this, e); + } internal void ClearKeys() { - for (int i = 0; i < keys.Length; i++) - keys[i] = false; - for (uint i = 0; i < scancodes.Length; i++) - scancodes[i] = false; + for (Key i = 0; i < Key.LastKey; i++) + state[i] = false; } - #endregion - - internal void SetKey(Key key, uint scancode, KeyModifiers mods, bool state) + #if false + internal void SetKey(Key key, uint scancode, KeyModifiers mods, bool pressed) { - if (keys[(int)key] != state || KeyRepeat) + if (state[key] != pressed || KeyRepeat) { // limit scancode to 8bits, otherwise the assignment // below will crash randomly @@ -209,6 +215,7 @@ namespace OpenTK.Input if (state && KeyDown != null) { + args.Key = key; args.ScanCode = scancode; args.Modifiers = mods; @@ -223,28 +230,7 @@ namespace OpenTK.Input } } } - - internal KeyModifiers GetModifiers() - { - KeyModifiers mods = 0; - - if (this[Key.AltLeft] || this[Key.AltRight]) - { - mods |= KeyModifiers.Alt; - } - - if (this[Key.ControlLeft] || this[Key.ControlRight]) - { - mods |= KeyModifiers.Control; - } - - if (this[Key.ShiftLeft] || this[Key.ShiftRight]) - { - mods |= KeyModifiers.Shift; - } - - return mods; - } + #endif #endregion } diff --git a/Source/OpenTK/Input/KeyboardKeyEventArgs.cs b/Source/OpenTK/Input/KeyboardKeyEventArgs.cs index 75c17e68..a22afe8e 100644 --- a/Source/OpenTK/Input/KeyboardKeyEventArgs.cs +++ b/Source/OpenTK/Input/KeyboardKeyEventArgs.cs @@ -46,7 +46,7 @@ namespace OpenTK.Input #region Fields Key key; - KeyModifiers mods; + KeyboardState state; uint scancode; #endregion @@ -97,7 +97,7 @@ namespace OpenTK.Input /// true if pressed; otherwise, false. public bool Alt { - get { return (mods & KeyModifiers.Alt) != 0; } + get { return state[Key.AltLeft] || state[Key.AltRight]; } } /// @@ -106,7 +106,7 @@ namespace OpenTK.Input /// true if pressed; otherwise, false. public bool Control { - get { return (mods & KeyModifiers.Control) != 0; } + get { return state[Key.ControlLeft] || state[Key.ControlRight]; } } /// @@ -115,7 +115,7 @@ namespace OpenTK.Input /// true if pressed; otherwise, false. public bool Shift { - get { return (mods & KeyModifiers.Shift) != 0; } + get { return state[Key.ShiftLeft] || state[Key.ShiftRight]; } } /// @@ -125,8 +125,24 @@ namespace OpenTK.Input /// The modifiers. public KeyModifiers Modifiers { - get { return mods; } - internal set { mods = value; } + get + { + KeyModifiers mods = 0; + mods |= Alt ? KeyModifiers.Alt : 0; + mods |= Control ? KeyModifiers.Control : 0; + mods |= Shift ? KeyModifiers.Shift : 0; + return mods; + } + } + + /// + /// Gets the current . + /// + /// The keyboard. + public KeyboardState Keyboard + { + get { return state; } + internal set { state = value; } } #endregion diff --git a/Source/OpenTK/Input/KeyboardState.cs b/Source/OpenTK/Input/KeyboardState.cs index 2ba3c928..2a61cd22 100644 --- a/Source/OpenTK/Input/KeyboardState.cs +++ b/Source/OpenTK/Input/KeyboardState.cs @@ -43,9 +43,6 @@ namespace OpenTK.Input const int NumInts = ((int)Key.LastKey + IntSize - 1) / IntSize; // The following line triggers bogus CS0214 in gmcs 2.0.1, sigh... unsafe fixed int Keys[NumInts]; - - const int CodesSize = 256; - unsafe fixed int Codes[CodesSize]; bool is_connected; #endregion @@ -61,6 +58,7 @@ namespace OpenTK.Input public bool this[Key key] { get { return IsKeyDown(key); } + internal set { SetKeyState(key, value); } } /// @@ -71,7 +69,7 @@ namespace OpenTK.Input /// True if code is pressed; false otherwise. public bool this[short code] { - get { return IsKeyDown(code); } + get { return IsKeyDown((Key)code); } } /// @@ -89,7 +87,7 @@ namespace OpenTK.Input /// The scan code to check. public bool IsKeyDown(short code) { - return ReadBit(code,true); + return code >= 0 && code < (short)Key.LastKey && ReadBit(code); } /// @@ -107,7 +105,7 @@ namespace OpenTK.Input /// The scan code to check. public bool IsKeyUp(short code) { - return !ReadBit(code,true); + return !IsKeyDown(code); } /// @@ -212,62 +210,51 @@ namespace OpenTK.Input #region Internal Members - internal void SetKeyState(Key key, byte code, bool down) + internal void SetKeyState(Key key, bool down) { if (down) { EnableBit((int)key); - EnableBit(code,true); } else { DisableBit((int)key); - DisableBit(code, true); } } - internal bool ReadBit(int offset, bool ScanCode = false) + internal bool ReadBit(int offset) { - ValidateOffset(offset, ScanCode); + ValidateOffset(offset); int int_offset = offset / 32; int bit_offset = offset % 32; unsafe { - if (ScanCode) - fixed (int* c = Codes) { return (*(c + int_offset) & (1 << bit_offset)) != 0u; } - else - fixed (int* k = Keys) { return (*(k + int_offset) & (1 << bit_offset)) != 0u; } + fixed (int* k = Keys) { return (*(k + int_offset) & (1 << bit_offset)) != 0u; } } } - internal void EnableBit(int offset, bool ScanCode = false) + internal void EnableBit(int offset) { - ValidateOffset(offset, ScanCode); + ValidateOffset(offset); int int_offset = offset / 32; int bit_offset = offset % 32; unsafe { - if (ScanCode) - fixed (int* c = Codes) { *(c + int_offset) |= 1 << bit_offset; } - else - fixed (int* k = Keys) { *(k + int_offset) |= 1 << bit_offset; } + fixed (int* k = Keys) { *(k + int_offset) |= 1 << bit_offset; } } } - internal void DisableBit(int offset, bool ScanCode = false) + internal void DisableBit(int offset) { - ValidateOffset(offset, ScanCode); + ValidateOffset(offset); int int_offset = offset / 32; int bit_offset = offset % 32; unsafe { - if (ScanCode) - fixed (int* c = Codes) { *(c + int_offset) &= ~(1 << bit_offset); } - else - fixed (int* k = Keys) { *(k + int_offset) &= ~(1 << bit_offset); } + fixed (int* k = Keys) { *(k + int_offset) &= ~(1 << bit_offset); } } } @@ -281,12 +268,6 @@ namespace OpenTK.Input for (int i = 0; i < NumInts; i++) *(k1 + i) |= *(k2 + i); } - int* c2 = other.Codes; - fixed (int* c1 = Codes) - { - for (int i = 0; i < CodesSize; i++) - *(c1 + i) |= *(c2 + i); - } } IsConnected |= other.IsConnected; } @@ -300,10 +281,10 @@ namespace OpenTK.Input #region Private Members - static void ValidateOffset(int offset, bool ScanCode) + static void ValidateOffset(int offset) { - if (offset < 0 || offset >= (ScanCode ? 256 : NumInts * IntSize)) - throw new ArgumentOutOfRangeException("offset"); + if (offset < 0 || offset >= NumInts * IntSize) + throw new ArgumentOutOfRangeException(); } #endregion diff --git a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs index 99441cf2..3697f1bf 100644 --- a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs +++ b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs @@ -136,8 +136,6 @@ namespace OpenTK.Platform.MacOS private Nullable deferredWindowBorder; private Nullable previousWindowBorder; private WindowState windowState = WindowState.Normal; - private OpenTK.Input.KeyboardKeyEventArgs keyArgs = new OpenTK.Input.KeyboardKeyEventArgs(); - private KeyPressEventArgs keyPressArgs = new KeyPressEventArgs((char)0); private string title; private RectangleF previousBounds; private int normalLevel; @@ -387,13 +385,6 @@ namespace OpenTK.Platform.MacOS return modifiers; } - private void GetKey(ushort keyCode, NSEventModifierMask modifierFlags, OpenTK.Input.KeyboardKeyEventArgs args) - { - args.Key = MacOSKeyMap.GetKey((Carbon.MacOSKeyCode)keyCode); - args.Modifiers = GetModifiers(modifierFlags); - args.ScanCode = (uint)keyCode; - } - private MouseButton GetMouseButton(int cocoaButtonIndex) { if (cocoaButtonIndex == 0) return MouseButton.Left; @@ -419,14 +410,15 @@ namespace OpenTK.Platform.MacOS { case NSEventType.KeyDown: { - var keyCode = Cocoa.SendUshort(e, selKeyCode); - var modifierFlags = (NSEventModifierMask)Cocoa.SendUint(e, selModifierFlags); + MacOSKeyCode keyCode = (MacOSKeyCode)Cocoa.SendUshort(e, selKeyCode); + //var modifierFlags = (NSEventModifierMask)Cocoa.SendUint(e, selModifierFlags); var isARepeat = Cocoa.SendBool(e, selIsARepeat); - GetKey(keyCode, modifierFlags, keyArgs); + //GetKey(keyCode, modifierFlags, keyArgs); + Key key = MacOSKeyMap.GetKey(keyCode); if (!isARepeat || InputDriver.Keyboard[0].KeyRepeat) { - OnKeyDown(keyArgs); + OnKeyDown(key); } var s = Cocoa.FromNSString(Cocoa.SendIntPtr(e, selCharactersIgnoringModifiers)); @@ -435,10 +427,9 @@ namespace OpenTK.Platform.MacOS int intVal = (int)c; if (!Char.IsControl(c) && (intVal < 63232 || intVal > 63235)) { - // For some reason, arrow keys (mapped 63232-63235) are seen as non-control characters, so get rid of those. - - keyPressArgs.KeyChar = c; - OnKeyPress(keyPressArgs); + // For some reason, arrow keys (mapped 63232-63235) + // are seen as non-control characters, so get rid of those. + OnKeyPress(c); } } } @@ -446,11 +437,11 @@ namespace OpenTK.Platform.MacOS case NSEventType.KeyUp: { - var keyCode = Cocoa.SendUshort(e, selKeyCode); - var modifierFlags = (NSEventModifierMask)Cocoa.SendUint(e, selModifierFlags); - - GetKey(keyCode, modifierFlags, keyArgs); - OnKeyUp(keyArgs); + MacOSKeyCode keyCode = (MacOSKeyCode)Cocoa.SendUshort(e, selKeyCode); + //var modifierFlags = (NSEventModifierMask)Cocoa.SendUint(e, selModifierFlags); + //GetKey(keyCode, modifierFlags, keyArgs); + Key key = MacOSKeyMap.GetKey(keyCode); + OnKeyUp(key); } break; @@ -521,9 +512,7 @@ namespace OpenTK.Platform.MacOS // Only raise events when the mouse has actually moved if (MouseState.X != p.X || MouseState.Y != p.Y) { - MouseState.X = p.X; - MouseState.Y = p.Y; - OnMouseMove(); + OnMouseMove(p.X, p.Y); } } break; @@ -548,8 +537,7 @@ namespace OpenTK.Platform.MacOS // Only raise wheel events when the user has actually scrolled if (dx != 0 || dy != 0) { - MouseState.SetScrollRelative(dx, dy); - OnMouseWheel(); + OnMouseWheel(dx, dy); } } break; @@ -559,8 +547,7 @@ namespace OpenTK.Platform.MacOS case NSEventType.OtherMouseDown: { var buttonNumber = Cocoa.SendInt(e, selButtonNumber); - MouseState[GetMouseButton(buttonNumber)] = true; - OnMouseDown(); + OnMouseDown(GetMouseButton(buttonNumber)); } break; @@ -569,8 +556,7 @@ namespace OpenTK.Platform.MacOS case NSEventType.OtherMouseUp: { var buttonNumber = Cocoa.SendInt(e, selButtonNumber); - MouseState[GetMouseButton(buttonNumber)] = false; - OnMouseUp(); + OnMouseUp(GetMouseButton(buttonNumber)); } break; } diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index 64a185ce..28a061c5 100755 --- a/Source/OpenTK/Platform/MacOS/HIDInput.cs +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -390,7 +390,8 @@ namespace OpenTK.Platform.MacOS { Debug.Print("[Warning] Key {0} not mapped.", usage); } - keyboard.State.SetKeyState(RawKeyMap[usage], (byte)usage, v_int != 0); + + keyboard.State[RawKeyMap[usage]] = v_int != 0; break; } } diff --git a/Source/OpenTK/Platform/NativeWindowBase.cs b/Source/OpenTK/Platform/NativeWindowBase.cs index da7e7b08..6827d976 100644 --- a/Source/OpenTK/Platform/NativeWindowBase.cs +++ b/Source/OpenTK/Platform/NativeWindowBase.cs @@ -45,19 +45,22 @@ namespace OpenTK.Platform readonly MouseMoveEventArgs MouseMoveArgs = new MouseMoveEventArgs(); readonly MouseWheelEventArgs MouseWheelArgs = new MouseWheelEventArgs(); - protected readonly KeyboardKeyEventArgs KeyDownArgs = new KeyboardKeyEventArgs(); - protected readonly KeyboardKeyEventArgs KeyUpArgs = new KeyboardKeyEventArgs(); - protected readonly KeyPressEventArgs KeyPressArgs = new KeyPressEventArgs((char)0); + readonly KeyboardKeyEventArgs KeyDownArgs = new KeyboardKeyEventArgs(); + readonly KeyboardKeyEventArgs KeyUpArgs = new KeyboardKeyEventArgs(); + readonly KeyPressEventArgs KeyPressArgs = new KeyPressEventArgs((char)0); // In order to simplify mouse event implementation, // we can store the current mouse state here. protected MouseState MouseState = new MouseState(); + protected KeyboardState KeyboardState = new KeyboardState(); + MouseState PreviousMouseState = new MouseState(); internal NativeWindowBase() { LegacyInputDriver = new LegacyInputDriver(this); MouseState.SetIsConnected(true); + KeyboardState.SetIsConnected(true); PreviousMouseState.SetIsConnected(true); } @@ -118,18 +121,30 @@ namespace OpenTK.Platform WindowStateChanged(this, e); } - protected void OnKeyDown(KeyboardKeyEventArgs e) + protected void OnKeyDown(Key key) { + KeyboardState.SetKeyState(key, true); + + var e = KeyDownArgs; + e.Keyboard = KeyboardState; + e.Key = key; KeyDown(this, e); } - protected void OnKeyPress(KeyPressEventArgs e) + protected void OnKeyPress(char c) { + var e = KeyPressArgs; + e.KeyChar = c; KeyPress(this, e); } - protected void OnKeyUp(KeyboardKeyEventArgs e) + protected void OnKeyUp(Key key) { + KeyboardState.SetKeyState(key, false); + + var e = KeyUpArgs; + e.Keyboard = KeyboardState; + e.Key = key; KeyUp(this, e); } @@ -143,48 +158,31 @@ namespace OpenTK.Platform MouseEnter(this, e); } - protected void OnMouseDown() + protected void OnMouseDown(MouseButton button) { + MouseState[button] = true; + var e = MouseDownArgs; e.Mouse = MouseState; - // Find which button caused this event - for (MouseButton b = MouseButton.Left; b < MouseButton.LastButton; b++) - { - if (!PreviousMouseState[b] && MouseState[b]) - { - e.Button = b; - PreviousMouseState = MouseState; - MouseDown(this, e); - return; - } - } - - Debug.WriteLine("OnMouseDown called without pressing a button"); + MouseDown(this, e); } - protected void OnMouseUp() + protected void OnMouseUp(MouseButton button) { + MouseState[button] = false; + var e = MouseUpArgs; e.Mouse = MouseState; - // Find which button caused this event - for (MouseButton b = MouseButton.Left; b < MouseButton.LastButton; b++) - { - if (PreviousMouseState[b] && !MouseState[b]) - { - e.Button = b; - PreviousMouseState = MouseState; - MouseUp(this, e); - return; - } - } - - Debug.WriteLine("OnMouseUp called without pressing a button"); + MouseUp(this, e); } - protected void OnMouseMove() + protected void OnMouseMove(int x, int y) { + MouseState.X = x; + MouseState.Y = y; + var e = MouseMoveArgs; e.Mouse = MouseState; e.XDelta = MouseState.X - PreviousMouseState.X; @@ -199,14 +197,15 @@ namespace OpenTK.Platform MouseMove(this, e); } - protected void OnMouseWheel() + protected void OnMouseWheel(float dx, float dy) { + MouseState.SetScrollRelative(dx, dy); + var e = MouseWheelArgs; e.Mouse = MouseState; - e.ValuePrecise = MouseState.Scroll.Y; e.DeltaPrecise = MouseState.Scroll.Y - PreviousMouseState.Scroll.Y; - if (e.DeltaPrecise == 0) + if (dx == 0 && dy == 0) { Debug.WriteLine("OnMouseWheel called without moving the mouse wheel."); } diff --git a/Source/OpenTK/Platform/SDL2/Sdl2Keyboard.cs b/Source/OpenTK/Platform/SDL2/Sdl2Keyboard.cs index 21c7d85a..d58c5f73 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2Keyboard.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2Keyboard.cs @@ -58,16 +58,16 @@ namespace OpenTK.Platform.SDL2 { Keymod mod = SDL.GetModState(); - state.SetKeyState(Key.LAlt, (byte)Scancode.LALT, (mod & Keymod.LALT) != 0); - state.SetKeyState(Key.RAlt, (byte)Scancode.RALT, (mod & Keymod.RALT) != 0); - state.SetKeyState(Key.LControl, (byte)Scancode.LCTRL, (mod & Keymod.LCTRL) != 0); - state.SetKeyState(Key.RControl, (byte)Scancode.RCTRL, (mod & Keymod.CTRL) != 0); - state.SetKeyState(Key.LShift, (byte)Scancode.LSHIFT, (mod & Keymod.LSHIFT) != 0); - state.SetKeyState(Key.RShift, (byte)Scancode.RSHIFT, (mod & Keymod.RSHIFT) != 0); - state.SetKeyState(Key.Menu, (byte)Scancode.APPLICATION, (mod & Keymod.GUI) != 0); - state.SetKeyState(Key.CapsLock, (byte)Scancode.CAPSLOCK, (mod & Keymod.CAPS) != 0); - state.SetKeyState(Key.NumLock, (byte)Scancode.NUMLOCKCLEAR, (mod & Keymod.NUM) != 0); - //state.SetKeyState(Key., (byte)Scancode.MODE, (mod & Keymod.MODE) != 0); + state[Key.LAlt] = (mod & Keymod.LALT) != 0; + state[Key.RAlt] = (mod & Keymod.RALT) != 0; + state[Key.LControl] = (mod & Keymod.LCTRL) != 0; + state[Key.RControl] = (mod & Keymod.RCTRL) != 0; + state[Key.LShift] = (mod & Keymod.LSHIFT) != 0; + state[Key.RShift] = (mod & Keymod.RSHIFT) != 0; + state[Key.Menu] = (mod & Keymod.GUI) != 0; + state[Key.CapsLock] = (mod & Keymod.CAPS) != 0; + state[Key.NumLock] = (mod & Keymod.NUM) != 0; + //state[Key.] = (mod & Keymod.MODE) != 0; } #endregion @@ -83,7 +83,7 @@ namespace OpenTK.Platform.SDL2 if (key != Key.Unknown) { - state.SetKeyState(key, (byte)scancode, pressed); + state[key] = pressed; } } diff --git a/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs b/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs index 1283e4c1..dfc38e30 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs @@ -66,12 +66,6 @@ namespace OpenTK.Platform.SDL2 // to .Net UTF16 strings char[] DecodeTextBuffer = new char[32]; - // Argument for KeyPress event (allocated once to avoid runtime allocations) - readonly KeyPressEventArgs keypress_args = new KeyPressEventArgs('\0'); - - // Argument for KeyDown and KeyUp events (allocated once to avoid runtime allocations) - readonly KeyboardKeyEventArgs key_args = new KeyboardKeyEventArgs(); - static readonly Dictionary windows = new Dictionary(); @@ -221,36 +215,29 @@ namespace OpenTK.Platform.SDL2 button_pressed ? true : false); } - window.MouseState[Sdl2Mouse.TranslateButton(ev.Button)] = button_pressed; - window.MouseState.X = ev.X; - window.MouseState.Y = ev.Y; - + MouseButton button = Sdl2Mouse.TranslateButton(ev.Button); if (button_pressed) { - window.OnMouseDown(); + window.OnMouseDown(button); } else { - window.OnMouseUp(); + window.OnMouseUp(button); } } static void ProcessKeyEvent(Sdl2NativeWindow window, Event ev) { bool key_pressed = ev.Key.State == State.Pressed; - var key = ev.Key.Keysym; - window.key_args.Key = TranslateKey(key.Scancode); - window.key_args.ScanCode = (uint)key.Scancode; - window.key_args.Modifiers = Sdl2KeyMap.GetModifiers(key.Mod); + Key key = TranslateKey(ev.Key.Keysym.Scancode); if (key_pressed) { - window.OnKeyDown(window.key_args); + window.OnKeyDown(key); } else { - window.OnKeyUp(window.key_args); + window.OnKeyUp(key); } - //window.keyboard.SetKey(TranslateKey(key.scancode), (uint)key.scancode, key_pressed); } static unsafe void ProcessTextInputEvent(Sdl2NativeWindow window, TextInputEvent ev) @@ -281,23 +268,19 @@ namespace OpenTK.Platform.SDL2 for (int i = 0; i < decoded_length; i++) { - window.keypress_args.KeyChar = window.DecodeTextBuffer[i]; - window.OnKeyPress(window.keypress_args); + window.OnKeyPress(window.DecodeTextBuffer[i]); } } static void ProcessMouseMotionEvent(Sdl2NativeWindow window, MouseMotionEvent ev) { //float scale = window.ClientSize.Width / (float)window.Size.Width; - window.MouseState.X = ev.X; - window.MouseState.Y = ev.Y; - window.OnMouseMove(); + window.OnMouseMove(ev.X, ev.Y); } static void ProcessMouseWheelEvent(Sdl2NativeWindow window, MouseWheelEvent ev) { - window.MouseState.SetScrollRelative(ev.X, ev.Y); - window.OnMouseWheel(); + window.OnMouseWheel(ev.X, ev.Y); } static void ProcessWindowEvent(Sdl2NativeWindow window, WindowEvent e) diff --git a/Source/OpenTK/Platform/Windows/WinGLNative.cs b/Source/OpenTK/Platform/Windows/WinGLNative.cs index fdf11665..6b377cb8 100644 --- a/Source/OpenTK/Platform/Windows/WinGLNative.cs +++ b/Source/OpenTK/Platform/Windows/WinGLNative.cs @@ -393,8 +393,7 @@ namespace OpenTK.Platform.Windows if (!Char.IsControl(c)) { - KeyPressArgs.KeyChar = c; - OnKeyPress(KeyPressArgs); + OnKeyPress(c); } } @@ -437,9 +436,7 @@ namespace OpenTK.Platform.Windows if (points == 0 || (points == -1 && lastError == Constants.ERROR_POINT_NOT_FOUND)) { // Just use the mouse move position - MouseState.X = point.X; - MouseState.Y = point.Y; - OnMouseMove(); + OnMouseMove(point.X, point.Y); } else if (points == -1) { @@ -477,9 +474,7 @@ namespace OpenTK.Platform.Windows position.Y -= 65536; } Functions.ScreenToClient(handle, ref position); - MouseState.X = position.X; - MouseState.Y = position.Y; - OnMouseMove(); + OnMouseMove(position.X, position.Y); } } mouse_last_timestamp = timestamp; @@ -508,37 +503,32 @@ namespace OpenTK.Platform.Windows { // This is due to inconsistent behavior of the WParam value on 64bit arch, whese // wparam = 0xffffffffff880000 or wparam = 0x00000000ff100000 - MouseState.SetScrollRelative(0, ((long)wParam << 32 >> 48) / 120.0f); - OnMouseWheel(); + OnMouseWheel(0, ((long)wParam << 32 >> 48) / 120.0f); } void HandleMouseHWheel(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { // This is due to inconsistent behavior of the WParam value on 64bit arch, whese // wparam = 0xffffffffff880000 or wparam = 0x00000000ff100000 - MouseState.SetScrollRelative(((long)wParam << 32 >> 48) / 120.0f, 0); - OnMouseWheel(); + OnMouseWheel(((long)wParam << 32 >> 48) / 120.0f, 0); } void HandleLButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { Functions.SetCapture(window.Handle); - MouseState[MouseButton.Left] = true; - OnMouseDown(); + OnMouseDown(MouseButton.Left); } void HandleMButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { Functions.SetCapture(window.Handle); - MouseState[MouseButton.Middle] = true; - OnMouseDown(); + OnMouseDown(MouseButton.Middle); } void HandleRButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { Functions.SetCapture(window.Handle); - MouseState[MouseButton.Right] = true; - OnMouseDown(); + OnMouseDown(MouseButton.Right); } void HandleXButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) @@ -547,29 +537,25 @@ namespace OpenTK.Platform.Windows MouseButton button = ((wParam.ToInt32() & 0xFFFF0000) >> 16) == 1 ? MouseButton.Button1 : MouseButton.Button2; - MouseState[button] = true; - OnMouseDown(); + OnMouseDown(button); } void HandleLButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { Functions.ReleaseCapture(); - MouseState[MouseButton.Left] = false; - OnMouseUp(); + OnMouseUp(MouseButton.Left); } void HandleMButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { Functions.ReleaseCapture(); - MouseState[MouseButton.Middle] = false; - OnMouseUp(); + OnMouseUp(MouseButton.Middle); } void HandleRButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { Functions.ReleaseCapture(); - MouseState[MouseButton.Right] = false; - OnMouseUp(); + OnMouseUp(MouseButton.Right); } void HandleXButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) @@ -578,8 +564,7 @@ namespace OpenTK.Platform.Windows MouseButton button = ((wParam.ToInt32() & 0xFFFF0000) >> 16) == 1 ? MouseButton.Button1 : MouseButton.Button2; - MouseState[button] = false; - OnMouseUp(); + OnMouseUp(button); } void HandleKeyboard(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) @@ -605,15 +590,11 @@ namespace OpenTK.Platform.Windows { if (pressed) { - KeyDownArgs.Key = key; - KeyDownArgs.Modifiers = InputDriver.Keyboard[0].GetModifiers(); - OnKeyDown(KeyDownArgs); + OnKeyDown(key); } else { - KeyUpArgs.Key = key; - KeyUpArgs.Modifiers = InputDriver.Keyboard[0].GetModifiers(); - OnKeyUp(KeyUpArgs); + OnKeyUp(key); } } } diff --git a/Source/OpenTK/Platform/Windows/WinRawKeyboard.cs b/Source/OpenTK/Platform/Windows/WinRawKeyboard.cs index e3312d32..fcfc4a6b 100644 --- a/Source/OpenTK/Platform/Windows/WinRawKeyboard.cs +++ b/Source/OpenTK/Platform/Windows/WinRawKeyboard.cs @@ -188,7 +188,7 @@ namespace OpenTK.Platform.Windows if (is_valid) { - keyboard.SetKeyState(key, (byte)scancode, pressed); + keyboard.SetKeyState(key, pressed); processed = true; } diff --git a/Source/OpenTK/Platform/X11/X11GLNative.cs b/Source/OpenTK/Platform/X11/X11GLNative.cs index f52ea305..7061e21e 100644 --- a/Source/OpenTK/Platform/X11/X11GLNative.cs +++ b/Source/OpenTK/Platform/X11/X11GLNative.cs @@ -763,18 +763,6 @@ namespace OpenTK.Platform.X11 return cursor; } - void SetMouseClamped(int x, int y, - int left, int top, int width, int height) - { - // Clamp mouse to the specified rectangle. - x = Math.Max(x, left); - x = Math.Min(x, width); - y = Math.Max(y, top); - y = Math.Min(y, height); - MouseState.X = x; - MouseState.Y = y; - } - #endregion #region INativeWindow Members @@ -861,18 +849,12 @@ namespace OpenTK.Platform.X11 if (pressed) { // Raise KeyDown event - KeyDownArgs.Key = key; - KeyDownArgs.ScanCode = (uint)e.KeyEvent.keycode; - //KeyDownArgs.Modifiers = keyboard.GetModifiers(); - OnKeyDown(KeyDownArgs); + OnKeyDown(key); } else { // Raise KeyUp event - KeyUpArgs.Key = key; - KeyUpArgs.ScanCode = (uint)e.KeyEvent.keycode; - //KeyUpArgs.Modifiers = keyboard.GetModifiers(); - OnKeyUp(KeyUpArgs); + OnKeyUp(key); } if (pressed) @@ -888,8 +870,7 @@ namespace OpenTK.Platform.X11 { if (!Char.IsControl(chars[i])) { - KeyPressArgs.KeyChar = chars[i]; - OnKeyPress(KeyPressArgs); + OnKeyPress(chars[i]); } } } @@ -922,10 +903,9 @@ namespace OpenTK.Platform.X11 } else if (!CursorVisible) { - SetMouseClamped( - MouseState.X + x - mouse_rel_x, - MouseState.Y + y - mouse_rel_y, - 0, 0, Width, Height); + OnMouseMove( + MathHelper.Clamp(MouseState.X + x - mouse_rel_x, 0, Width), + MathHelper.Clamp(MouseState.Y + y - mouse_rel_y, 0, Height)); mouse_rel_x = x; mouse_rel_y = y; @@ -935,53 +915,42 @@ namespace OpenTK.Platform.X11 } else { - SetMouseClamped(x, y, 0, 0, Width, Height); + OnMouseMove( + MathHelper.Clamp(x, 0, Width), + MathHelper.Clamp(y, 0, Height)); mouse_rel_x = x; mouse_rel_y = y; } - OnMouseMove(); break; } case XEventName.ButtonPress: - switch (e.ButtonEvent.button) { - case 1: MouseState.EnableBit((int)MouseButton.Left); break; - case 2: MouseState.EnableBit((int)MouseButton.Middle); break; - case 3: MouseState.EnableBit((int)MouseButton.Right); break; - case 4: MouseState.SetScrollRelative(0, 1); break; - case 5: MouseState.SetScrollRelative(0, -1); break; - case 6: MouseState.EnableBit((int)MouseButton.Button1); break; - case 7: MouseState.EnableBit((int)MouseButton.Button2); break; - case 8: MouseState.EnableBit((int)MouseButton.Button3); break; - case 9: MouseState.EnableBit((int)MouseButton.Button4); break; - case 10: MouseState.EnableBit((int)MouseButton.Button5); break; - case 11: MouseState.EnableBit((int)MouseButton.Button6); break; - case 12: MouseState.EnableBit((int)MouseButton.Button7); break; - case 13: MouseState.EnableBit((int)MouseButton.Button8); break; - case 14: MouseState.EnableBit((int)MouseButton.Button9); break; + int dx, dy; + MouseButton button = X11KeyMap.TranslateButton(e.ButtonEvent.button, out dx, out dy); + + if (button != MouseButton.LastButton) + { + OnMouseDown(button); + } + else if (dx != 0 || dy != 0) + { + OnMouseWheel(dx, dy); + } } - OnMouseDown(); break; case XEventName.ButtonRelease: - switch (e.ButtonEvent.button) { - case 1: MouseState.DisableBit((int)MouseButton.Left); break; - case 2: MouseState.DisableBit((int)MouseButton.Middle); break; - case 3: MouseState.DisableBit((int)MouseButton.Right); break; - case 6: MouseState.DisableBit((int)MouseButton.Button1); break; - case 7: MouseState.DisableBit((int)MouseButton.Button2); break; - case 8: MouseState.DisableBit((int)MouseButton.Button3); break; - case 9: MouseState.DisableBit((int)MouseButton.Button4); break; - case 10: MouseState.DisableBit((int)MouseButton.Button5); break; - case 11: MouseState.DisableBit((int)MouseButton.Button6); break; - case 12: MouseState.DisableBit((int)MouseButton.Button7); break; - case 13: MouseState.DisableBit((int)MouseButton.Button8); break; - case 14: MouseState.DisableBit((int)MouseButton.Button9); break; + int dx, dy; + MouseButton button = X11KeyMap.TranslateButton(e.ButtonEvent.button, out dx, out dy); + + if (button != MouseButton.LastButton) + { + OnMouseUp(button); + } } - OnMouseUp(); break; case XEventName.FocusIn: From 8b7d5bc7e492c688ed6183e0df4408f64b31dde0 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 5 May 2014 00:43:45 +0200 Subject: [PATCH 30/51] [Input] Implement key repeat --- Source/OpenTK/Input/KeyboardKeyEventArgs.cs | 21 ++++-- .../Platform/MacOS/CocoaNativeWindow.cs | 5 +- Source/OpenTK/Platform/NativeWindowBase.cs | 4 +- .../OpenTK/Platform/SDL2/Sdl2NativeWindow.cs | 2 +- Source/OpenTK/Platform/Windows/WinGLNative.cs | 5 +- Source/OpenTK/Platform/X11/Structs.cs | 30 ++++++++- Source/OpenTK/Platform/X11/X11GLNative.cs | 64 ++++++++++++++++++- Source/OpenTK/Platform/X11/XI2Mouse.cs | 3 +- 8 files changed, 119 insertions(+), 15 deletions(-) diff --git a/Source/OpenTK/Input/KeyboardKeyEventArgs.cs b/Source/OpenTK/Input/KeyboardKeyEventArgs.cs index a22afe8e..614a8f95 100644 --- a/Source/OpenTK/Input/KeyboardKeyEventArgs.cs +++ b/Source/OpenTK/Input/KeyboardKeyEventArgs.cs @@ -46,8 +46,8 @@ namespace OpenTK.Input #region Fields Key key; + bool repeat; KeyboardState state; - uint scancode; #endregion @@ -65,7 +65,6 @@ namespace OpenTK.Input public KeyboardKeyEventArgs(KeyboardKeyEventArgs args) { Key = args.Key; - ScanCode = args.ScanCode; } #endregion @@ -87,8 +86,7 @@ namespace OpenTK.Input [CLSCompliant(false)] public uint ScanCode { - get { return scancode; } - internal set { scancode = value; } + get { return (uint)Key; } } /// @@ -145,6 +143,21 @@ namespace OpenTK.Input internal set { state = value; } } + /// + /// Gets a indicating whether + /// this key event is a repeat. + /// + /// + /// true, if this event was caused by the user holding down + /// a key; false, if this was caused by the user pressing a + /// key for the first time. + /// + public bool IsRepeat + { + get { return repeat; } + internal set { repeat = value; } + } + #endregion } } diff --git a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs index 3697f1bf..5372e7b2 100644 --- a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs +++ b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs @@ -416,10 +416,7 @@ namespace OpenTK.Platform.MacOS //GetKey(keyCode, modifierFlags, keyArgs); Key key = MacOSKeyMap.GetKey(keyCode); - if (!isARepeat || InputDriver.Keyboard[0].KeyRepeat) - { - OnKeyDown(key); - } + OnKeyDown(key, isARepeat); var s = Cocoa.FromNSString(Cocoa.SendIntPtr(e, selCharactersIgnoringModifiers)); foreach (var c in s) diff --git a/Source/OpenTK/Platform/NativeWindowBase.cs b/Source/OpenTK/Platform/NativeWindowBase.cs index 6827d976..47013810 100644 --- a/Source/OpenTK/Platform/NativeWindowBase.cs +++ b/Source/OpenTK/Platform/NativeWindowBase.cs @@ -121,13 +121,14 @@ namespace OpenTK.Platform WindowStateChanged(this, e); } - protected void OnKeyDown(Key key) + protected void OnKeyDown(Key key, bool repeat) { KeyboardState.SetKeyState(key, true); var e = KeyDownArgs; e.Keyboard = KeyboardState; e.Key = key; + e.IsRepeat = repeat; KeyDown(this, e); } @@ -145,6 +146,7 @@ namespace OpenTK.Platform var e = KeyUpArgs; e.Keyboard = KeyboardState; e.Key = key; + e.IsRepeat = false; KeyUp(this, e); } diff --git a/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs b/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs index dfc38e30..dc232663 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs @@ -232,7 +232,7 @@ namespace OpenTK.Platform.SDL2 Key key = TranslateKey(ev.Key.Keysym.Scancode); if (key_pressed) { - window.OnKeyDown(key); + window.OnKeyDown(key, ev.Key.Repeat > 0); } else { diff --git a/Source/OpenTK/Platform/Windows/WinGLNative.cs b/Source/OpenTK/Platform/Windows/WinGLNative.cs index 6b377cb8..f3661a19 100644 --- a/Source/OpenTK/Platform/Windows/WinGLNative.cs +++ b/Source/OpenTK/Platform/Windows/WinGLNative.cs @@ -581,7 +581,8 @@ namespace OpenTK.Platform.Windows // In this case, both keys will be reported as pressed. bool extended = (lParam.ToInt64() & ExtendedBit) != 0; - short scancode = (short)((lParam.ToInt64() >> 16) & 0xFF); + short scancode = (short)((lParam.ToInt64() >> 16) & 0xff); + ushort repeat_count = unchecked((ushort)((ulong)lParam.ToInt64() & 0xffffu)); VirtualKeys vkey = (VirtualKeys)wParam; bool is_valid; Key key = WinKeyMap.TranslateKey(scancode, vkey, extended, false, out is_valid); @@ -590,7 +591,7 @@ namespace OpenTK.Platform.Windows { if (pressed) { - OnKeyDown(key); + OnKeyDown(key, repeat_count > 0); } else { diff --git a/Source/OpenTK/Platform/X11/Structs.cs b/Source/OpenTK/Platform/X11/Structs.cs index 3deefa1a..68622733 100644 --- a/Source/OpenTK/Platform/X11/Structs.cs +++ b/Source/OpenTK/Platform/X11/Structs.cs @@ -1707,7 +1707,7 @@ namespace OpenTK.Platform.X11 public double root_y; public double event_x; public double event_y; - public int flags; + public XIEventFlags flags; public XIButtonState buttons; public XIValuatorState valuators; public XIModifierState mods; @@ -1828,4 +1828,32 @@ namespace OpenTK.Platform.X11 RawButtonReleaseMask = (1 << (int)XIEventType.RawButtonRelease), RawMotionMask = (1 << (int)XIEventType.RawMotion), } + + [Flags] + enum XIKeyEventFlags + { + Repeat = (1 << 16), + } + + [Flags] + enum XIPointerEventFlags + { + Emulated = (1 << 16), + } + + [Flags] + enum XITouchEventFlags + { + PendingEnd = (1 << 16), + EmulatingPointer = (1 << 17), + } + + [Flags] + enum XIEventFlags + { + KeyRepeat = XIKeyEventFlags.Repeat, + PointerEmulated = XIPointerEventFlags.Emulated, + TouchPendingEnd = XITouchEventFlags.PendingEnd, + TouchEmulatingPointer = XITouchEventFlags.EmulatingPointer + } } diff --git a/Source/OpenTK/Platform/X11/X11GLNative.cs b/Source/OpenTK/Platform/X11/X11GLNative.cs index 7061e21e..447e415d 100644 --- a/Source/OpenTK/Platform/X11/X11GLNative.cs +++ b/Source/OpenTK/Platform/X11/X11GLNative.cs @@ -124,6 +124,9 @@ namespace OpenTK.Platform.X11 public static bool MouseWarpActive = false; + readonly bool xi2_supported; + readonly int xi2_opcode; + #endregion #region Constructors @@ -228,6 +231,14 @@ namespace OpenTK.Platform.X11 bool supported; Functions.XkbSetDetectableAutoRepeat(window.Display, true, out supported); + // The XInput2 extension makes keyboard and mouse handling much easier. + // Check whether it is available. + xi2_supported = XI2Mouse.IsSupported(window.Display); + if (xi2_supported) + { + xi2_opcode = XI2Mouse.XIOpCode; + } + exists = true; } @@ -848,8 +859,41 @@ namespace OpenTK.Platform.X11 { if (pressed) { + // Check if this is a key repeat event. + // X11 does not provide this information, + // so we rely on the XInput2 extension for that. + // Todo: hack this when XInput2 is not available + // by checking if another KeyPress event is enqueued. + bool is_repeat = false; + if (xi2_supported && e.GenericEventCookie.extension == xi2_opcode) + { + if (e.GenericEventCookie.evtype == (int)XIEventType.KeyPress) + { + unsafe + { + XIDeviceEvent* xi = (XIDeviceEvent*)e.GenericEventCookie.data; + is_repeat = (xi->flags & XIEventFlags.KeyRepeat) != 0; + } + } + } + else + { + if (API.Pending(window.Display) > 0) + { + unsafe + { + XEvent dummy = new XEvent(); + KeyRepeatTestData arg = new KeyRepeatTestData(); + arg.Event = e; + API.CheckIfEvent(window.Display, ref dummy, IsKeyRepeatPredicate, + new IntPtr(&arg)); + is_repeat = arg.IsRepeat; + } + } + } + // Raise KeyDown event - OnKeyDown(key); + OnKeyDown(key, is_repeat); } else { @@ -1010,6 +1054,24 @@ namespace OpenTK.Platform.X11 } } + struct KeyRepeatTestData + { + public XEvent Event; + public bool IsRepeat; + } + + unsafe static bool IsKeyRepeatPredicate(IntPtr display, ref XEvent e, IntPtr arg) + { + // IsRepeat is true when the event queue contains an identical + // KeyPress event at later time no greater than 2. + KeyRepeatTestData* data = (KeyRepeatTestData*)arg; + data->IsRepeat = + e.type == XEventName.KeyPress && + e.KeyEvent.keycode == data->Event.KeyEvent.keycode && + e.KeyEvent.time.ToInt64() - data->Event.KeyEvent.time.ToInt64() < 2; + return false; // keep the event in the queue + } + #endregion #region Bounds diff --git a/Source/OpenTK/Platform/X11/XI2Mouse.cs b/Source/OpenTK/Platform/X11/XI2Mouse.cs index 96090e55..893783b0 100644 --- a/Source/OpenTK/Platform/X11/XI2Mouse.cs +++ b/Source/OpenTK/Platform/X11/XI2Mouse.cs @@ -40,7 +40,8 @@ namespace OpenTK.Platform.X11 List mice = new List(); Dictionary rawids = new Dictionary(); // maps raw ids to mouse ids internal readonly X11WindowInfo window; - static int XIOpCode; + internal static int XIOpCode { get; private set; } + static bool supported; static readonly Functions.EventPredicate PredicateImpl = IsEventValid; readonly IntPtr Predicate = Marshal.GetFunctionPointerForDelegate(PredicateImpl); From b53d4a6554fb5aaed867c5c110ced484764109cf Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 5 May 2014 00:43:58 +0200 Subject: [PATCH 31/51] [Examples] Test key events for consistency --- .../Examples/OpenTK/Test/GameWindowStates.cs | 115 ++++++++++++++++-- 1 file changed, 107 insertions(+), 8 deletions(-) diff --git a/Source/Examples/OpenTK/Test/GameWindowStates.cs b/Source/Examples/OpenTK/Test/GameWindowStates.cs index d3d3fe26..28da31fa 100644 --- a/Source/Examples/OpenTK/Test/GameWindowStates.cs +++ b/Source/Examples/OpenTK/Test/GameWindowStates.cs @@ -26,16 +26,26 @@ namespace Examples.Tests bool mouse_in_window = false; bool viewport_changed = true; - // legacy NativeWindow.MouseDevice events + // legacy GameWindow.Mouse.* events Vector4 mousedevice_pos; int mousedevice_buttons; MouseState mousedevice_state; - // new NativeWindow.Mouse* events + // new GameWindow.Mouse* events Vector4 mouse_pos; int mouse_buttons; MouseState mouse_state; + // legacy GameWindow.Keyboard.Key* events + Dictionary legacy_keyboard_keys = new Dictionary(); + KeyboardState legacy_keyboard_state; + KeyModifiers legacy_keyboard_modifiers; + + //new GameWindow.Key* events + Dictionary keyboard_keys = new Dictionary(); + KeyboardState keyboard_state; + KeyModifiers keyboard_modifiers; + // time drift Stopwatch watch = new Stopwatch(); double update_time, render_time; @@ -52,8 +62,6 @@ namespace Examples.Tests double variable_refresh_timestep_pos = -1; double fixed_update_timestep_pos = -1; - KeyModifiers modifiers; - public GameWindowStates() : base(800, 600, GraphicsMode.Default) { @@ -62,6 +70,8 @@ namespace Examples.Tests KeyDown += KeyDownHandler; KeyUp += KeyUpHandler; KeyPress += KeyPressHandler; + Keyboard.KeyDown += KeyboardDeviceDownHandler; + Keyboard.KeyUp += KeyboardDeviceUpHandler; MouseEnter += delegate { mouse_in_window = true; }; MouseLeave += delegate { mouse_in_window = false; }; @@ -77,7 +87,9 @@ namespace Examples.Tests MouseUp += MouseButtonHandler; } - private void KeyPressHandler(object sender, KeyPressEventArgs e) + #region Keyboard Events + + void KeyPressHandler(object sender, KeyPressEventArgs e) { if (TypedText.Length > 32) TypedText.Remove(0, 1); @@ -125,14 +137,43 @@ namespace Examples.Tests case Key.Comma: TargetRenderFrequency--; break; case Key.Period: TargetRenderFrequency++; break; } - modifiers = e.Modifiers; + + if (!keyboard_keys.ContainsKey(e.Key)) + { + keyboard_keys.Add(e.Key, 0); + } + keyboard_keys[e.Key] = e.IsRepeat ? 1 : 0; + keyboard_modifiers = e.Modifiers; + keyboard_state = e.Keyboard; } void KeyUpHandler(object sender, KeyboardKeyEventArgs e) { - modifiers = e.Modifiers; + keyboard_keys.Remove(e.Key); + keyboard_modifiers = e.Modifiers; + keyboard_state = e.Keyboard; } + void KeyboardDeviceDownHandler(object sender, KeyboardKeyEventArgs e) + { + if (!legacy_keyboard_keys.ContainsKey(e.Key)) + { + legacy_keyboard_keys.Add(e.Key, 0); + } + legacy_keyboard_keys[e.Key] = e.IsRepeat ? 1 : 0; + legacy_keyboard_modifiers = e.Modifiers; + legacy_keyboard_state = e.Keyboard; + } + + void KeyboardDeviceUpHandler(object sender, KeyboardKeyEventArgs e) + { + legacy_keyboard_keys.Remove(e.Key); + legacy_keyboard_modifiers = e.Modifiers; + legacy_keyboard_state = e.Keyboard; + } + + #endregion + #region MouseDevice events void MouseDeviceMoveHandler(object sender, MouseMoveEventArgs e) @@ -209,6 +250,8 @@ namespace Examples.Tests #endregion + #region Private Members + static int Clamp(int val, int min, int max) { return val > max ? max : val < min ? min : val; @@ -228,7 +271,7 @@ namespace Examples.Tests int DrawKeyboards(Graphics gfx, int line) { line++; - DrawString(gfx, String.Format("Keyboard ({0}):", modifiers), line++); + DrawString(gfx, "Keyboard:", line++); for (int i = 0; i < 4; i++) { var state = OpenTK.Input.Keyboard.GetState(i); @@ -281,6 +324,59 @@ namespace Examples.Tests return line; } + int DrawKeyboardDevice(Graphics gfx, int line) + { + StringBuilder sb = new StringBuilder(); + sb.Append("KeyboardDevice: "); + for (Key key = 0; key < Key.LastKey; key++) + { + if (Keyboard[key]) + { + sb.Append(key); + sb.Append(" "); + } + } + DrawString(gfx, sb.ToString(), line++); + + sb.Remove(0, sb.Length); + sb.Append("KeyboardDevice events: ["); + sb.Append(legacy_keyboard_modifiers); + sb.Append("] "); + foreach (var pair in legacy_keyboard_keys) + { + sb.Append(pair.Key); + sb.Append(":"); + sb.Append(pair.Value); + sb.Append(" "); + } + DrawString(gfx, sb.ToString(), line++); + + sb.Remove(0, sb.Length); + sb.Append("KeyboardDevice state: "); + sb.Append(legacy_keyboard_state); + DrawString(gfx, sb.ToString(), line++); + + sb.Remove(0, sb.Length); + sb.Append("Keyboard events: ["); + sb.Append(keyboard_modifiers); + sb.Append("] "); + foreach (var pair in keyboard_keys) + { + sb.Append(pair.Key); + sb.Append(":"); + sb.Append(pair.Value); + sb.Append(" "); + } + DrawString(gfx, sb.ToString(), line++); + + sb.Remove(0, sb.Length); + sb.Append("Keyboard state: "); + sb.Append(keyboard_state); + DrawString(gfx, sb.ToString(), line++); + + return line; + } + int DrawMouseDevice(Graphics gfx, int line) { StringBuilder sb = new StringBuilder(); @@ -369,6 +465,8 @@ namespace Examples.Tests return line; } + #endregion + protected override void OnUpdateFrame(FrameEventArgs e) { double clock_time = watch.Elapsed.TotalSeconds; @@ -401,6 +499,7 @@ namespace Examples.Tests CursorVisible ? "visible" : "hidden", Focused ? "Focused" : "Not focused"), line++); + line = DrawKeyboardDevice(gfx, line); line = DrawMouseDevice(gfx, line); // Timing information From 2e9ff4d8b96d2ac16e95403f4eee38c3ae6ee48b Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 5 May 2014 00:56:19 +0200 Subject: [PATCH 32/51] [Examples] Print KeyboardState --- .../Examples/OpenTK/Test/GameWindowStates.cs | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/Source/Examples/OpenTK/Test/GameWindowStates.cs b/Source/Examples/OpenTK/Test/GameWindowStates.cs index 28da31fa..12f47e9e 100644 --- a/Source/Examples/OpenTK/Test/GameWindowStates.cs +++ b/Source/Examples/OpenTK/Test/GameWindowStates.cs @@ -268,6 +268,19 @@ namespace Examples.Tests return offset + gfx.MeasureString(str, TextFont).Width; } + static void KeyboardStateToString(KeyboardState state, StringBuilder sb) + { + for (int key_index = 0; key_index < (int)Key.LastKey; key_index++) + { + Key k = (Key)key_index; + if (state[k]) + { + sb.Append(k); + sb.Append(" "); + } + } + } + int DrawKeyboards(Graphics gfx, int line) { line++; @@ -280,15 +293,7 @@ namespace Examples.Tests StringBuilder sb = new StringBuilder(); sb.Append(i); sb.Append(": "); - for (int key_index = 0; key_index < (int)Key.LastKey; key_index++) - { - Key k = (Key)key_index; - if (state[k]) - { - sb.Append(k); - sb.Append(" "); - } - } + KeyboardStateToString(state, sb); DrawString(gfx, sb.ToString(), line++); } } @@ -353,7 +358,7 @@ namespace Examples.Tests sb.Remove(0, sb.Length); sb.Append("KeyboardDevice state: "); - sb.Append(legacy_keyboard_state); + KeyboardStateToString(legacy_keyboard_state, sb); DrawString(gfx, sb.ToString(), line++); sb.Remove(0, sb.Length); @@ -371,7 +376,7 @@ namespace Examples.Tests sb.Remove(0, sb.Length); sb.Append("Keyboard state: "); - sb.Append(keyboard_state); + KeyboardStateToString(keyboard_state, sb); DrawString(gfx, sb.ToString(), line++); return line; From 41276361fb06bd4d0cf0e4d2472df6e434a1c540 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 5 May 2014 09:14:14 +0200 Subject: [PATCH 33/51] [Mac] Report KeyDown/Up events for modifier flags --- .../Platform/MacOS/CocoaNativeWindow.cs | 11 +-- Source/OpenTK/Platform/NativeWindowBase.cs | 67 +++++++++++++++++++ 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs index 5372e7b2..9e746ea5 100644 --- a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs +++ b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs @@ -411,9 +411,7 @@ namespace OpenTK.Platform.MacOS case NSEventType.KeyDown: { MacOSKeyCode keyCode = (MacOSKeyCode)Cocoa.SendUshort(e, selKeyCode); - //var modifierFlags = (NSEventModifierMask)Cocoa.SendUint(e, selModifierFlags); var isARepeat = Cocoa.SendBool(e, selIsARepeat); - //GetKey(keyCode, modifierFlags, keyArgs); Key key = MacOSKeyMap.GetKey(keyCode); OnKeyDown(key, isARepeat); @@ -435,13 +433,18 @@ namespace OpenTK.Platform.MacOS case NSEventType.KeyUp: { MacOSKeyCode keyCode = (MacOSKeyCode)Cocoa.SendUshort(e, selKeyCode); - //var modifierFlags = (NSEventModifierMask)Cocoa.SendUint(e, selModifierFlags); - //GetKey(keyCode, modifierFlags, keyArgs); Key key = MacOSKeyMap.GetKey(keyCode); OnKeyUp(key); } break; + case NSEventType.FlagsChanged: + { + var modifierFlags = (NSEventModifierMask)Cocoa.SendUint(e, selModifierFlags); + UpdateModifierFlags(GetModifiers(modifierFlags)); + } + break; + case NSEventType.MouseEntered: { var eventTrackingArea = Cocoa.SendIntPtr(e, selTrackingArea); diff --git a/Source/OpenTK/Platform/NativeWindowBase.cs b/Source/OpenTK/Platform/NativeWindowBase.cs index 47013810..7a268ef3 100644 --- a/Source/OpenTK/Platform/NativeWindowBase.cs +++ b/Source/OpenTK/Platform/NativeWindowBase.cs @@ -150,6 +150,73 @@ namespace OpenTK.Platform KeyUp(this, e); } + /// \internal + /// + /// Call this method to simulate KeyDown/KeyUp events + /// on platforms that do not generate key events for + /// modifier flags (e.g. Mac/Cocoa). + /// Note: this method does not distinguish between the + /// left and right variants of modifier keys. + /// + /// Mods. + protected void UpdateModifierFlags(KeyModifiers mods) + { + bool alt = (mods & KeyModifiers.Alt) != 0; + bool control = (mods & KeyModifiers.Control) != 0; + bool shift = (mods & KeyModifiers.Shift) != 0; + + if (alt) + { + OnKeyDown(Key.AltLeft, KeyboardState[Key.AltLeft]); + OnKeyDown(Key.AltRight, KeyboardState[Key.AltLeft]); + } + else + { + if (KeyboardState[Key.AltLeft]) + { + OnKeyUp(Key.AltLeft); + } + if (KeyboardState[Key.AltRight]) + { + OnKeyUp(Key.AltRight); + } + } + + if (control) + { + OnKeyDown(Key.ControlLeft, KeyboardState[Key.AltLeft]); + OnKeyDown(Key.ControlRight, KeyboardState[Key.AltLeft]); + } + else + { + if (KeyboardState[Key.ControlLeft]) + { + OnKeyUp(Key.ControlLeft); + } + if (KeyboardState[Key.ControlRight]) + { + OnKeyUp(Key.ControlRight); + } + } + + if (shift) + { + OnKeyDown(Key.ShiftLeft, KeyboardState[Key.AltLeft]); + OnKeyDown(Key.ShiftRight, KeyboardState[Key.AltLeft]); + } + else + { + if (KeyboardState[Key.ShiftLeft]) + { + OnKeyUp(Key.ShiftLeft); + } + if (KeyboardState[Key.ShiftRight]) + { + OnKeyUp(Key.ShiftRight); + } + } + } + protected void OnMouseLeave(EventArgs e) { MouseLeave(this, e); From 3408523e27e2590a67b1fc7f79eae4c75fe355e0 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 6 May 2014 09:00:42 +0200 Subject: [PATCH 34/51] [Examples] Added PointToClient test --- Source/Examples/OpenTK.Examples.csproj | 3 +- Source/Examples/OpenTK/Test/PointToClient.cs | 37 ++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 Source/Examples/OpenTK/Test/PointToClient.cs diff --git a/Source/Examples/OpenTK.Examples.csproj b/Source/Examples/OpenTK.Examples.csproj index d0724a0a..7928ff09 100644 --- a/Source/Examples/OpenTK.Examples.csproj +++ b/Source/Examples/OpenTK.Examples.csproj @@ -549,7 +549,7 @@ Always - + Always @@ -572,6 +572,7 @@ Dependencies\x64\libSDL2.dylib + diff --git a/Source/Examples/OpenTK/Test/PointToClient.cs b/Source/Examples/OpenTK/Test/PointToClient.cs new file mode 100644 index 00000000..78e281b6 --- /dev/null +++ b/Source/Examples/OpenTK/Test/PointToClient.cs @@ -0,0 +1,37 @@ +using System; +using System.Diagnostics; +using System.Drawing; +using OpenTK; + +namespace Examples.Tests +{ + [Example("PointToClient Test", ExampleCategory.OpenTK, "NativeWindow")] + public class PointToClientTest + { + public static void Main() + { + using (var window = new NativeWindow()) + { + Trace.WriteLine(String.Format("Window bounds: {0}", window.Bounds)); + Trace.WriteLine(String.Format("Window client: {0}", window.ClientRectangle)); + + Point pclient = new Point(100, 100); + Point pscreen = window.PointToScreen(pclient); + Point ptest = window.PointToClient(pscreen); + Trace.WriteLine(String.Format("Client: {0} -> Screen: {1} -> Client: {2}", + pclient, pscreen, ptest)); + Trace.WriteLine(String.Format("Test {0}", + ptest == pclient ? "succeeded" : "failed")); + + pscreen = new Point(100, 100); + pclient = window.PointToClient(pscreen); + ptest = window.PointToScreen(pclient); + Trace.WriteLine(String.Format("Screen: {0} -> Client: {1} -> Screen: {2}", + pscreen, pclient, ptest)); + Trace.WriteLine(String.Format("Test {0}", + ptest == pscreen ? "succeeded" : "failed")); + } + } + } +} + From 637a8bee66579eccc79e99bb57a99707f92afcca Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 6 May 2014 09:01:23 +0200 Subject: [PATCH 35/51] [Platform] More selective mouse events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We should not raise mouse events when the cursor state hasn’t changed. --- Source/OpenTK/Platform/NativeWindowBase.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/OpenTK/Platform/NativeWindowBase.cs b/Source/OpenTK/Platform/NativeWindowBase.cs index 7a268ef3..5d19b7be 100644 --- a/Source/OpenTK/Platform/NativeWindowBase.cs +++ b/Source/OpenTK/Platform/NativeWindowBase.cs @@ -260,6 +260,7 @@ namespace OpenTK.Platform if (e.XDelta == 0 && e.YDelta == 0) { Debug.WriteLine("OnMouseMove called without moving the mouse"); + return; } PreviousMouseState = MouseState; @@ -277,6 +278,7 @@ namespace OpenTK.Platform if (dx == 0 && dy == 0) { Debug.WriteLine("OnMouseWheel called without moving the mouse wheel."); + return; } PreviousMouseState = MouseState; From cd315a3bd6a8f199c7a70d375ac5bc28f3d6d543 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 6 May 2014 09:02:09 +0200 Subject: [PATCH 36/51] [Platform] Forward PointToScreen to backend --- Source/OpenTK/NativeWindow.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Source/OpenTK/NativeWindow.cs b/Source/OpenTK/NativeWindow.cs index d88610ec..3d8819b8 100644 --- a/Source/OpenTK/NativeWindow.cs +++ b/Source/OpenTK/NativeWindow.cs @@ -163,12 +163,7 @@ namespace OpenTK /// public Point PointToScreen(Point point) { - // Here we use the fact that PointToClient just translates the point, and PointToScreen - // should perform the inverse operation. - Point trans = PointToClient(Point.Empty); - point.X -= trans.X; - point.Y -= trans.Y; - return point; + return implementation.PointToScreen(point); } #endregion From b1f627893bde306fa5e8b41f86f282f24d14f12a Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 6 May 2014 09:03:13 +0200 Subject: [PATCH 37/51] [Mac] Fixed PointTo* calculations On HiDPI displays, the client rectangle is using a pixel-based coordinate system, while the screen is using a point-based coordinate system. PointToClient/PointToScreen now correctly accounts for that. --- .../Platform/MacOS/CocoaNativeWindow.cs | 62 ++++++++++++------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs index 9e746ea5..a9fb48b2 100644 --- a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs +++ b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs @@ -469,7 +469,7 @@ namespace OpenTK.Platform.MacOS { if (selectedCursor != MouseCursor.Default) { - SetCursor(MouseCursor.Default); + //SetCursor(MouseCursor.Default); } OnMouseLeave(EventArgs.Empty); @@ -587,14 +587,20 @@ namespace OpenTK.Platform.MacOS public override System.Drawing.Point PointToClient(System.Drawing.Point point) { - var r = Cocoa.SendRect(windowInfo.Handle, selConvertRectFromScreen, new RectangleF(point.X, point.Y, 0, 0)); - return new Point((int)r.X, (int)(GetContentViewFrame().Height - GetCurrentScreenFrame().Height - r.Y)); + var r = + Cocoa.SendRect(windowInfo.ViewHandle, selConvertRectToBacking, + Cocoa.SendRect(windowInfo.Handle, selConvertRectFromScreen, + new RectangleF(point.X, GetCurrentScreenFrame().Height - point.Y, 0, 0))); + return new Point((int)r.X, (int)(Height - r.Y)); } public override System.Drawing.Point PointToScreen(System.Drawing.Point point) { - var r = Cocoa.SendRect(windowInfo.Handle, selConvertRectToScreen, new RectangleF(point.X, point.Y, 0, 0)); - return new Point((int)r.X, (int)(-GetContentViewFrame().Height + GetCurrentScreenFrame().Height - r.Y)); + var r = + Cocoa.SendRect(windowInfo.Handle, selConvertRectToScreen, + Cocoa.SendRect(windowInfo.ViewHandle, selConvertRectFromBacking, + new RectangleF(point.X, Height - point.Y, 0, 0))); + return new Point((int)r.X, (int)(GetCurrentScreenFrame().Height - r.Y)); } public override System.Drawing.Icon Icon @@ -810,11 +816,21 @@ namespace OpenTK.Platform.MacOS get { var r = Cocoa.SendRect(windowInfo.Handle, selFrame); - return new Rectangle((int)r.X, (int)(GetCurrentScreenFrame().Height - r.Y), (int)r.Width, (int)r.Height); + return new Rectangle( + (int)r.X, + (int)(GetCurrentScreenFrame().Height - r.Y - r.Height), + (int)r.Width, + (int)r.Height); } set { - Cocoa.SendVoid(windowInfo.Handle, selSetFrame, new RectangleF(value.X, GetCurrentScreenFrame().Height - value.Y, value.Width, value.Height), true); + Cocoa.SendVoid(windowInfo.Handle, selSetFrame, + new RectangleF( + value.X, + GetCurrentScreenFrame().Height - value.Y - value.Height, + value.Width, + value.Height), + true); } } @@ -1039,25 +1055,27 @@ namespace OpenTK.Platform.MacOS private void SetCursorVisible(bool visible) { + // If the mouse is outside the window and we want to hide it, + // move it inside the window first. + // Otherwise, if we are making the cursor visible again, + // we place it in the same spot as reported in the current + // MouseState to avoid sudden jumps. + if (!visible && !Bounds.Contains(new Point(MouseState.X, MouseState.Y))) + { + Mouse.SetPosition( + (Bounds.Left + Bounds.Right) / 2, + (Bounds.Top + Bounds.Bottom) / 2); + } + else if (visible) + { + var p = PointToScreen(new Point(MouseState.X, MouseState.Y)); + Mouse.SetPosition((int)p.X, (int)p.Y); + } + Carbon.CG.AssociateMouseAndMouseCursorPosition(visible); Cocoa.SendVoid(NSCursor, visible ? selUnhide : selHide); } - private void SetCursor(MouseCursor cursor) - { - if (cursor == MouseCursor.Default) - { - Cocoa.SendVoid(NSCursor, selUnhide); - } - else if (cursor == MouseCursor.Empty) - { - Cocoa.SendVoid(NSCursor, selHide); - } - else - { - } - } - private void SetMenuVisible(bool visible) { var options = (NSApplicationPresentationOptions)Cocoa.SendInt(NSApplication.Handle, selPresentationOptions); From 7e86d349d2a713487ab0284216906b528100e4c6 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 6 May 2014 09:12:08 +0200 Subject: [PATCH 38/51] [Examples] Test MouseCursor in GameWindowStates --- .../Examples/OpenTK/Test/GameWindowStates.cs | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/Source/Examples/OpenTK/Test/GameWindowStates.cs b/Source/Examples/OpenTK/Test/GameWindowStates.cs index 12f47e9e..efb2d3bb 100644 --- a/Source/Examples/OpenTK/Test/GameWindowStates.cs +++ b/Source/Examples/OpenTK/Test/GameWindowStates.cs @@ -26,6 +26,8 @@ namespace Examples.Tests bool mouse_in_window = false; bool viewport_changed = true; + MouseCursor Pencil; + // legacy GameWindow.Mouse.* events Vector4 mousedevice_pos; int mousedevice_buttons; @@ -136,6 +138,10 @@ namespace Examples.Tests case Key.BracketRight: TargetUpdateFrequency++; break; case Key.Comma: TargetRenderFrequency--; break; case Key.Period: TargetRenderFrequency++; break; + + case Key.Space: + CursorVisible = !CursorVisible; + break; } if (!keyboard_keys.ContainsKey(e.Key)) @@ -187,18 +193,15 @@ namespace Examples.Tests void MouseDeviceButtonHandler(object sender, MouseButtonEventArgs e) { - if (e.Button == MouseButton.Left && e.IsPressed) - { - CursorVisible = false; - } - if (e.IsPressed) { mousedevice_buttons |= 1 << (int)e.Button; + Cursor = Pencil; } else { mousedevice_buttons &= ~(1 << (int)e.Button); + Cursor = MouseCursor.Default; } mousedevice_state = e.Mouse; } @@ -225,11 +228,6 @@ namespace Examples.Tests void MouseButtonHandler(object sender, MouseButtonEventArgs e) { - if (e.Button == MouseButton.Left && e.IsPressed) - { - CursorVisible = false; - } - if (e.IsPressed) { mouse_buttons |= 1 << (int)e.Button; @@ -588,6 +586,17 @@ namespace Examples.Tests { watch.Start(); + using (var bitmap = new Bitmap("Data/Textures/cursor.png")) + { + var data = bitmap.LockBits( + new Rectangle(0, 0, bitmap.Width, bitmap.Height), + System.Drawing.Imaging.ImageLockMode.ReadOnly, + System.Drawing.Imaging.PixelFormat.Format32bppPArgb); + + Pencil = new OpenTK.MouseCursor( + 2, 21, data.Width, data.Height, data.Scan0); + } + GL.ClearColor(Color.MidnightBlue); GL.Enable(EnableCap.Texture2D); From a270b9328ec487bee8bf08987070e50ced41594b Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 6 May 2014 09:31:35 +0200 Subject: [PATCH 39/51] [SDL] Report mouse in client coordinates --- Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs b/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs index dc232663..3bfc54e1 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs @@ -274,8 +274,10 @@ namespace OpenTK.Platform.SDL2 static void ProcessMouseMotionEvent(Sdl2NativeWindow window, MouseMotionEvent ev) { - //float scale = window.ClientSize.Width / (float)window.Size.Width; - window.OnMouseMove(ev.X, ev.Y); + float scale = window.ClientSize.Width / (float)window.Size.Width; + window.OnMouseMove( + (int)Math.Round(ev.X * scale), + (int)Math.Round(ev.Y * scale)); } static void ProcessMouseWheelEvent(Sdl2NativeWindow window, MouseWheelEvent ev) From e3fd9e1374d72b3e220ef502309c01192b9d5e41 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 6 May 2014 09:36:30 +0200 Subject: [PATCH 40/51] [SDL] Avoid CursorVisible = true cursor jump --- Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs b/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs index 3bfc54e1..3f52e425 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs @@ -397,6 +397,16 @@ namespace OpenTK.Platform.SDL2 SDL.ShowCursor(!grab); SDL.SetWindowGrab(window.Handle, grab); SDL.SetRelativeMouseMode(grab); + if (!grab) + { + // Move the cursor to the current position + // in order to avoid a sudden jump when it + // becomes visible again + float scale = Width / (float)Size.Width; + SDL.WarpMouseInWindow(window.Handle, + (int)Math.Round(MouseState.X / scale), + (int)Math.Round(MouseState.Y / scale)); + } } // Hack to force WindowState events to be pumped From d430b462fed2a98682d4f56bf0918a5a3e2530ef Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 8 May 2014 20:58:11 +0200 Subject: [PATCH 41/51] [X11] Corrected size events --- Source/OpenTK/Platform/X11/X11GLNative.cs | 159 ++++------------------ 1 file changed, 28 insertions(+), 131 deletions(-) diff --git a/Source/OpenTK/Platform/X11/X11GLNative.cs b/Source/OpenTK/Platform/X11/X11GLNative.cs index 447e415d..017e0206 100644 --- a/Source/OpenTK/Platform/X11/X11GLNative.cs +++ b/Source/OpenTK/Platform/X11/X11GLNative.cs @@ -739,7 +739,7 @@ namespace OpenTK.Platform.X11 if (Location != new_location) { bounds.Location = new_location; - Move(this, EventArgs.Empty); + OnMove(EventArgs.Empty); } // Note: width and height denote the internal (client) size. @@ -750,9 +750,15 @@ namespace OpenTK.Platform.X11 if (Bounds.Size != new_size) { bounds.Size = new_size; - client_rectangle.Size = new Size(e.ConfigureEvent.width, e.ConfigureEvent.height); - Resize(this, EventArgs.Empty); + // X11 sets the client width/height to 0 + // when the window is minimized. Many apps + // do not expect this and crash, so clamp + // minimum width/height to 1 instead. + client_rectangle.Size = new Size( + Math.Max(e.ConfigureEvent.width, 1), + Math.Max(e.ConfigureEvent.height, 1)); + OnResize(EventArgs.Empty); } //Debug.Print("[X11] Window bounds changed: {0}", bounds); @@ -800,7 +806,7 @@ namespace OpenTK.Platform.X11 bool previous_visible = visible; visible = true; if (visible != previous_visible) - VisibleChanged(this, EventArgs.Empty); + OnVisibleChanged(EventArgs.Empty); } return; @@ -809,7 +815,7 @@ namespace OpenTK.Platform.X11 bool previous_visible = visible; visible = false; if (visible != previous_visible) - VisibleChanged(this, EventArgs.Empty); + OnVisibleChanged(EventArgs.Empty); } break; @@ -822,7 +828,7 @@ namespace OpenTK.Platform.X11 { Debug.WriteLine("Exit message received."); CancelEventArgs ce = new CancelEventArgs(); - Closing(this, ce); + OnClosing(ce); if (!ce.Cancel) { @@ -843,7 +849,7 @@ namespace OpenTK.Platform.X11 Debug.WriteLine("Window destroyed"); exists = false; - Closed(this, EventArgs.Empty); + OnClosed(EventArgs.Empty); return; @@ -1002,7 +1008,7 @@ namespace OpenTK.Platform.X11 bool previous_focus = has_focus; has_focus = true; if (has_focus != previous_focus) - FocusedChanged(this, EventArgs.Empty); + OnFocusedChanged(EventArgs.Empty); } break; @@ -1011,19 +1017,19 @@ namespace OpenTK.Platform.X11 bool previous_focus = has_focus; has_focus = false; if (has_focus != previous_focus) - FocusedChanged(this, EventArgs.Empty); + OnFocusedChanged(EventArgs.Empty); } break; case XEventName.LeaveNotify: if (CursorVisible) { - MouseLeave(this, EventArgs.Empty); + OnMouseLeave(EventArgs.Empty); } break; case XEventName.EnterNotify: - MouseEnter(this, EventArgs.Empty); + OnMouseEnter(EventArgs.Empty); break; case XEventName.MappingNotify: @@ -1038,7 +1044,7 @@ namespace OpenTK.Platform.X11 case XEventName.PropertyNotify: if (e.PropertyEvent.atom == _atom_net_wm_state) { - WindowStateChanged(this, EventArgs.Empty); + OnWindowStateChanged(EventArgs.Empty); } //if (e.PropertyEvent.atom == _atom_net_frame_extents) @@ -1122,115 +1128,27 @@ namespace OpenTK.Platform.X11 #endregion - #region Location - - public Point Location - { - get { return Bounds.Location; } - set - { - Bounds = new Rectangle(value, Bounds.Size); - } - } - - #endregion - - #region Size - - public Size Size - { - get { return Bounds.Size; } - set - { - Bounds = new Rectangle(Bounds.Location, value); - } - } - - #endregion - - #region ClientRectangle - - public Rectangle ClientRectangle - { - get - { - if (client_rectangle.Width == 0) - client_rectangle.Width = 1; - if (client_rectangle.Height == 0) - client_rectangle.Height = 1; - return client_rectangle; - } - set - { - using (new XLock(window.Display)) - { - Functions.XMoveWindow(window.Display, window.Handle, - value.X, value.Y); - Functions.XResizeWindow(window.Display, window.Handle, - value.Width, value.Height); - } - ProcessEvents(); - } - } - - #endregion - #region ClientSize public override Size ClientSize { get { - return ClientRectangle.Size; + return client_rectangle.Size; } set { - ClientRectangle = new Rectangle(Point.Empty, value); + using (new XLock(window.Display)) + { + Functions.XResizeWindow(window.Display, window.Handle, + value.Width, value.Height); + } + ProcessEvents(); } } #endregion - #region Width - - public int Width - { - get { return ClientSize.Width; } - set { ClientSize = new Size(value, Height); } - } - - #endregion - - #region Height - - public int Height - { - get { return ClientSize.Height; } - set { ClientSize = new Size(Width, value); } - } - - #endregion - - #region X - - public int X - { - get { return Location.X; } - set { Location = new Point(value, Y); } - } - - #endregion - - #region Y - - public int Y - { - get { return Location.Y; } - set { Location = new Point(X, value); } - } - - #endregion - #region Icon public override Icon Icon @@ -1299,7 +1217,7 @@ namespace OpenTK.Platform.X11 } icon = value; - IconChanged(this, EventArgs.Empty); + OnIconChanged(EventArgs.Empty); } } @@ -1497,33 +1415,12 @@ namespace OpenTK.Platform.X11 break; } - WindowBorderChanged(this, EventArgs.Empty); + OnWindowBorderChanged(EventArgs.Empty); } } #endregion - #region Events - - public event EventHandler Move = delegate { }; - public event EventHandler Resize = delegate { }; - public event EventHandler Closing = delegate { }; - public event EventHandler Closed = delegate { }; - public event EventHandler Disposed = delegate { }; - public event EventHandler IconChanged = delegate { }; - public event EventHandler TitleChanged = delegate { }; - public event EventHandler VisibleChanged = delegate { }; - public event EventHandler FocusedChanged = delegate { }; - public event EventHandler WindowBorderChanged = delegate { }; - public event EventHandler WindowStateChanged = delegate { }; - public event EventHandler KeyDown = delegate { }; - public event EventHandler KeyPress = delegate { }; - public event EventHandler KeyUp = delegate { }; - public event EventHandler MouseEnter = delegate { }; - public event EventHandler MouseLeave = delegate { }; - - #endregion - #region Cursor public override MouseCursor Cursor @@ -1659,7 +1556,7 @@ namespace OpenTK.Platform.X11 } } - TitleChanged(this, EventArgs.Empty); + OnTitleChanged(EventArgs.Empty); } } From 8195800ee7d4c96b06583f76f9ee857e15009955 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Fri, 9 May 2014 00:13:23 +0200 Subject: [PATCH 42/51] [X11] Improved vsync support OpenTK will now check for GLX_EXT_swap_control, GLX_MESA_swap_control and GLX_SGI_swap_control. This allows us to control vsync on more systems. --- Source/OpenTK/Platform/X11/Bindings/Glx.cs | 60 +++++++++++++++- Source/OpenTK/Platform/X11/X11GLContext.cs | 81 ++++++++++++++-------- 2 files changed, 113 insertions(+), 28 deletions(-) diff --git a/Source/OpenTK/Platform/X11/Bindings/Glx.cs b/Source/OpenTK/Platform/X11/Bindings/Glx.cs index 4a312b9d..27c83695 100644 --- a/Source/OpenTK/Platform/X11/Bindings/Glx.cs +++ b/Source/OpenTK/Platform/X11/Bindings/Glx.cs @@ -266,7 +266,14 @@ namespace OpenTK.Platform.X11 static string[] EntryPointNames = new string[] { "glXCreateContextAttribs", + "glXSwapIntervalEXT", + "glXGetSwapIntervalEXT", + "glXSwapIntervalMESA", + "glXGetSwapIntervalMESA", + "glXSwapIntervalOML", + "glXGetSwapIntervalOML", "glXSwapIntervalSGI", + "glXGetSwapIntervalSGI", }; static IntPtr[] EntryPoints = new IntPtr[EntryPointNames.Length]; @@ -405,6 +412,36 @@ namespace OpenTK.Platform.X11 #endregion } + public partial class Ext + { + [AutoGenerated(EntryPoint = "glXSwapIntervalEXT")] + public static ErrorCode SwapInterval(int interval) + { + throw new NotImplementedException(); + } + + [AutoGenerated(EntryPoint = "glXGetSwapIntervalEXT")] + public static int GetSwapInterval() + { + throw new NotImplementedException(); + } + } + + public partial class Mesa + { + [AutoGenerated(EntryPoint = "glXSwapIntervalMESA")] + public static ErrorCode SwapInterval(int interval) + { + throw new NotImplementedException(); + } + + [AutoGenerated(EntryPoint = "glXGetSwapIntervalMESA")] + public static int GetSwapInterval() + { + throw new NotImplementedException(); + } + } + public partial class Sgi { [AutoGenerated(EntryPoint = "glXSwapIntervalSGI")] @@ -412,6 +449,12 @@ namespace OpenTK.Platform.X11 { throw new NotImplementedException(); } + + [AutoGenerated(EntryPoint = "glXGetSwapIntervalSGI")] + public static int GetSwapInterval() + { + throw new NotImplementedException(); + } } [Slot(0)] @@ -419,7 +462,22 @@ namespace OpenTK.Platform.X11 internal unsafe static extern IntPtr glXCreateContextAttribsARB(IntPtr display, IntPtr fbconfig, IntPtr share_context, bool direct, int* attribs); [Slot(1)] [DllImport(Library, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)] - internal static extern IntPtr glXSwapIntervalSGI(int interval); + internal static extern ErrorCode glXSwapIntervalEXT(int interval); + [Slot(2)] + [DllImport(Library, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)] + internal static extern int glXGetSwapIntervalEXT(); + [Slot(3)] + [DllImport(Library, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)] + internal static extern ErrorCode glXSwapIntervalMESA(int interval); + [Slot(4)] + [DllImport(Library, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)] + internal static extern int glXGetSwapIntervalMESA(); + [Slot(5)] + [DllImport(Library, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)] + internal static extern ErrorCode glXSwapIntervalSGI(int interval); + [Slot(6)] + [DllImport(Library, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)] + internal static extern int glXGetSwapIntervalSGI(); #endregion } diff --git a/Source/OpenTK/Platform/X11/X11GLContext.cs b/Source/OpenTK/Platform/X11/X11GLContext.cs index e67c1514..9596f0e3 100644 --- a/Source/OpenTK/Platform/X11/X11GLContext.cs +++ b/Source/OpenTK/Platform/X11/X11GLContext.cs @@ -29,10 +29,13 @@ namespace OpenTK.Platform.X11 // current on window originating from a different display. IntPtr display; X11WindowInfo currentWindow; - bool vsync_supported; + bool vsync_ext_supported; + bool vsync_mesa_supported; + bool vsync_sgi_supported; bool vsync_tear_supported; - int swap_interval = 1; // As defined in GLX_SGI_swap_control + int sgi_swap_interval = 1; // As defined in GLX_SGI_swap_control readonly X11GraphicsMode ModeSelector = new X11GraphicsMode(); + string extensions = null; #endregion @@ -232,7 +235,7 @@ namespace OpenTK.Platform.X11 return result; } - static bool SupportsExtension(IntPtr display, X11WindowInfo window, string e) + bool SupportsExtension(IntPtr display, X11WindowInfo window, string e) { if (window == null) throw new ArgumentNullException("window"); @@ -241,15 +244,17 @@ namespace OpenTK.Platform.X11 if (window.Display != display) throw new InvalidOperationException(); - string extensions = null; - using (new XLock(display)) + if (String.IsNullOrEmpty(extensions)) { - extensions = Glx.QueryExtensionsString(display, window.Screen); + using (new XLock(display)) + { + extensions = Glx.QueryExtensionsString(display, window.Screen); + } } return !String.IsNullOrEmpty(extensions) && extensions.Contains(e); } - static bool SupportsCreateContextAttribs(IntPtr display, X11WindowInfo window) + bool SupportsCreateContextAttribs(IntPtr display, X11WindowInfo window) { return SupportsExtension(display, window, "GLX_ARB_create_context") && @@ -354,29 +359,40 @@ namespace OpenTK.Platform.X11 { get { - if (vsync_supported) - return swap_interval; - else - return 0; + using (new XLock(display)) + { + if (vsync_ext_supported) + return Glx.Ext.GetSwapInterval(); + else if (vsync_mesa_supported) + return Glx.Mesa.GetSwapInterval(); + else if (vsync_sgi_supported) + return sgi_swap_interval; + else + return 0; + } } set { - if (vsync_supported) + if (value < 0 && !vsync_tear_supported) { - if (value < 0 && !vsync_tear_supported) - { - value = 1; - } - - ErrorCode error_code = 0; - using (new XLock(Display)) - error_code = Glx.Sgi.SwapInterval(value); - - if (error_code == X11.ErrorCode.NO_ERROR) - swap_interval = value; - else - Debug.Print("VSync = {0} failed, error code: {1}.", value, error_code); + value = 1; } + + ErrorCode error_code = 0; + using (new XLock(Display)) + { + if (vsync_ext_supported) + error_code = Glx.Ext.SwapInterval(value); + else if (vsync_mesa_supported) + error_code = Glx.Mesa.SwapInterval(value); + else if (vsync_sgi_supported) + error_code = Glx.Sgi.SwapInterval(value); + } + + if (error_code == X11.ErrorCode.NO_ERROR) + sgi_swap_interval = value; + else + Debug.Print("VSync = {0} failed, error code: {1}.", value, error_code); } } @@ -386,12 +402,23 @@ namespace OpenTK.Platform.X11 public override void LoadAll() { - vsync_supported = + vsync_ext_supported = + SupportsExtension(display, currentWindow, "GLX_EXT_swap_control") && + Glx.SupportsFunction("glXSwapIntervalEXT") && + Glx.SupportsFunction("glXGetSwapIntervalEXT"); + vsync_mesa_supported = + SupportsExtension(display, currentWindow, "GLX_MESA_swap_control") && + Glx.SupportsFunction("glXSwapIntervalMESA") && + Glx.SupportsFunction("glXGetSwapIntervalMESA"); + vsync_sgi_supported = SupportsExtension(display, currentWindow, "GLX_SGI_swap_control") && Glx.SupportsFunction("glXSwapIntervalSGI"); + Debug.Print("Context supports vsync: {0}.", + vsync_ext_supported || vsync_mesa_supported || vsync_ext_supported); + vsync_tear_supported = SupportsExtension(display, currentWindow, "GLX_EXT_swap_control_tear"); - Debug.Print("Context supports vsync: {0}.", vsync_supported); + Debug.Print("Context supports vsync tear: {0}.", vsync_tear_supported); base.LoadAll(); } From 86146f54d5641807d50591949a40cc24ca59d676 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Fri, 9 May 2014 00:26:51 +0200 Subject: [PATCH 43/51] [X11] Fixed key repeat --- Source/OpenTK/Platform/X11/X11GLNative.cs | 52 +---------------------- 1 file changed, 1 insertion(+), 51 deletions(-) diff --git a/Source/OpenTK/Platform/X11/X11GLNative.cs b/Source/OpenTK/Platform/X11/X11GLNative.cs index 017e0206..22bb8515 100644 --- a/Source/OpenTK/Platform/X11/X11GLNative.cs +++ b/Source/OpenTK/Platform/X11/X11GLNative.cs @@ -865,40 +865,8 @@ namespace OpenTK.Platform.X11 { if (pressed) { - // Check if this is a key repeat event. - // X11 does not provide this information, - // so we rely on the XInput2 extension for that. - // Todo: hack this when XInput2 is not available - // by checking if another KeyPress event is enqueued. - bool is_repeat = false; - if (xi2_supported && e.GenericEventCookie.extension == xi2_opcode) - { - if (e.GenericEventCookie.evtype == (int)XIEventType.KeyPress) - { - unsafe - { - XIDeviceEvent* xi = (XIDeviceEvent*)e.GenericEventCookie.data; - is_repeat = (xi->flags & XIEventFlags.KeyRepeat) != 0; - } - } - } - else - { - if (API.Pending(window.Display) > 0) - { - unsafe - { - XEvent dummy = new XEvent(); - KeyRepeatTestData arg = new KeyRepeatTestData(); - arg.Event = e; - API.CheckIfEvent(window.Display, ref dummy, IsKeyRepeatPredicate, - new IntPtr(&arg)); - is_repeat = arg.IsRepeat; - } - } - } - // Raise KeyDown event + bool is_repeat = KeyboardState[key]; OnKeyDown(key, is_repeat); } else @@ -1060,24 +1028,6 @@ namespace OpenTK.Platform.X11 } } - struct KeyRepeatTestData - { - public XEvent Event; - public bool IsRepeat; - } - - unsafe static bool IsKeyRepeatPredicate(IntPtr display, ref XEvent e, IntPtr arg) - { - // IsRepeat is true when the event queue contains an identical - // KeyPress event at later time no greater than 2. - KeyRepeatTestData* data = (KeyRepeatTestData*)arg; - data->IsRepeat = - e.type == XEventName.KeyPress && - e.KeyEvent.keycode == data->Event.KeyEvent.keycode && - e.KeyEvent.time.ToInt64() - data->Event.KeyEvent.time.ToInt64() < 2; - return false; // keep the event in the queue - } - #endregion #region Bounds From ab29797079b5396a287837d8fe921ab9a84b06c5 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Fri, 9 May 2014 01:49:51 +0200 Subject: [PATCH 44/51] [Platform] Raise KeyUp events on focus lost This ensures that no keys are stuck in pressed state when the user switches away from the application window. --- Source/OpenTK/Platform/NativeWindowBase.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Source/OpenTK/Platform/NativeWindowBase.cs b/Source/OpenTK/Platform/NativeWindowBase.cs index 5d19b7be..3b1fcd39 100644 --- a/Source/OpenTK/Platform/NativeWindowBase.cs +++ b/Source/OpenTK/Platform/NativeWindowBase.cs @@ -109,6 +109,20 @@ namespace OpenTK.Platform protected void OnFocusedChanged(EventArgs e) { FocusedChanged(this, e); + + if (!Focused) + { + // Clear keyboard state, otherwise KeyUp + // events may be missed resulting in stuck + // keys. + for (Key key = 0; key < Key.LastKey; key++) + { + if (KeyboardState[key]) + { + OnKeyUp(key); + } + } + } } protected void OnWindowBorderChanged(EventArgs e) From f0d0f6e53a90415009f2e933a10377c734819995 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Fri, 9 May 2014 13:10:23 +0200 Subject: [PATCH 45/51] [X11] Fixed WindowBorder and WindowState setters After many hours of fighting with xlib, the monster is slain and WindowBorder/WindowState changes now take effect without messing up the window size or position on the desktop. Phew! --- Source/OpenTK/Platform/X11/X11GLNative.cs | 217 ++++++++++++++-------- 1 file changed, 137 insertions(+), 80 deletions(-) diff --git a/Source/OpenTK/Platform/X11/X11GLNative.cs b/Source/OpenTK/Platform/X11/X11GLNative.cs index 22bb8515..e5201638 100644 --- a/Source/OpenTK/Platform/X11/X11GLNative.cs +++ b/Source/OpenTK/Platform/X11/X11GLNative.cs @@ -111,6 +111,13 @@ namespace OpenTK.Platform.X11 bool _decorations_hidden = false; + // Store previous border and bounds + // when switching from WindowState.Normal + // to a different state. When switching + // back, reset window to these.s + WindowBorder _previous_window_border; + Size _previous_window_size; + MouseCursor cursor = MouseCursor.Default; IntPtr cursorHandle; bool cursor_visible = true; @@ -1248,75 +1255,105 @@ namespace OpenTK.Platform.X11 { OpenTK.WindowState current_state = this.WindowState; + // When switching away from normal state, store + // the "normal" border and size. These will be used + // for restoring to normal state. + if (current_state == OpenTK.WindowState.Normal) + { + _previous_window_border = WindowBorder; + _previous_window_size = ClientSize; + } + if (current_state == value) return; Debug.Print("GameWindow {0} changing WindowState from {1} to {2}.", window.Handle.ToString(), - current_state.ToString(), value.ToString()); + current_state.ToString(), value.ToString()); - using (new XLock(window.Display)) + // When minimizing the window, call XIconifyWindow and bail out. + // For other states, we first need to restore the window, set the + // new state and reset the window border and bounds. + if (value != OpenTK.WindowState.Minimized) { - // Reset the current window state - if (current_state == OpenTK.WindowState.Minimized) - Functions.XMapWindow(window.Display, window.Handle); - else if (current_state == OpenTK.WindowState.Fullscreen) - Functions.SendNetWMMessage(window, _atom_net_wm_state, _atom_remove, - _atom_net_wm_state_fullscreen, - IntPtr.Zero); - else if (current_state == OpenTK.WindowState.Maximized) - Functions.SendNetWMMessage(window, _atom_net_wm_state, _atom_toggle, - _atom_net_wm_state_maximized_horizontal, - _atom_net_wm_state_maximized_vertical); - - Functions.XSync(window.Display, false); - } - // We can't resize the window if its border is fixed, so make it resizable first. - bool temporary_resizable = false; - WindowBorder previous_state = WindowBorder; - if (WindowBorder != WindowBorder.Resizable) - { - temporary_resizable = true; - WindowBorder = WindowBorder.Resizable; - } - - using (new XLock(window.Display)) - { - switch (value) + // Some WMs cannot switch between specific states directly, + // Switch back to a regular window first. + if (WindowBorder == WindowBorder.Fixed) + { + ChangeWindowBorder(WindowBorder.Resizable); + } + + ResetWindowState(current_state); + } + + // Change to the desired WindowState. + // Note that OnWindowStateChanged is called inside + // ProcessEvents. + ChangeWindowState(value); + ProcessEvents(); + } + } + + void ResetWindowState(OpenTK.WindowState current_state) + { + if (current_state != OpenTK.WindowState.Normal) + { + using (new XLock(window.Display)) + { + switch (current_state) { - case OpenTK.WindowState.Normal: - Functions.XRaiseWindow(window.Display, window.Handle); - - break; - - case OpenTK.WindowState.Maximized: - Functions.SendNetWMMessage(window, _atom_net_wm_state, _atom_add, - _atom_net_wm_state_maximized_horizontal, - _atom_net_wm_state_maximized_vertical); - Functions.XRaiseWindow(window.Display, window.Handle); - - break; - case OpenTK.WindowState.Minimized: - // Todo: multiscreen support - Functions.XIconifyWindow(window.Display, window.Handle, window.Screen); - + Functions.XMapWindow(window.Display, window.Handle); break; - + case OpenTK.WindowState.Fullscreen: - //_previous_window_border = this.WindowBorder; - //this.WindowBorder = WindowBorder.Hidden; - Functions.SendNetWMMessage(window, _atom_net_wm_state, _atom_add, - _atom_net_wm_state_fullscreen, IntPtr.Zero); - Functions.XRaiseWindow(window.Display, window.Handle); - + Functions.SendNetWMMessage(window, + _atom_net_wm_state, + _atom_remove, + _atom_net_wm_state_fullscreen, + IntPtr.Zero); + break; + + case OpenTK.WindowState.Maximized: + Functions.SendNetWMMessage(window, + _atom_net_wm_state, + _atom_toggle, + _atom_net_wm_state_maximized_horizontal, + _atom_net_wm_state_maximized_vertical); break; } } + } + } - if (temporary_resizable) - WindowBorder = previous_state; + void ChangeWindowState(OpenTK.WindowState value) + { + using (new XLock(window.Display)) + { + switch (value) + { + case OpenTK.WindowState.Normal: + Functions.XRaiseWindow(window.Display, window.Handle); + ChangeWindowBorder(_previous_window_border, + _previous_window_size.Width, _previous_window_size.Height); + break; - ProcessEvents(); + case OpenTK.WindowState.Maximized: + Functions.SendNetWMMessage(window, _atom_net_wm_state, _atom_add, + _atom_net_wm_state_maximized_horizontal, + _atom_net_wm_state_maximized_vertical); + Functions.XRaiseWindow(window.Display, window.Handle); + break; + + case OpenTK.WindowState.Minimized: + Functions.XIconifyWindow(window.Display, window.Handle, window.Screen); + break; + + case OpenTK.WindowState.Fullscreen: + Functions.SendNetWMMessage(window, _atom_net_wm_state, _atom_add, + _atom_net_wm_state_fullscreen, IntPtr.Zero); + Functions.XRaiseWindow(window.Display, window.Handle); + break; + } } } @@ -1328,47 +1365,67 @@ namespace OpenTK.Platform.X11 { get { - if (IsWindowBorderHidden) + if (IsWindowBorderHidden || WindowState == OpenTK.WindowState.Fullscreen) return WindowBorder.Hidden; - - if (IsWindowBorderResizable) - return WindowBorder.Resizable; - else + else if (!IsWindowBorderResizable) return WindowBorder.Fixed; + else if (WindowState == OpenTK.WindowState.Maximized) + return _previous_window_border; + else + return WindowBorder.Resizable; } set { if (WindowBorder == value) return; - if (WindowBorder == WindowBorder.Hidden) - EnableWindowDecorations(); - - switch (value) + // We cannot change the border of a fullscreen window. + // Record the new value and set it on the next WindowState + // change. + if (WindowState == OpenTK.WindowState.Fullscreen) { - case WindowBorder.Fixed: - Debug.Print("Making WindowBorder fixed."); - SetWindowMinMax((short)Width, (short)Height, (short)Width, (short)Height); - - break; - - case WindowBorder.Resizable: - Debug.Print("Making WindowBorder resizable."); - SetWindowMinMax(_min_width, _min_height, -1, -1); - - break; - - case WindowBorder.Hidden: - Debug.Print("Making WindowBorder hidden."); - DisableWindowDecorations(); - - break; + _previous_window_border = value; + return; } + ChangeWindowBorder(value); OnWindowBorderChanged(EventArgs.Empty); } } + void ChangeWindowBorder(WindowBorder value) + { + ChangeWindowBorder(value, Width, Height); + } + + void ChangeWindowBorder(WindowBorder value, int width, int height) + { + if (WindowBorder == WindowBorder.Hidden) + EnableWindowDecorations(); + + switch (value) + { + case WindowBorder.Fixed: + Debug.Print("Making WindowBorder fixed."); + SetWindowMinMax((short)width, (short)height, (short)width, (short)height); + break; + case WindowBorder.Resizable: + Debug.Print("Making WindowBorder resizable."); + SetWindowMinMax(_min_width, _min_height, -1, -1); + break; + case WindowBorder.Hidden: + Debug.Print("Making WindowBorder hidden."); + // Make the hidden border resizable, otherwise + // we won't be able to maximize the window or + // enter fullscreen mode. + SetWindowMinMax(_min_width, _min_height, -1, -1); + DisableWindowDecorations(); + break; + } + + ProcessEvents(); + } + #endregion #region Cursor From 8eae337d93b5c52c615c01770897f126eee00164 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Fri, 9 May 2014 15:22:33 +0200 Subject: [PATCH 46/51] [Win] Cleaned up window size/pos properties --- Source/OpenTK/Platform/Windows/WinGLNative.cs | 70 +------------------ 1 file changed, 3 insertions(+), 67 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinGLNative.cs b/Source/OpenTK/Platform/Windows/WinGLNative.cs index f3661a19..18598c78 100644 --- a/Source/OpenTK/Platform/Windows/WinGLNative.cs +++ b/Source/OpenTK/Platform/Windows/WinGLNative.cs @@ -419,8 +419,7 @@ namespace OpenTK.Platform.Windows }; // Max points GetMouseMovePointsEx can return is 64. - int numPoints = 64; - + const int numPoints = 64; MouseMovePoint* movePoints = stackalloc MouseMovePoint[numPoints]; // GetMouseMovePointsEx fills in movePoints so that the most @@ -977,7 +976,7 @@ namespace OpenTK.Platform.Windows #region Location - public Point Location + public override Point Location { get { return Bounds.Location; } set @@ -991,7 +990,7 @@ namespace OpenTK.Platform.Windows #region Size - public Size Size + public override Size Size { get { return Bounds.Size; } set @@ -1003,29 +1002,6 @@ namespace OpenTK.Platform.Windows #endregion - #region ClientRectangle - - public Rectangle ClientRectangle - { - get - { - if (client_rectangle.Width == 0) - client_rectangle.Width = 1; - if (client_rectangle.Height == 0) - client_rectangle.Height = 1; - return client_rectangle; - } - set - { - WindowStyle style = (WindowStyle)Functions.GetWindowLong(window.Handle, GetWindowLongOffsets.STYLE); - Win32Rectangle rect = Win32Rectangle.From(value); - Functions.AdjustWindowRect(ref rect, style, false); - Size = new Size(rect.Width, rect.Height); - } - } - - #endregion - #region ClientSize public override Size ClientSize @@ -1045,46 +1021,6 @@ namespace OpenTK.Platform.Windows #endregion - #region Width - - public int Width - { - get { return ClientRectangle.Width; } - set { ClientRectangle = new Rectangle(0, 0, value, Height); } - } - - #endregion - - #region Height - - public int Height - { - get { return ClientRectangle.Height; } - set { ClientRectangle = new Rectangle(0, 0, Width, value); } - } - - #endregion - - #region X - - public int X - { - get { return Location.X; } - set { Location = new Point(value, Y); } - } - - #endregion - - #region Y - - public int Y - { - get { return Location.Y; } - set { Location = new Point(X, value); } - } - - #endregion - #region Icon public override Icon Icon From c98b4ea1782b3999f1c3ab367fcd4a7d6681a57e Mon Sep 17 00:00:00 2001 From: thefiddler Date: Fri, 9 May 2014 15:23:39 +0200 Subject: [PATCH 47/51] [Input] Do not hook FocusedChanged This is handled by NativeWindowBase now. --- Source/OpenTK/Input/KeyboardDevice.cs | 6 ------ Source/OpenTK/Platform/LegacyInputDriver.cs | 8 -------- 2 files changed, 14 deletions(-) diff --git a/Source/OpenTK/Input/KeyboardDevice.cs b/Source/OpenTK/Input/KeyboardDevice.cs index 4913dc41..cb64f178 100644 --- a/Source/OpenTK/Input/KeyboardDevice.cs +++ b/Source/OpenTK/Input/KeyboardDevice.cs @@ -196,12 +196,6 @@ namespace OpenTK.Input KeyUp(this, e); } - internal void ClearKeys() - { - for (Key i = 0; i < Key.LastKey; i++) - state[i] = false; - } - #if false internal void SetKey(Key key, uint scancode, KeyModifiers mods, bool pressed) { diff --git a/Source/OpenTK/Platform/LegacyInputDriver.cs b/Source/OpenTK/Platform/LegacyInputDriver.cs index 2378b91b..e2ee530d 100644 --- a/Source/OpenTK/Platform/LegacyInputDriver.cs +++ b/Source/OpenTK/Platform/LegacyInputDriver.cs @@ -69,14 +69,6 @@ namespace OpenTK.Platform // Hook keyboard events window.KeyDown += keyboard.HandleKeyDown; window.KeyUp += keyboard.HandleKeyUp; - - window.FocusedChanged += (sender, e) => - { - if (!window.Focused) - { - keyboard.ClearKeys(); - } - }; } #region IInputDriver Members From fb034de00d1c49c167347894c844aa0981c01ff9 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Fri, 9 May 2014 15:24:12 +0200 Subject: [PATCH 48/51] [Graphics] Made entry points internal Internal protected means internal *or* protected, not internal *and* protected. --- Source/OpenTK/Graphics/GraphicsBindingsBase.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/OpenTK/Graphics/GraphicsBindingsBase.cs b/Source/OpenTK/Graphics/GraphicsBindingsBase.cs index 0b3dc28d..237b8783 100644 --- a/Source/OpenTK/Graphics/GraphicsBindingsBase.cs +++ b/Source/OpenTK/Graphics/GraphicsBindingsBase.cs @@ -51,9 +51,9 @@ namespace OpenTK.Graphics protected string[] EntryPointNamesInstance; - internal protected IntPtr[] _EntryPointsInstance; - internal protected byte[] _EntryPointNamesInstance; - internal protected int[] _EntryPointNameOffsetsInstance; + internal IntPtr[] _EntryPointsInstance; + internal byte[] _EntryPointNamesInstance; + internal int[] _EntryPointNameOffsetsInstance; /// /// Retrieves an unmanaged function pointer to the specified function. From 4556e54405d87e1e691d881d899b090d587501fa Mon Sep 17 00:00:00 2001 From: thefiddler Date: Fri, 9 May 2014 16:14:13 +0200 Subject: [PATCH 49/51] [Win] Fixed infinite recursion --- Source/OpenTK/Platform/Windows/WinGLNative.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/OpenTK/Platform/Windows/WinGLNative.cs b/Source/OpenTK/Platform/Windows/WinGLNative.cs index 18598c78..74ca8c06 100644 --- a/Source/OpenTK/Platform/Windows/WinGLNative.cs +++ b/Source/OpenTK/Platform/Windows/WinGLNative.cs @@ -1008,7 +1008,7 @@ namespace OpenTK.Platform.Windows { get { - return ClientRectangle.Size; + return client_rectangle.Size; } set { From d7e037385253e9f811fdd29f8fcc81e6f6aeae1e Mon Sep 17 00:00:00 2001 From: thefiddler Date: Fri, 9 May 2014 16:45:45 +0200 Subject: [PATCH 50/51] [Platform] Fixed keys getting stuck on focus loss NativeWindowBase will now clear all keyboard keys when losing focus. This prevents keys from getting stuck when refocusing the window. [Win] Also fixed WindowState.Maximized when WindowBorder is Hidden and the window is minimized. --- .../Platform/MacOS/CocoaNativeWindow.cs | 2 ++ Source/OpenTK/Platform/NativeWindowBase.cs | 31 ++++++++++--------- .../OpenTK/Platform/SDL2/Sdl2NativeWindow.cs | 1 + Source/OpenTK/Platform/Windows/WinGLNative.cs | 12 +++---- Source/OpenTK/Platform/X11/X11GLNative.cs | 1 + 5 files changed, 26 insertions(+), 21 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs index a9fb48b2..701b0bb7 100644 --- a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs +++ b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs @@ -398,6 +398,8 @@ namespace OpenTK.Platform.MacOS public override void ProcessEvents() { + base.ProcessEvents(); + while (true) { var e = Cocoa.SendIntPtr(NSApplication.Handle, selNextEventMatchingMask, uint.MaxValue, IntPtr.Zero, NSDefaultRunLoopMode, true); diff --git a/Source/OpenTK/Platform/NativeWindowBase.cs b/Source/OpenTK/Platform/NativeWindowBase.cs index 3b1fcd39..97365f9b 100644 --- a/Source/OpenTK/Platform/NativeWindowBase.cs +++ b/Source/OpenTK/Platform/NativeWindowBase.cs @@ -109,20 +109,6 @@ namespace OpenTK.Platform protected void OnFocusedChanged(EventArgs e) { FocusedChanged(this, e); - - if (!Focused) - { - // Clear keyboard state, otherwise KeyUp - // events may be missed resulting in stuck - // keys. - for (Key key = 0; key < Key.LastKey; key++) - { - if (KeyboardState[key]) - { - OnKeyUp(key); - } - } - } } protected void OnWindowBorderChanged(EventArgs e) @@ -326,7 +312,22 @@ namespace OpenTK.Platform public abstract void Close(); - public abstract void ProcessEvents(); + public virtual void ProcessEvents() + { + if (!Focused) + { + // Clear keyboard state, otherwise KeyUp + // events may be missed resulting in stuck + // keys. + for (Key key = 0; key < Key.LastKey; key++) + { + if (KeyboardState[key]) + { + OnKeyUp(key); + } + } + } + } public abstract Point PointToClient(Point point); diff --git a/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs b/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs index 3f52e425..1e250a52 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs @@ -550,6 +550,7 @@ namespace OpenTK.Platform.SDL2 public override void ProcessEvents() { + base.ProcessEvents(); lock (sync) { if (Exists) diff --git a/Source/OpenTK/Platform/Windows/WinGLNative.cs b/Source/OpenTK/Platform/Windows/WinGLNative.cs index 74ca8c06..fdcc14b4 100644 --- a/Source/OpenTK/Platform/Windows/WinGLNative.cs +++ b/Source/OpenTK/Platform/Windows/WinGLNative.cs @@ -581,7 +581,7 @@ namespace OpenTK.Platform.Windows bool extended = (lParam.ToInt64() & ExtendedBit) != 0; short scancode = (short)((lParam.ToInt64() >> 16) & 0xff); - ushort repeat_count = unchecked((ushort)((ulong)lParam.ToInt64() & 0xffffu)); + //ushort repeat_count = unchecked((ushort)((ulong)lParam.ToInt64() & 0xffffu)); VirtualKeys vkey = (VirtualKeys)wParam; bool is_valid; Key key = WinKeyMap.TranslateKey(scancode, vkey, extended, false, out is_valid); @@ -590,7 +590,8 @@ namespace OpenTK.Platform.Windows { if (pressed) { - OnKeyDown(key, repeat_count > 0); + //OnKeyDown(key, repeat_count > 0); + OnKeyDown(key, KeyboardState[key]); } else { @@ -912,7 +913,6 @@ namespace OpenTK.Platform.Windows { suppress_resize++; WindowBorder = WindowBorder.Hidden; - ProcessEvents(); suppress_resize--; } @@ -923,7 +923,6 @@ namespace OpenTK.Platform.Windows deferred_window_border.HasValue ? deferred_window_border.Value : previous_window_border.HasValue ? previous_window_border.Value : WindowBorder; - ProcessEvents(); suppress_resize--; deferred_window_border = previous_window_border = null; } @@ -932,7 +931,6 @@ namespace OpenTK.Platform.Windows { suppress_resize++; WindowState = WindowState.Normal; - ProcessEvents(); suppress_resize--; } @@ -1247,12 +1245,12 @@ namespace OpenTK.Platform.Windows ShowWindowCommand command = 0; bool exiting_fullscreen = false; - borderless_maximized_window_state = false; switch (value) { case WindowState.Normal: command = ShowWindowCommand.RESTORE; + borderless_maximized_window_state = false; // If we are leaving fullscreen mode we need to restore the border. if (WindowState == WindowState.Fullscreen) @@ -1280,6 +1278,7 @@ namespace OpenTK.Platform.Windows } else { + borderless_maximized_window_state = false; command = ShowWindowCommand.MAXIMIZE; } break; @@ -1457,6 +1456,7 @@ namespace OpenTK.Platform.Windows MSG msg; public override void ProcessEvents() { + base.ProcessEvents(); while (Functions.PeekMessage(ref msg, IntPtr.Zero, 0, 0, PeekMessageFlags.Remove)) { Functions.TranslateMessage(ref msg); diff --git a/Source/OpenTK/Platform/X11/X11GLNative.cs b/Source/OpenTK/Platform/X11/X11GLNative.cs index e5201638..13b429d7 100644 --- a/Source/OpenTK/Platform/X11/X11GLNative.cs +++ b/Source/OpenTK/Platform/X11/X11GLNative.cs @@ -795,6 +795,7 @@ namespace OpenTK.Platform.X11 public override void ProcessEvents() { + base.ProcessEvents(); // Process all pending events while (Exists && window != null) { From 0e48ad30d0477e0599f8caf0af2bde76f26c328f Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sat, 10 May 2014 01:52:06 +0200 Subject: [PATCH 51/51] [Examples] Fixed Scroll.X/Y misreporting Scroll.X and Y are no longer swapped. --- Source/Examples/OpenTK/Test/GameWindowStates.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Examples/OpenTK/Test/GameWindowStates.cs b/Source/Examples/OpenTK/Test/GameWindowStates.cs index efb2d3bb..1977b12c 100644 --- a/Source/Examples/OpenTK/Test/GameWindowStates.cs +++ b/Source/Examples/OpenTK/Test/GameWindowStates.cs @@ -208,8 +208,8 @@ namespace Examples.Tests void MouseDeviceWheelHandler(object sender, MouseWheelEventArgs e) { - mousedevice_pos.Z = e.Mouse.Scroll.Y; - mousedevice_pos.W = e.Mouse.Scroll.X; + mousedevice_pos.Z = e.Mouse.Scroll.X; + mousedevice_pos.W = e.Mouse.Scroll.Y; mousedevice_state = e.Mouse; } @@ -241,8 +241,8 @@ namespace Examples.Tests void MouseWheelHandler(object sender, MouseWheelEventArgs e) { - mouse_pos.Z = e.Mouse.Scroll.Y; - mouse_pos.W = e.Mouse.Scroll.X; + mouse_pos.Z = e.Mouse.Scroll.X; + mouse_pos.W = e.Mouse.Scroll.Y; mouse_state = e.Mouse; }