[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.
This commit is contained in:
thefiddler 2014-05-04 01:29:55 +02:00
parent e85377c350
commit 94fdf1881c
16 changed files with 675 additions and 552 deletions

View file

@ -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)

View file

@ -361,398 +361,4 @@ namespace OpenTK.Input
#endregion
}
/// <summary>
/// Represents a mouse wheel.
/// </summary>
public sealed class MouseWheel
{
/// <summary>
/// Gets the X offset of the wheel.
/// </summary>
/// <value>The x.</value>
public float X { get; internal set; }
/// <summary>
/// Gets the Y offset of the wheel.
/// </summary>
/// <value>The y.</value>
public float Y { get; internal set; }
}
#region Event Arguments
/// <summary>
/// Defines the event data for <see cref="MouseDevice"/> events.
/// </summary>
/// <remarks>
/// <para>
/// Do not cache instances of this type outside their event handler.
/// If necessary, you can clone an instance using the
/// <see cref="MouseEventArgs(MouseEventArgs)"/> constructor.
/// </para>
/// </remarks>
public class MouseEventArgs : EventArgs
{
#region Fields
int x, y;
int buttons;
#endregion
#region Constructors
/// <summary>
/// Constructs a new instance.
/// </summary>
public MouseEventArgs()
{
Wheel = new MouseWheel();
}
/// <summary>
/// Constructs a new instance.
/// </summary>
/// <param name="x">The X position.</param>
/// <param name="y">The Y position.</param>
public MouseEventArgs(int x, int y)
{
this.x = x;
this.y = y;
}
/// <summary>
/// Constructs a new instance.
/// </summary>
/// <param name="args">The <see cref="MouseEventArgs"/> instance to clone.</param>
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
/// <summary>
/// Gets the X position of the mouse for the event.
/// </summary>
public int X { get { return x; } internal set { x = value; } }
/// <summary>
/// Gets the Y position of the mouse for the event.
/// </summary>
public int Y { get { return y; } internal set { y = value; } }
/// <summary>
/// Gets the status of the mouse wheel.
/// </summary>
public MouseWheel Wheel { get; private set; }
/// <summary>
/// Gets the <see cref="ButtonState"/> of the left mouse button.
/// </summary>
public ButtonState LeftButton
{
get { return GetButton(MouseButton.Left); }
internal set { SetButton(MouseButton.Left, value); }
}
/// <summary>
/// Gets the <see cref="ButtonState"/> of the right mouse button.
/// </summary>
public ButtonState RightButton
{
get { return GetButton(MouseButton.Right); }
internal set { SetButton(MouseButton.Right, value); }
}
/// <summary>
/// Gets the <see cref="ButtonState"/> of the middle mouse button.
/// </summary>
public ButtonState MiddleButton
{
get { return GetButton(MouseButton.Middle); }
internal set { SetButton(MouseButton.Middle, value); }
}
/// <summary>
/// Gets the <see cref="ButtonState"/> of the first extra mouse button.
/// </summary>
public ButtonState X1Button
{
get { return GetButton(MouseButton.Button1); }
internal set { SetButton(MouseButton.Button1, value); }
}
/// <summary>
/// Gets the <see cref="ButtonState"/> of the second extra mouse button.
/// </summary>
public ButtonState X2Button
{
get { return GetButton(MouseButton.Button2); }
internal set { SetButton(MouseButton.Button2, value); }
}
/// <summary>
/// Gets a <see cref="System.Drawing.Point"/> representing the location of the mouse for the event.
/// </summary>
public Point Position
{
get { return new Point(x, y); }
set
{
X = value.X;
Y = value.Y;
}
}
#endregion
}
/// <summary>
/// Defines the event data for <see cref="MouseDevice.Move"/> events.
/// </summary>
/// <remarks>
/// <para>
/// Do not cache instances of this type outside their event handler.
/// If necessary, you can clone an instance using the
/// <see cref="MouseMoveEventArgs(MouseMoveEventArgs)"/> constructor.
/// </para>
/// </remarks>
public class MouseMoveEventArgs : MouseEventArgs
{
#region Fields
int x_delta, y_delta;
#endregion
#region Constructors
/// <summary>
/// Constructs a new <see cref="MouseMoveEventArgs"/> instance.
/// </summary>
public MouseMoveEventArgs() { }
/// <summary>
/// Constructs a new <see cref="MouseMoveEventArgs"/> instance.
/// </summary>
/// <param name="x">The X position.</param>
/// <param name="y">The Y position.</param>
/// <param name="xDelta">The change in X position produced by this event.</param>
/// <param name="yDelta">The change in Y position produced by this event.</param>
public MouseMoveEventArgs(int x, int y, int xDelta, int yDelta)
: base(x, y)
{
XDelta = xDelta;
YDelta = yDelta;
}
/// <summary>
/// Constructs a new <see cref="MouseMoveEventArgs"/> instance.
/// </summary>
/// <param name="args">The <see cref="MouseMoveEventArgs"/> instance to clone.</param>
public MouseMoveEventArgs(MouseMoveEventArgs args)
: this(args.X, args.Y, args.XDelta, args.YDelta)
{
}
#endregion
#region Public Members
/// <summary>
/// Gets the change in X position produced by this event.
/// </summary>
public int XDelta { get { return x_delta; } internal set { x_delta = value; } }
/// <summary>
/// Gets the change in Y position produced by this event.
/// </summary>
public int YDelta { get { return y_delta; } internal set { y_delta = value; } }
#endregion
}
/// <summary>
/// Defines the event data for <see cref="MouseDevice.ButtonDown"/> and <see cref="MouseDevice.ButtonUp"/> events.
/// </summary>
/// <remarks>
/// <para>
/// Do not cache instances of this type outside their event handler.
/// If necessary, you can clone an instance using the
/// <see cref="MouseButtonEventArgs(MouseButtonEventArgs)"/> constructor.
/// </para>
/// </remarks>
public class MouseButtonEventArgs : MouseEventArgs
{
#region Fields
MouseButton button;
bool pressed;
#endregion
#region Constructors
/// <summary>
/// Constructs a new <see cref="MouseButtonEventArgs"/> instance.
/// </summary>
public MouseButtonEventArgs() { }
/// <summary>
/// Constructs a new <see cref="MouseButtonEventArgs"/> instance.
/// </summary>
/// <param name="x">The X position.</param>
/// <param name="y">The Y position.</param>
/// <param name="button">The mouse button for the event.</param>
/// <param name="pressed">The current state of the button.</param>
public MouseButtonEventArgs(int x, int y, MouseButton button, bool pressed)
: base(x, y)
{
this.button = button;
this.pressed = pressed;
}
/// <summary>
/// Constructs a new <see cref="MouseButtonEventArgs"/> instance.
/// </summary>
/// <param name="args">The <see cref="MouseButtonEventArgs"/> instance to clone.</param>
public MouseButtonEventArgs(MouseButtonEventArgs args)
: this(args.X, args.Y, args.Button, args.IsPressed)
{
}
#endregion
#region Public Members
/// <summary>
/// Gets the <see cref="MouseButton"/> that triggered this event.
/// </summary>
public MouseButton Button { get { return button; } internal set { button = value; } }
/// <summary>
/// Gets a System.Boolean representing the state of the mouse button for the event.
/// </summary>
public bool IsPressed
{
get { return GetButton(Button) == ButtonState.Pressed; }
internal set { SetButton(Button, value ? ButtonState.Pressed : ButtonState.Released); }
}
#endregion
}
/// <summary>
/// Defines the event data for <see cref="MouseDevice.WheelChanged"/> events.
/// </summary>
/// <remarks>
/// <para>
/// Do not cache instances of this type outside their event handler.
/// If necessary, you can clone an instance using the
/// <see cref="MouseWheelEventArgs(MouseWheelEventArgs)"/> constructor.
/// </para>
/// </remarks>
public class MouseWheelEventArgs : MouseEventArgs
{
#region Fields
float delta;
#endregion
#region Constructors
/// <summary>
/// Constructs a new <see cref="MouseWheelEventArgs"/> instance.
/// </summary>
public MouseWheelEventArgs() { }
/// <summary>
/// Constructs a new <see cref="MouseWheelEventArgs"/> instance.
/// </summary>
/// <param name="x">The X position.</param>
/// <param name="y">The Y position.</param>
/// <param name="value">The value of the wheel.</param>
/// <param name="delta">The change in value of the wheel for this event.</param>
public MouseWheelEventArgs(int x, int y, int value, int delta)
: base(x, y)
{
Wheel.Y = value;
this.delta = delta;
}
/// <summary>
/// Constructs a new <see cref="MouseWheelEventArgs"/> instance.
/// </summary>
/// <param name="args">The <see cref="MouseWheelEventArgs"/> instance to clone.</param>
public MouseWheelEventArgs(MouseWheelEventArgs args)
: this(args.X, args.Y, args.Value, args.Delta)
{
}
#endregion
#region Public Members
/// <summary>
/// Gets the value of the wheel in integer units.
/// To support high-precision mice, it is recommended to use <see cref="ValuePrecise"/> instead.
/// </summary>
public int Value { get { return (int)Math.Round(Wheel.Y, MidpointRounding.AwayFromZero); } }
/// <summary>
/// Gets the change in value of the wheel for this event in integer units.
/// To support high-precision mice, it is recommended to use <see cref="DeltaPrecise"/> instead.
/// </summary>
public int Delta { get { return (int)Math.Round(delta, MidpointRounding.AwayFromZero); } }
/// <summary>
/// Gets the precise value of the wheel in floating-point units.
/// </summary>
public float ValuePrecise { get { return Wheel.Y; } internal set { Wheel.Y = value; } }
/// <summary>
/// Gets the precise change in value of the wheel for this event in floating-point units.
/// </summary>
public float DeltaPrecise { get { return delta; } internal set { delta = value; } }
#endregion
}
#endregion
}

