From 94fdf1881c65f753a71d67e171eecc5dae057cf2 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 4 May 2014 01:29:55 +0200 Subject: [PATCH] [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;