/InputSystem/Windows/WindowsTouch.cs
C# | 390 lines | 208 code | 53 blank | 129 comment | 24 complexity | 15253c6cd4ae43a7e942130c7a94d11d MD5 | raw file
Possible License(s): Apache-2.0
- using System;
- using System.Collections.Generic;
- using System.Runtime.InteropServices;
- using Delta.Engine;
- using Delta.InputSystem.Devices;
- using Delta.Platforms.Windows;
- using Delta.Utilities;
- using Delta.Utilities.Datatypes;
- using Delta.Utilities.Helpers;
- using WinMessage = System.Windows.Forms.Message;
-
- namespace Delta.InputSystem.Windows
- {
- /// <summary>
- /// Windows multi-touch implementation. Only works on Windows 7 because this
- /// is part of the Windows 7 SDK. For references and getting started see:
- /// http://msdn.microsoft.com/en-us/library/dd371581%28VS.85%29.aspx
- /// </summary>
- public class WindowsTouch : BaseTouch
- {
- #region NativeTouchInput Struct
- /// <summary>
- /// Touch API defined structures [winuser.h]
- /// @see: http://msdn.microsoft.com/en-us/library/dd562194%28VS.85%29.aspx
- /// Note: We changed the naming a bit, we don't want C++ naming here.
- /// </summary>
- [StructLayout(LayoutKind.Sequential)]
- private struct NativeTouchInput
- {
- #region x (Public)
- public readonly int x;
- #endregion
-
- #region y (Public)
- public readonly int y;
- #endregion
-
- #region sourcePointer (Public)
- public readonly IntPtr sourcePointer;
- #endregion
-
- #region id (Public)
- public readonly int id;
- #endregion
-
- #region flags (Public)
- public readonly int flags;
- #endregion
-
- #region mask (Public)
- public readonly int mask;
- #endregion
-
- #region timeStamp (Public)
- public readonly int timeStamp;
- #endregion
-
- #region extraInfo (Public)
- public readonly IntPtr extraInfo;
- #endregion
-
- #region contactX (Public)
- public readonly int contactX;
- #endregion
-
- #region contactY (Public)
- public readonly int contactY;
- #endregion
- }
- #endregion
-
- #region Constants
- /// <summary>
- /// Touch input flag for a Move event. (-> @see WinUser.h)
- /// </summary>
- private const int TouchInputMove = 0x0001;
-
- /// <summary>
- /// Touch input flag for a Down event. (-> @see WinUser.h)
- /// </summary>
- private const int TouchInputDown = 0x0002;
-
- /// <summary>
- /// Touch input flag for an Up event. (-> @see WinUser.h)
- /// </summary>
- private const int TouchInputUp = 0x0004;
-
- /// <summary>
- /// WM_TOUCH event that will be enabled in the constructor via the native
- /// RegisterTouchWindow method.
- /// </summary>
- private const int WM_TOUCH = 0x0240;
- #endregion
-
- #region IsConnected (Public)
- /// <summary>
- /// Is connected
- /// </summary>
- public override bool IsConnected
- {
- get
- {
- return isTouchEnabled &&
- //Note: Currently a one-time thingy, see OnNativeEvent
- // This should not be here, but for testing it is..
- HasData;
- } // get
- }
- #endregion
-
- #region Private
-
- #region TouchCursorByteSize (Private)
- /// <summary>
- /// Constant size in bytes of the "TouchCursor" helper struct.
- /// </summary>
- private static int TouchCursorByteSize;
- #endregion
-
- // IsConnected
-
- #region win7Window (Private)
- /// <summary>
- /// The native windows from where we will receive the touch data updates.
- /// </summary>
- private readonly Window win7Window;
- #endregion
-
- #region isTouchEnabled (Private)
- /// <summary>
- /// Is touch enabled? If this is true we can use this class (IsConnected
- /// will return true if Settings.Debug.IsTouchEnabled is not false).
- /// </summary>
- private readonly bool isTouchEnabled;
- #endregion
-
- #region nativeTouches (Private)
- /// <summary>
- /// Cached list of native touches we receive in the OnNativeEvent event
- /// (if it is a WM_TOUCH). We can receive multiple native touches at once
- /// and even multiple times per frame, so we just go through this list
- /// from start to finish in the Update method and clear it out again!
- /// </summary>
- private readonly List<NativeTouchInput> nativeTouches =
- new List<NativeTouchInput>();
- #endregion
-
- #endregion
-
- #region Constructors
- /// <summary>
- /// Create windows touch
- /// </summary>
- public WindowsTouch()
- {
- // If touch input is disabled in settings, we don't need to continue
- // here, just abort. isTouchEnabled defaults to false.
- if (Settings.Modules.IsTouchEnabled == false)
- {
- return;
- }
-
- try
- {
- win7Window = Application.Window as Window;
- // Note: If we are not in Windows 7, we don't even try this!
- if (win7Window != null &&
- // Windows 7 is Windows NT 6.1 internally
- Environment.OSVersion.VersionString.Contains("Windows NT 6.1."))
- {
- // Register our window form on Windows 7, but don't use any extra
- // parameters yet. For more details see:
- // http://msdn.microsoft.com/en-us/library/dd317326%28VS.85%29.aspx
- isTouchEnabled = RegisterTouchWindow(win7Window.Handle, 0);
- TouchCursorByteSize = Marshal.SizeOf(typeof(NativeTouchInput));
- win7Window.NativeEvent += OnNativeEvent;
- } // if
- } // try
- catch (Exception ex)
- {
- // Note: This warning should only be outputted on Windows 7!
- Log.Warning("Failed to initialize TouchStateUpdater for " +
- "WindowsTouch, unable to use touch input from WIndows 7 " +
- "multi-touch support! " + ex);
- }
- }
- #endregion
-
- #region Methods (Private)
-
- // These DLLImports are only available on Windows 7.
- // What happens when we try to use this on other platforms like XP?
-
- #region RegisterTouchWindow
- [DllImport("User32")]
- private static extern bool RegisterTouchWindow(IntPtr handle,
- UInt32 flags);
- #endregion
-
- #region UnregisterTouchWindow
- [DllImport("User32")]
- private static extern bool UnregisterTouchWindow(IntPtr handle);
- #endregion
-
- #region CloseTouchInputHandle
- [DllImport("user32")]
- private static extern void CloseTouchInputHandle(IntPtr lParam);
- #endregion
-
- #region GetTouchInputInfo
- [DllImport("user32")]
- private static extern bool GetTouchInputInfo(IntPtr hTouchInput,
- int cInputs, [In, Out] NativeTouchInput[] pInputs, int cbSize);
- #endregion
-
- #region OnNativeEvent
- /// <summary>
- /// Handle windows message, processes only if something happens
- /// </summary>
- /// <param name="message">Message</param>
- private void OnNativeEvent(ref WinMessage message)
- {
- // Try catch up the (for us interesting) touch events
- switch (message.Msg)
- {
- // WM_TOUCH (-> @see "winuser.h" for the window message constants)
- case WM_TOUCH:
- // Acknowledge event that we will handle it now
- message.Result = new IntPtr(1);
- break;
-
- default:
- return;
- }
-
- // We got new data!
- HasData = true;
-
- // Extract the current number of active touch points
- int usedNativeTouches = (message.WParam.ToInt32() & 0xffff);
- /*don't care anymore
- // Make sure we never go above MaxNumberOfTouches
- if (usedNativeTouches > MaxNumberOfTouches)
- {
- usedNativeTouches = MaxNumberOfTouches;
- }
- */
-
- // So we know how many current touch inputs we will receive now
- //Log.Info("nativeTouches received: usedNativeTouches="+usedNativeTouches);
- NativeTouchInput[] newTouches = new NativeTouchInput[usedNativeTouches];
- bool isTouchInputProcessed = GetTouchInputInfo(message.LParam,
- usedNativeTouches, newTouches, TouchCursorByteSize);
-
- // If we don't get the data (shouldn't normally happen)
- if (isTouchInputProcessed == false)
- {
- // Happens if something was bad like, invalid params, but will not
- // execute if nothing happen resp. noone touches....
- Log.Warning("Windows 7 Touch error, no input processed " +
- "(we got no touches now)");
- return;
- } // if
-
- // Just add the received touches to our nativeTouches list
- nativeTouches.AddRange(newTouches);
-
- // Before we finally close up the handling, we notify the OS that we
- // have finished our "work"
- CloseTouchInputHandle(message.LParam);
- //Log.Info("nativeTouches end: usedNativeTouches=" + usedNativeTouches);
- }
- #endregion
-
- #region Update
- /// <summary>
- /// Update
- /// </summary>
- protected override void Update()
- {
- if (IsConnected == false)
- {
- return;
- }
-
- //Log.Info("nativeTouches=" + nativeTouches.Count +
- // ", ActiveTouches=" + ActiveTouches);
-
- // In any case (receive any touches this frame or not), we need to check
- // which touches are still active and update their states (mostly just
- // going from Pressed to IsPressed and Release to NotPressed).
- base.UpdateAndResetTouchStates();
-
- // Handle all collected native touch events
- for (int index = 0; index < nativeTouches.Count; index++)
- {
- // Note: We could have received several native touches with the same
- // id, then we will update the same Touches in our array (see below).
- NativeTouchInput touch = nativeTouches[index];
-
- // Touch space conversion to Quadratic Space. We have to convert the
- // raw touch coordinates first in Pixel Space of the "OS Desktop Area"
- // because they are scale by 100x (@see: WinUser.h (line 5382))
- // "#define TOUCH_COORD_TO_PIXEL(l) ((l) / 100)"
- // And finally convert in our Quadratic Space for the Viewport
- Point quadPos = ScreenSpace.ToQuadraticSpace(win7Window.DesktopToViewport(
- touch.x / 100, touch.y / 100));
-
- // Search for the touch id in the existing list
- int existingIndex = base.GetOrCreateIndexFromTouchId(touch.id);
- if (existingIndex == MathHelper.InvalidIndex)
- {
- continue;
- }
-
- // Convert into our touch state
- InputState touchState = InputState.NotPressed;
- if ((touch.flags & TouchInputDown) != 0)
- {
- // The initial touch input down is Pressed, then it goes to
- // IsPressed! This is because we sometimes get TouchInputDown
- // several times for the same press (and this would mess up our
- // state logic).
- // InputState.Pressed : InputState.IsPressed;
- touchState = InputState.Pressed;
-
- // And store some extra states for gestures detection.
- base.SetInitialTouchPressData(quadPos);
- }
- //Note: Move is also handled separately below!
- else if ((touch.flags & TouchInputMove) != 0)
- {
- // Only process this move if we did not just entered Pressed state.
- // Please note that Pressed will be changed to IsPressed
- // automatically above.
- touchState = Touches[existingIndex].State == InputState.Pressed
- ? InputState.Pressed
- : InputState.IsPressed;
- }
- else if ((touch.flags & TouchInputUp) != 0)
- {
- touchState = InputState.Released;
- }
- else
- {
- Log.Warning("Invalid touch flags, can't handle: " + touch.flags);
- }
-
- //Log.Info("native touch state " + existingIndex + "=" + touchState +
- // ", id=" + touch.id +
- // ", native TouchInputDown=" + (touch.flags & TouchInputDown) +
- // ", native TouchInputMove=" + (touch.flags & TouchInputMove) +
- // ", native TouchInputUp=" + (touch.flags & TouchInputUp));
-
- // This either updates or creates a new touch
- Touches[existingIndex].Set(touch.id,
- // We need to convert from screen space to quadratic space
- // note Check if the device orientation working properly??
- quadPos,
- // Convert from TouchLocationState to our InputState enum.
- touchState,
- (touch.flags & TouchInputMove) != 0,
- // We got no tap count data in Windows 7
- 0,
- // But we can use the touch time (in ms)
- Time.Milliseconds);
-
- if (touchState == InputState.Pressed)
- {
- SetInitialTouchPressData(quadPos);
- }
- } // for
-
- // We handled all the caught events, reset for the next update.
- nativeTouches.Clear();
-
- // Tell us how many touches are currently active, this is very important
- // to make sure we only iterate this far through the Touches array.
- // Note: We also do not need to clear the rest of the Touches array
- // because it is not supposed to be used anyway!
- //disabled for now: ActiveTouches = numberOfTouches;
-
- // The rest of the code is the same for all touch device implementations
- base.HandleStatesAndGestures();
- }
- #endregion
-
- #endregion
- }
- }