View file

@ -0,0 +1,370 @@
#region License
//
// MouseEventArgs.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// 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
{
/// <summary>
/// Defines the event data for <see cref="MouseDevice"/> events.
/// </summary>
/// <remarks>
/// <para>
/// Do not cache instances of this type outside their event handler.
/// If necessary, you can clone an instance using the
/// <see cref="MouseEventArgs(MouseEventArgs)"/> constructor.
/// </para>
/// </remarks>
public class MouseEventArgs : EventArgs
{
#region Fields
MouseState state;
#endregion
#region Constructors
/// <summary>
/// Constructs a new instance.
/// </summary>
public MouseEventArgs()
{
state.SetIsConnected(true);
}
/// <summary>
/// Constructs a new instance.
/// </summary>
/// <param name="x">The X position.</param>
/// <param name="y">The Y position.</param>
public MouseEventArgs(int x, int y)
: this()
{
state.X = x;
state.Y = y;
}
/// <summary>
/// Constructs a new instance.
/// </summary>
/// <param name="args">The <see cref="MouseEventArgs"/> instance to clone.</param>
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
/// <summary>
/// Gets the X position of the mouse for the event.
/// </summary>
public int X { get { return state.X; } internal set { state.X = value; } }
/// <summary>
/// Gets the Y position of the mouse for the event.
/// </summary>
public int Y { get { return state.Y; } internal set { state.Y = value; } }
/// <summary>
/// Gets a <see cref="System.Drawing.Point"/> representing the location of the mouse for the event.
/// </summary>
public Point Position
{
get { return new Point(state.X, state.Y); }
set
{
X = value.X;
Y = value.Y;
}
}
/// <summary>
/// Gets the current <see cref="OpenTK.Input.MouseState"/>.
/// </summary>
public MouseState Mouse
{
get { return state; }
internal set { state = value; }
}
#endregion
}
/// <summary>
/// Defines the event data for <see cref="MouseDevice.Move"/> events.
/// </summary>
/// <remarks>
/// <para>
/// Do not cache instances of this type outside their event handler.
/// If necessary, you can clone an instance using the
/// <see cref="MouseMoveEventArgs(MouseMoveEventArgs)"/> constructor.
/// </para>
/// </remarks>
public class MouseMoveEventArgs : MouseEventArgs
{
#region Fields
int x_delta, y_delta;
#endregion
#region Constructors
/// <summary>
/// Constructs a new <see cref="MouseMoveEventArgs"/> instance.
/// </summary>
public MouseMoveEventArgs() { }
/// <summary>
/// Constructs a new <see cref="MouseMoveEventArgs"/> instance.
/// </summary>
/// <param name="x">The X position.</param>
/// <param name="y">The Y position.</param>
/// <param name="xDelta">The change in X position produced by this event.</param>
/// <param name="yDelta">The change in Y position produced by this event.</param>
public MouseMoveEventArgs(int x, int y, int xDelta, int yDelta)
: base(x, y)
{
XDelta = xDelta;
YDelta = yDelta;
}
/// <summary>
/// Constructs a new <see cref="MouseMoveEventArgs"/> instance.
/// </summary>
/// <param name="args">The <see cref="MouseMoveEventArgs"/> instance to clone.</param>
public MouseMoveEventArgs(MouseMoveEventArgs args)
: this(args.X, args.Y, args.XDelta, args.YDelta)
{
}
#endregion
#region Public Members
/// <summary>
/// Gets the change in X position produced by this event.
/// </summary>
public int XDelta { get { return x_delta; } internal set { x_delta = value; } }
/// <summary>
/// Gets the change in Y position produced by this event.
/// </summary>
public int YDelta { get { return y_delta; } internal set { y_delta = value; } }
#endregion
}
/// <summary>
/// Defines the event data for <see cref="MouseDevice.ButtonDown"/> and <see cref="MouseDevice.ButtonUp"/> events.
/// </summary>
/// <remarks>
/// <para>
/// Do not cache instances of this type outside their event handler.
/// If necessary, you can clone an instance using the
/// <see cref="MouseButtonEventArgs(MouseButtonEventArgs)"/> constructor.
/// </para>
/// </remarks>
public class MouseButtonEventArgs : MouseEventArgs
{
#region Fields
MouseButton button;
bool pressed;
#endregion
#region Constructors
/// <summary>
/// Constructs a new <see cref="MouseButtonEventArgs"/> instance.
/// </summary>
public MouseButtonEventArgs() { }
/// <summary>
/// Constructs a new <see cref="MouseButtonEventArgs"/> instance.
/// </summary>
/// <param name="x">The X position.</param>
/// <param name="y">The Y position.</param>
/// <param name="button">The mouse button for the event.</param>
/// <param name="pressed">The current state of the button.</param>
public MouseButtonEventArgs(int x, int y, MouseButton button, bool pressed)
: base(x, y)
{
this.button = button;
this.pressed = pressed;
}
/// <summary>
/// Constructs a new <see cref="MouseButtonEventArgs"/> instance.
/// </summary>
/// <param name="args">The <see cref="MouseButtonEventArgs"/> instance to clone.</param>
public MouseButtonEventArgs(MouseButtonEventArgs args)
: this(args.X, args.Y, args.Button, args.IsPressed)
{
}
#endregion
#region Public Members
/// <summary>
/// Gets the <see cref="MouseButton"/> that triggered this event.
/// </summary>
public MouseButton Button { get { return button; } internal set { button = value; } }
/// <summary>
/// Gets a System.Boolean representing the state of the mouse button for the event.
/// </summary>
public bool IsPressed
{
get { return GetButton(Button) == ButtonState.Pressed; }
internal set { SetButton(Button, value ? ButtonState.Pressed : ButtonState.Released); }
}
#endregion
}
/// <summary>
/// Defines the event data for <see cref="MouseDevice.WheelChanged"/> events.
/// </summary>
/// <remarks>
/// <para>
/// Do not cache instances of this type outside their event handler.
/// If necessary, you can clone an instance using the
/// <see cref="MouseWheelEventArgs(MouseWheelEventArgs)"/> constructor.
/// </para>
/// </remarks>
public class MouseWheelEventArgs : MouseEventArgs
{
#region Fields
float delta;
#endregion
#region Constructors
/// <summary>
/// Constructs a new <see cref="MouseWheelEventArgs"/> instance.
/// </summary>
public MouseWheelEventArgs() { }
/// <summary>
/// Constructs a new <see cref="MouseWheelEventArgs"/> instance.
/// </summary>
/// <param name="x">The X position.</param>
/// <param name="y">The Y position.</param>
/// <param name="value">The value of the wheel.</param>
/// <param name="delta">The change in value of the wheel for this event.</param>
public MouseWheelEventArgs(int x, int y, int value, int delta)
: base(x, y)
{
Mouse.SetScrollAbsolute(Mouse.Scroll.X, value);
this.delta = delta;
}
/// <summary>
/// Constructs a new <see cref="MouseWheelEventArgs"/> instance.
/// </summary>
/// <param name="args">The <see cref="MouseWheelEventArgs"/> instance to clone.</param>
public MouseWheelEventArgs(MouseWheelEventArgs args)
: this(args.X, args.Y, args.Value, args.Delta)
{
}
#endregion
#region Public Members
/// <summary>
/// Gets the value of the wheel in integer units.
/// To support high-precision mice, it is recommended to use <see cref="ValuePrecise"/> instead.
/// </summary>
public int Value { get { return (int)Math.Round(Mouse.Scroll.Y, MidpointRounding.AwayFromZero); } }
/// <summary>
/// Gets the change in value of the wheel for this event in integer units.
/// To support high-precision mice, it is recommended to use <see cref="DeltaPrecise"/> instead.
/// </summary>
public int Delta { get { return (int)Math.Round(delta, MidpointRounding.AwayFromZero); } }
/// <summary>
/// Gets the precise value of the wheel in floating-point units.
/// </summary>
public float ValuePrecise
{
get { return Mouse.Scroll.Y; }
internal set { Mouse.SetScrollAbsolute(Mouse.Scroll.X, value); }
}
/// <summary>
/// Gets the precise change in value of the wheel for this event in floating-point units.
/// </summary>
public float DeltaPrecise { get { return delta; } internal set { delta = value; } }
#endregion
}
}

View file

@ -0,0 +1,119 @@
#region License
//
// MouseWheel.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// 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
{
/// <summary>
/// Represents the state of a mouse wheel.
/// </summary>
public struct MouseScrollWheel : IEquatable<MouseScrollWheel>
{
#region Public Members
/// <summary>
/// Gets the absolute horizontal offset of the wheel,
/// or 0 if no horizontal scroll wheel exists.
/// </summary>
/// <value>The x.</value>
public float X { get; internal set; }
/// <summary>
/// Gets the absolute vertical offset of the wheel,
/// or 0 if no vertical scroll wheel exists.
/// </summary>
/// <value>The y.</value>
public float Y { get; internal set; }
/// <param name="left">A <see cref="MouseScrollWheel"/> instance to test for equality.</param>
/// <param name="right">A <see cref="MouseScrollWheel"/> instance to test for equality.</param>
public static bool operator ==(MouseScrollWheel left, MouseScrollWheel right)
{
return left.Equals(right);
}
/// <param name="left">A <see cref="MouseScrollWheel"/> instance to test for inequality.</param>
/// <param name="right">A <see cref="MouseScrollWheel"/> instance to test for inequality.</param>
public static bool operator !=(MouseScrollWheel left, MouseScrollWheel right)
{
return !left.Equals(right);
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents the current <see cref="OpenTK.Input.MouseScrollWheel"/>.
/// </summary>
/// <returns>A <see cref="System.String"/> that represents the current <see cref="OpenTK.Input.MouseScrollWheel"/>.</returns>
public override string ToString()
{
return string.Format("[MouseScrollWheel: X={0}, Y={1}]", X, Y);
}
/// <summary>
/// Serves as a hash function for a <see cref="OpenTK.Input.MouseScrollWheel"/> object.
/// </summary>
/// <returns>A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
/// hash table.</returns>
public override int GetHashCode()
{
return X.GetHashCode() ^ Y.GetHashCode();
}
/// <summary>
/// Determines whether the specified <see cref="System.Object"/> is equal to the current <see cref="OpenTK.Input.MouseScrollWheel"/>.
/// </summary>
/// <param name="obj">The <see cref="System.Object"/> to compare with the current <see cref="OpenTK.Input.MouseScrollWheel"/>.</param>
/// <returns><c>true</c> if the specified <see cref="System.Object"/> is equal to the current
/// <see cref="OpenTK.Input.MouseScrollWheel"/>; otherwise, <c>false</c>.</returns>
public override bool Equals(object obj)
{
return
obj is MouseScrollWheel &&
Equals((MouseScrollWheel)obj);
}
#endregion
#region IEquatable Members
/// <summary>
/// Determines whether the specified <see cref="OpenTK.Input.MouseScrollWheel"/> is equal to the current <see cref="OpenTK.Input.MouseScrollWheel"/>.
/// </summary>
/// <param name="other">The <see cref="OpenTK.Input.MouseScrollWheel"/> to compare with the current <see cref="OpenTK.Input.MouseScrollWheel"/>.</param>
/// <returns><c>true</c> if the specified <see cref="OpenTK.Input.MouseScrollWheel"/> is equal to the current
/// <see cref="OpenTK.Input.MouseScrollWheel"/>; otherwise, <c>false</c>.</returns>
public bool Equals(MouseScrollWheel other)
{
return X == other.X && Y == other.Y;
}
#endregion
}
}

View file

@ -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
/// </summary>
public int Wheel
{
get { return (int)Math.Round(wheel, MidpointRounding.AwayFromZero); }
get { return (int)Math.Round(scroll.Y, MidpointRounding.AwayFromZero); }
}
/// <summary>
@ -101,11 +97,16 @@ namespace OpenTK.Input
/// </summary>
public float WheelPrecise
{
get { return wheel; }
internal set
{
wheel = value;
get { return scroll.Y; }
}
/// <summary>
/// Gets a <see cref="OpenTK.Input.MouseScrollWheel"/> instance,
/// representing the current state of the mouse scroll wheel.
/// </summary>
public MouseScrollWheel Scroll
{
get { return scroll; }
}
/// <summary>
@ -253,13 +254,7 @@ namespace OpenTK.Input
/// </returns>
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
/// <returns>True, if both instances are equal; false otherwise.</returns>
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

View file

@ -798,6 +798,8 @@
<Compile Include="WindowIcon.cs" />
<Compile Include="Platform\MacOS\Cocoa\NSBitmapFormat.cs" />
<Compile Include="Platform\NativeWindowBase.cs" />
<Compile Include="Input\MouseScrollWheel.cs" />
<Compile Include="Input\MouseEventArgs.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup>

View file

@ -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

View file

@ -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;
}

View file

@ -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
{

View file

@ -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()
{
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;
}
}
protected void OnMouseUp(MouseButtonEventArgs e)
Debug.WriteLine("OnMouseDown called without pressing a button");
}
protected void OnMouseUp()
{
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;
}
}
protected void OnMouseMove(MouseMoveEventArgs e)
Debug.WriteLine("OnMouseUp called without pressing a button");
}
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);
}

View file

@ -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)

View file

@ -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)

View file

@ -3591,7 +3591,8 @@ namespace OpenTK.Platform.Windows
BUTTON_5_DOWN = 0x0100,
BUTTON_5_UP = 0x0200,
WHEEL = 0x0400
WHEEL = 0x0400,
HWHEEL = 0x0800,
}
#endregion

View file

@ -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)

View file

@ -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)
{

View file

@ -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;