Hi,
I still have the Windows Procedure message 281 that gives {begin ; parsed content by Windows at each instant ; end} events in Rhinoceros 5.14 and only {begin ; end} in Rhinoceros 7.16.
I have disabled the parameter described in this thread, given that I’d like to have the Windows native behavior of the message 281, and the Rhinoceros commands runned by its UI or its scripts by tapping the screen.
This is a critical feature on the plugins I develop. I have reported below the comparison with the code I have around my plugins to handle this feature. If you need more info let me know.
The link is made with DllImport and Rhinocommon in .NET 4.8 as the following signatures:
public struct GESTUREINFO
{
public uint cbSize;
public uint dwFlags;
public uint dwID;
public IntPtr hwndTarget;
public POINTS ptsLocation;
public uint dwInstanceID;
public uint dwSequenceID;
public ulong ullArguments;
public uint cbExtraArgs;
}
internal static class User32
{
public const int WM_NCDESTROY = 130;
public const int GWLP_WNDPROC = -4;
public const int WM_TOUCH = 576;
public const int TOUCHEVENTF_MOVE = 1;
public const int TOUCHEVENTF_DOWN = 2;
public const int TOUCHEVENTF_UP = 4;
public const int TOUCHEVENTF_INRANGE = 8;
public const int TOUCHEVENTF_PRIMARY = 16;
public const int TOUCHEVENTF_NOCOALESCE = 32;
public const int TOUCHEVENTF_PEN = 64;
public const int TOUCHEVENTF_PALM = 128;
public const int TOUCHINPUTMASKF_TIMEFROMSYSTEM = 1;
public const int TOUCHINPUTMASKF_EXTRAINFO = 2;
public const int TOUCHINPUTMASKF_CONTACTAREA = 4;
public const uint GC_ALLGESTURES = 1;
public const uint WM_GESTURE = 281;
public const uint WM_GESTURENOTIFY = 282;
public const uint GF_BEGIN = 1;
public const uint GF_INERTIA = 2;
public const uint GF_END = 4;
public const uint GID_BEGIN = 1;
public const uint GID_END = 2;
public const uint GID_ZOOM = 3;
public const uint GID_PAN = 4;
public const uint GID_ROTATE = 5;
public const uint GID_TWOFINGERTAP = 6;
public const uint GID_PRESSANDTAP = 7;
[DllImport("user32")]
public static extern bool SetProcessDPIAware();
[DllImport("user32")]
public static extern bool IsWindow(IntPtr hWnd);
[DllImport("user32")]
public static extern bool ScreenToClient(IntPtr hWnd, ref POINT lpPoint);
[DllImport("user32", EntryPoint = "SetWindowLongPtr")]
public static extern IntPtr SubclassWindow64(
IntPtr hWnd,
int nIndex,
User32.WindowProcDelegate dwNewLong);
[DllImport("user32", EntryPoint = "SetWindowLong")]
public static extern IntPtr SubclassWindow(
IntPtr hWnd,
int nIndex,
User32.WindowProcDelegate dwNewLong);
[DllImport("user32")]
public static extern uint CallWindowProc(
IntPtr prevWndFunc,
IntPtr hWnd,
int msg,
IntPtr wparam,
IntPtr lparam);
[DllImport("user32", EntryPoint = "GetSystemMetrics")]
public static extern int GetDigitizerCapabilities(User32.DigitizerIndex index);
[DllImport("user32")]
public static extern bool RegisterTouchWindow(IntPtr hWnd, User32.TouchWindowFlag flags);
[DllImport("user32")]
public static extern bool UnregisterTouchWindow(IntPtr hWnd);
[DllImport("user32")]
public static extern bool IsTouchWindow(IntPtr hWnd, out uint ulFlags);
[DllImport("user32")]
public static extern bool GetTouchInputInfo(
IntPtr hTouchInput,
int cInputs,
[In, Out] TOUCHINPUT[] pInputs,
int cbSize);
[DllImport("user32")]
public static extern void CloseTouchInputHandle(IntPtr lParam);
[DllImport("user32")]
public static extern bool SetProp(IntPtr hWnd, string lpString, IntPtr hData);
public static ushort LoWord(uint number) => (ushort) (number & (uint) ushort.MaxValue);
public static ushort HiWord(uint number) => (ushort) (number >> 16 & (uint) ushort.MaxValue);
public static uint LoDWord(ulong number) => (uint) (number & (ulong) uint.MaxValue);
public static uint HiDWord(ulong number) => (uint) (number >> 32 & (ulong) uint.MaxValue);
public static short LoWord(int number) => (short) number;
public static short HiWord(int number) => (short) (number >> 16);
public static int LoDWord(long number) => (int) number;
public static int HiDWord(long number) => (int) (number >> 32);
[DllImport("user32")]
public static extern bool SetGestureConfig(
IntPtr hwnd,
uint dwReserved,
uint cIDs,
GESTURECONFIG[] pGestureConfig,
uint cbSize);
[DllImport("user32")]
public static extern bool GetGestureInfo(IntPtr hGestureInfo, ref GESTUREINFO pGestureInfo);
public static ushort GID_ROTATE_ANGLE_TO_ARGUMENT(ushort arg) => (ushort) (((double) arg + 6.2831853) / 12.5663706 * (double) ushort.MaxValue);
public static double GID_ROTATE_ANGLE_FROM_ARGUMENT(ushort arg) => (double) arg / (double) ushort.MaxValue * 4.0 * 3.14159265 - 6.2831853;
[DllImport("user32")]
public static extern bool CloseGestureInfoHandle(IntPtr hGestureInfo);
public delegate uint WindowProcDelegate(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam);
public enum DigitizerIndex
{
SM_DIGITIZER = 94, // 0x0000005E
SM_MAXIMUMTOUCHES = 95, // 0x0000005F
}
public enum TouchWindowFlag : uint
{
FineTouch = 1,
WantPalm = 2,
}
public struct GESTURENOTIFYSTRUCT
{
public uint cbSize;
public uint dwFlags;
public IntPtr hwndTarget;
public POINTS ptsLocation;
public uint dwInstanceID;
}
}
public abstract class Handler
{
private readonly IHwndWrapper _hWndWrapper;
private static List<object> _controlInUse = new List<object>();
private User32.WindowProcDelegate _windowProcDelegate;
private IntPtr _originalWindowProcId;
protected abstract bool SetHWndTouchInfo();
protected abstract uint WindowProc(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam);
internal static T CreateHandler<T>(IHwndWrapper hWndWrapper) where T : Handler
{
if (Handler._controlInUse.Contains(hWndWrapper.Source))
throw new Exception("Only one handler can be registered for a control.");
hWndWrapper.HandleDestroyed += (EventHandler) ((s, e) => Handler._controlInUse.Remove(s));
Handler._controlInUse.Add(hWndWrapper.Source);
return Activator.CreateInstance(typeof (T), BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod | BindingFlags.CreateInstance, (Binder) null, new object[1]
{
(object) hWndWrapper
}, Thread.CurrentThread.CurrentCulture) as T;
}
internal Handler(IHwndWrapper hWndWrapper)
{
this._hWndWrapper = hWndWrapper;
if (this._hWndWrapper.IsHandleCreated)
this.Initialize();
else
this._hWndWrapper.HandleCreated += (EventHandler) ((s, e) => this.Initialize());
}
private void Initialize()
{
if (!this.SetHWndTouchInfo())
throw new NotSupportedException("Cannot register window");
this._windowProcDelegate = new User32.WindowProcDelegate(this.WindowProcSubClass);
this._originalWindowProcId = IntPtr.Size == 4 ? User32.SubclassWindow(this._hWndWrapper.Handle, -4, this._windowProcDelegate) : User32.SubclassWindow64(this._hWndWrapper.Handle, -4, this._windowProcDelegate);
using (Graphics graphics = Graphics.FromHwnd(this._hWndWrapper.Handle))
{
this.DpiX = graphics.DpiX;
this.DpiY = graphics.DpiY;
}
this.WindowMessage += (EventHandler<WMEventArgs>) ((s, e) => {});
}
private uint WindowProcSubClass(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam)
{
this.WindowMessage((object) this, new WMEventArgs(hWnd, msg, wparam, lparam));
if (msg == 282 && this.GestureNotify != null)
{
this.GestureNotify((object) this, new GestureNotifyEventArgs(lparam));
}
else
{
uint num = this.WindowProc(hWnd, msg, wparam, lparam);
if (num != 0U)
return num;
}
return User32.CallWindowProc(this._originalWindowProcId, hWnd, msg, wparam, lparam);
}
internal IHwndWrapper HWndWrapper => this._hWndWrapper;
protected IntPtr ControlHandle => !this._hWndWrapper.IsHandleCreated ? IntPtr.Zero : this._hWndWrapper.Handle;
public float DpiX { get; private set; }
public float DpiY { get; private set; }
public event EventHandler<GestureNotifyEventArgs> GestureNotify;
internal event EventHandler<WMEventArgs> WindowMessage;
public static bool IsTouchWindows(IntPtr hWnd) => User32.IsTouchWindow(hWnd, out uint _);
public static class DigitizerCapabilities
{
public static DigitizerStatus Status => (DigitizerStatus) User32.GetDigitizerCapabilities(User32.DigitizerIndex.SM_DIGITIZER);
public static int MaxumumTouches => User32.GetDigitizerCapabilities(User32.DigitizerIndex.SM_MAXIMUMTOUCHES);
public static bool IsIntegratedTouch => (Handler.DigitizerCapabilities.Status & DigitizerStatus.IntegratedTouch) != (DigitizerStatus) 0;
public static bool IsExternalTouch => (Handler.DigitizerCapabilities.Status & DigitizerStatus.ExternalTouch) != (DigitizerStatus) 0;
public static bool IsIntegratedPan => (Handler.DigitizerCapabilities.Status & DigitizerStatus.IntegratedPan) != (DigitizerStatus) 0;
public static bool IsExternalPan => (Handler.DigitizerCapabilities.Status & DigitizerStatus.ExternalPan) != (DigitizerStatus) 0;
public static bool IsMultiInput => (Handler.DigitizerCapabilities.Status & DigitizerStatus.MultiInput) != (DigitizerStatus) 0;
public static bool IsStackReady => (Handler.DigitizerCapabilities.Status & DigitizerStatus.StackReady) != (DigitizerStatus) 0;
public static bool IsMultiTouchReady => (Handler.DigitizerCapabilities.Status & (DigitizerStatus.MultiInput | DigitizerStatus.StackReady)) != (DigitizerStatus) 0;
}
}
public class GestureHandler : Handler
{
private static readonly EventHandler<GestureEventArgs> _emptyFunc = (EventHandler<GestureEventArgs>)((s, e) => { });
private readonly Dictionary<uint, EventHandler<GestureEventArgs>> _eventMap = new Dictionary<uint, EventHandler<GestureEventArgs>>()
{
{
GestureHandler.EventMapID.Begin,
GestureHandler._emptyFunc
},
{
GestureHandler.EventMapID.End,
GestureHandler._emptyFunc
},
{
GestureHandler.EventMapID.PanBegin,
GestureHandler._emptyFunc
},
{
GestureHandler.EventMapID.Pan,
GestureHandler._emptyFunc
},
{
GestureHandler.EventMapID.PanEnd,
GestureHandler._emptyFunc
},
{
GestureHandler.EventMapID.PressAndTap,
GestureHandler._emptyFunc
},
{
GestureHandler.EventMapID.RotateBegin,
GestureHandler._emptyFunc
},
{
GestureHandler.EventMapID.Rotate,
GestureHandler._emptyFunc
},
{
GestureHandler.EventMapID.RotateEnd,
GestureHandler._emptyFunc
},
{
GestureHandler.EventMapID.TwoFingerTap,
GestureHandler._emptyFunc
},
{
GestureHandler.EventMapID.ZoomBegin,
GestureHandler._emptyFunc
},
{
GestureHandler.EventMapID.Zoom,
GestureHandler._emptyFunc
},
{
GestureHandler.EventMapID.ZoomEnd,
GestureHandler._emptyFunc
}
};
private static uint MapWM2EventId(uint dwID, uint dwFlags) => (uint)(((int)dwID << 3) + (dwID == 6U || dwID == 7U || (dwID == 1U || dwID == 2U) ? 0 : (int)dwFlags & 5));
internal GestureHandler(IHwndWrapper hWndWrapper)
: base(hWndWrapper)
{
}
protected override bool SetHWndTouchInfo()
{
var gestureConfig = new GESTURECONFIG[]
{
new GESTURECONFIG() { dwID = 3U, dwWant = 1U, dwBlock = 0U },
new GESTURECONFIG() { dwID = 4U, dwWant = 1U, dwBlock = 0U },
new GESTURECONFIG() { dwID = 5U, dwWant = 1U, dwBlock = 0U },
new GESTURECONFIG() { dwID = 6U, dwWant = 1U, dwBlock = 0U }
};
var result = User32.SetGestureConfig(
this.ControlHandle,
0U,
(uint)gestureConfig.Length,
gestureConfig,
(uint)Marshal.SizeOf(typeof(GESTURECONFIG)));
return result;
}
internal GestureEventArgs LastBeginEvent { get; set; }
internal GestureEventArgs LastEvent { get; set; }
public event EventHandler<GestureEventArgs> Begin
{
add => this._eventMap[GestureHandler.EventMapID.Begin] += value;
remove => this._eventMap[GestureHandler.EventMapID.Begin] -= value;
}
public event EventHandler<GestureEventArgs> End
{
add => this._eventMap[GestureHandler.EventMapID.End] += value;
remove => this._eventMap[GestureHandler.EventMapID.End] -= value;
}
public event EventHandler<GestureEventArgs> PanBegin
{
add => this._eventMap[GestureHandler.EventMapID.PanBegin] += value;
remove => this._eventMap[GestureHandler.EventMapID.PanBegin] -= value;
}
public event EventHandler<GestureEventArgs> Pan
{
add => this._eventMap[GestureHandler.EventMapID.Pan] += value;
remove => this._eventMap[GestureHandler.EventMapID.Pan] -= value;
}
public event EventHandler<GestureEventArgs> PanEnd
{
add => this._eventMap[GestureHandler.EventMapID.PanEnd] += value;
remove => this._eventMap[GestureHandler.EventMapID.PanEnd] -= value;
}
public event EventHandler<GestureEventArgs> PressAndTap
{
add => this._eventMap[GestureHandler.EventMapID.PressAndTap] += value;
remove => this._eventMap[GestureHandler.EventMapID.PressAndTap] -= value;
}
public event EventHandler<GestureEventArgs> RotateBegin
{
add => this._eventMap[GestureHandler.EventMapID.RotateBegin] += value;
remove => this._eventMap[GestureHandler.EventMapID.RotateBegin] -= value;
}
public event EventHandler<GestureEventArgs> Rotate
{
add => this._eventMap[GestureHandler.EventMapID.Rotate] += value;
remove => this._eventMap[GestureHandler.EventMapID.Rotate] -= value;
}
public event EventHandler<GestureEventArgs> RotateEnd
{
add => this._eventMap[GestureHandler.EventMapID.RotateEnd] += value;
remove => this._eventMap[GestureHandler.EventMapID.RotateEnd] -= value;
}
public event EventHandler<GestureEventArgs> TwoFingerTap
{
add => this._eventMap[GestureHandler.EventMapID.TwoFingerTap] += value;
remove => this._eventMap[GestureHandler.EventMapID.TwoFingerTap] -= value;
}
public event EventHandler<GestureEventArgs> ZoomBegin
{
add => this._eventMap[GestureHandler.EventMapID.ZoomBegin] += value;
remove => this._eventMap[GestureHandler.EventMapID.ZoomBegin] -= value;
}
public event EventHandler<GestureEventArgs> Zoom
{
add => this._eventMap[GestureHandler.EventMapID.Zoom] += value;
remove => this._eventMap[GestureHandler.EventMapID.Zoom] -= value;
}
public event EventHandler<GestureEventArgs> ZoomEnd
{
add => this._eventMap[GestureHandler.EventMapID.ZoomEnd] += value;
remove => this._eventMap[GestureHandler.EventMapID.ZoomEnd] -= value;
}
protected override uint WindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam)
{
if (msg != 281)
{
return 0;
}
GESTUREINFO gestureinfo = new GESTUREINFO()
{
cbSize = (uint)Marshal.SizeOf(typeof(GESTUREINFO))
};
GestureEventArgs e = User32.GetGestureInfo(lParam, ref gestureinfo) ? new GestureEventArgs(this, ref gestureinfo) : throw new Exception("Cannot get gesture information");
try
{
var mapWM2EventId = GestureHandler.MapWM2EventId(gestureinfo.dwID, gestureinfo.dwFlags);
this._eventMap[mapWM2EventId]((object)this, e);
var message = string.Format(
"time='{0}' mapWM2EventId='{1}'",
DateTime.Now.ToLongTimeString(),
mapWM2EventId
);
System.Diagnostics.Trace.WriteLine(message);
}
catch (ArgumentOutOfRangeException ex)
{
}
this.LastEvent = e;
if (e.IsBegin)
this.LastBeginEvent = e;
if (gestureinfo.dwID != 6U && gestureinfo.dwID != 7U)
return 0;
User32.CloseGestureInfoHandle(lParam);
return 1;
}
private static class EventMapID
{
public static readonly uint Begin = GestureHandler.MapWM2EventId(1U, 0U);
public static readonly uint End = GestureHandler.MapWM2EventId(2U, 0U);
public static readonly uint PanBegin = GestureHandler.MapWM2EventId(4U, 1U);
public static readonly uint Pan = GestureHandler.MapWM2EventId(4U, 0U);
public static readonly uint PanEnd = GestureHandler.MapWM2EventId(4U, 4U);
public static readonly uint PressAndTap = GestureHandler.MapWM2EventId(7U, 0U);
public static readonly uint RotateBegin = GestureHandler.MapWM2EventId(5U, 1U);
public static readonly uint Rotate = GestureHandler.MapWM2EventId(5U, 0U);
public static readonly uint RotateEnd = GestureHandler.MapWM2EventId(5U, 4U);
public static readonly uint TwoFingerTap = GestureHandler.MapWM2EventId(6U, 0U);
public static readonly uint ZoomBegin = GestureHandler.MapWM2EventId(3U, 1U);
public static readonly uint Zoom = GestureHandler.MapWM2EventId(3U, 0U);
public static readonly uint ZoomEnd = GestureHandler.MapWM2EventId(3U, 4U);
}
}
public class GestureEventArgs : EventArgs
{
private readonly uint _dwFlags;
internal GestureEventArgs(GestureHandler handler, ref GESTUREINFO gestureInfo)
{
this._dwFlags = gestureInfo.dwFlags;
this.GestureId = gestureInfo.dwID;
this.GestureArguments = gestureInfo.ullArguments;
this.LastEvent = handler.LastEvent;
this.LastBeginEvent = handler.LastBeginEvent;
this.DecodeGesture(handler.HWndWrapper, ref gestureInfo);
if (!this.IsBegin)
return;
this.LastBeginEvent = (GestureEventArgs) null;
this.LastEvent = (GestureEventArgs) null;
}
private void DecodeGesture(IHwndWrapper hWndWrapper, ref GESTUREINFO gestureInfo)
{
this.Location = hWndWrapper.PointToClient(new Point((int) gestureInfo.ptsLocation.x, (int) gestureInfo.ptsLocation.y));
this.Center = this.Location;
switch (this.GestureId)
{
case 3:
Point point = this.IsBegin ? this.Location : this.LastBeginEvent.Location;
this.Center = new Point((this.Location.X + point.X) / 2, (this.Location.Y + point.Y) / 2);
this.ZoomFactor = this.IsBegin ? 1.0 : (double) gestureInfo.ullArguments / (double) this.LastEvent.GestureArguments;
break;
case 4:
this.PanTranslation = this.IsBegin ? new Size(0, 0) : new Size(this.Location.X - this.LastEvent.Location.X, this.Location.Y - this.LastEvent.Location.Y);
int number = User32.HiDWord((long) gestureInfo.ullArguments);
this.PanVelocity = new Size((int) User32.LoWord(number), (int) User32.HiWord(number));
break;
case 5:
ushort num = this.IsBegin ? (ushort) 0 : (ushort) this.LastEvent.GestureArguments;
this.RotateAngle = User32.GID_ROTATE_ANGLE_FROM_ARGUMENT((ushort) (gestureInfo.ullArguments - (ulong) num));
break;
}
}
public uint GestureId { get; private set; }
public ulong GestureArguments { get; private set; }
public Point Location { get; private set; }
public bool IsBegin => ((int) this._dwFlags & 1) != 0;
public bool IsEnd => ((int) this._dwFlags & 4) != 0;
public bool IsInertia => ((int) this._dwFlags & 2) != 0;
public double RotateAngle { get; private set; }
public Point Center { get; private set; }
public double ZoomFactor { get; private set; }
public Size PanTranslation { get; private set; }
public Size PanVelocity { get; private set; }
public GestureEventArgs LastBeginEvent { get; internal set; }
public GestureEventArgs LastEvent { get; internal set; }
}
main event in GestureHandler class:
`
protected static uint WindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam)
{
if (msg != 281)
return 0;
GESTUREINFO gestureinfo = new GESTUREINFO()
{
cbSize = (uint) Marshal.SizeOf(typeof (GESTUREINFO))
};
GestureEventArgs e = User32.GetGestureInfo(lParam, ref gestureinfo) ? new GestureEventArgs(this, ref gestureinfo) : throw new Exception(“Cannot get gesture information”);
…
this.LastEvent = e;
if (e.IsBegin)
this.LastBeginEvent = e;
if (gestureinfo.dwID != 6U && gestureinfo.dwID != 7U)
return 0;
User32.CloseGestureInfoHandle(lParam);
return 1;}
`
initialization in GestureHandler class:
private bool SetGesturesDesired()
{
var gestureConfig = new GESTURECONFIG
{
new GESTURECONFIG() { dwID = 3U, dwWant = 1U, dwBlock = 0U },
new GESTURECONFIG() { dwID = 4U, dwWant = 1U, dwBlock = 0U },
new GESTURECONFIG() { dwID = 5U, dwWant = 1U, dwBlock = 0U },
new GESTURECONFIG() { dwID = 6U, dwWant = 1U, dwBlock = 0U }
};
var result = User32.SetGestureConfig(
this.ControlHandle,
0U,
(uint)gestureConfig.Length,
gestureConfig,
(uint)Marshal.SizeOf(typeof(GESTURECONFIG)));
return result;
}
private void Initialize()
{
var mainWindowHandle = Rhino.RhinoApp.MainWindowHandle();
if (!SetGesturesDesired())
throw new NotSupportedException(“Cannot register window”);
this._windowProcDelegate = new User32.WindowProcDelegate(this.WindowProcSubClass);
this._originalWindowProcId = IntPtr.Size == 4 ? User32.SubclassWindow(mainWindowHandle, -4, this._windowProcDelegate) : User32.SubclassWindow64(mainWindowHandle, -4, this._windowProcDelegate);
using (Graphics graphics = Graphics.FromHwnd(mainWindowHandle))
{
this.DpiX = graphics.DpiX;
this.DpiY = graphics.DpiY;
}
this.WindowMessage += (EventHandler) ((s, e) => {});
}
I have tried the same gestures on the same pc with Rhinoceros 5.14 compared to Rhinoceros 7.16 and the results are reported below:
Rhinoceros 7.16 one zoom gesture (down index and thumb, drag index and thumb closing their distance, up index and thumb)
03/06/2022 11:39:40.120 DEBUG: time=‘11:39:40’ mapWM2EventId=‘8’
03/06/2022 11:39:40.581 DEBUG: time=‘11:39:40’ mapWM2EventId=‘16’
Rhinoceros 7.16 one rotate gesture (down index and thumb, rotate index and thumb reciprocally, up index and thumb)
03/06/2022 11:40:10.810 DEBUG: time=‘11:40:10’ mapWM2EventId=‘8’
03/06/2022 11:40:11.567 DEBUG: time=‘11:40:11’ mapWM2EventId=‘16’
Rhinoceros 7.16 one pan gesture (down index and medium, drag index and medium equally, up index and medium)
03/06/2022 11:40:36.678 DEBUG: time=‘11:40:36’ mapWM2EventId=‘8’
03/06/2022 11:40:37.103 DEBUG: time=‘11:40:37’ mapWM2EventId=‘16’
Rhinoceros 5.14 one zoom gesture
03/06/2022 10:48:26.666 DEBUG: time=‘10:48:26’ mapWM2EventId=‘8’
03/06/2022 10:48:26.666 DEBUG: time=‘10:48:26’ mapWM2EventId=‘25’
03/06/2022 10:48:26.666 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.681 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.681 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.744 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.759 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.759 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.759 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.775 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.806 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.806 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.806 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.806 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.806 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.837 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.837 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.837 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.837 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.853 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.853 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.853 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.853 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.884 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.884 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.884 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.884 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.900 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.900 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.900 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.900 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.916 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.932 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.932 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.932 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.962 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.962 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.978 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.978 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.994 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.994 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.994 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:26.994 DEBUG: time=‘10:48:26’ mapWM2EventId=‘24’
03/06/2022 10:48:27.009 DEBUG: time=‘10:48:27’ mapWM2EventId=‘24’
03/06/2022 10:48:27.025 DEBUG: time=‘10:48:27’ mapWM2EventId=‘24’
03/06/2022 10:48:27.025 DEBUG: time=‘10:48:27’ mapWM2EventId=‘24’
03/06/2022 10:48:27.025 DEBUG: time=‘10:48:27’ mapWM2EventId=‘24’
03/06/2022 10:48:27.040 DEBUG: time=‘10:48:27’ mapWM2EventId=‘24’
03/06/2022 10:48:27.040 DEBUG: time=‘10:48:27’ mapWM2EventId=‘24’
03/06/2022 10:48:27.040 DEBUG: time=‘10:48:27’ mapWM2EventId=‘24’
03/06/2022 10:48:27.040 DEBUG: time=‘10:48:27’ mapWM2EventId=‘24’
03/06/2022 10:48:27.072 DEBUG: time=‘10:48:27’ mapWM2EventId=‘24’
03/06/2022 10:48:27.072 DEBUG: time=‘10:48:27’ mapWM2EventId=‘24’
03/06/2022 10:48:27.072 DEBUG: time=‘10:48:27’ mapWM2EventId=‘24’
03/06/2022 10:48:27.072 DEBUG: time=‘10:48:27’ mapWM2EventId=‘24’
03/06/2022 10:48:27.103 DEBUG: time=‘10:48:27’ mapWM2EventId=‘24’
03/06/2022 10:48:27.119 DEBUG: time=‘10:48:27’ mapWM2EventId=‘24’
03/06/2022 10:48:27.119 DEBUG: time=‘10:48:27’ mapWM2EventId=‘24’
03/06/2022 10:48:27.150 DEBUG: time=‘10:48:27’ mapWM2EventId=‘24’
03/06/2022 10:48:27.150 DEBUG: time=‘10:48:27’ mapWM2EventId=‘24’
03/06/2022 10:48:27.181 DEBUG: time=‘10:48:27’ mapWM2EventId=‘24’
03/06/2022 10:48:27.197 DEBUG: time=‘10:48:27’ mapWM2EventId=‘24’
03/06/2022 10:48:27.212 DEBUG: time=‘10:48:27’ mapWM2EventId=‘24’
03/06/2022 10:48:27.228 DEBUG: time=‘10:48:27’ mapWM2EventId=‘24’
03/06/2022 10:48:27.259 DEBUG: time=‘10:48:27’ mapWM2EventId=‘24’
03/06/2022 10:48:27.306 DEBUG: time=‘10:48:27’ mapWM2EventId=‘24’
03/06/2022 10:48:27.353 DEBUG: time=‘10:48:27’ mapWM2EventId=‘24’
03/06/2022 10:48:27.385 DEBUG: time=‘10:48:27’ mapWM2EventId=‘28’
03/06/2022 10:48:27.385 DEBUG: time=‘10:48:27’ mapWM2EventId=‘16’
Rhinoceros 5.14 one rotate gesture
03/06/2022 10:49:56.172 DEBUG: time=‘10:49:56’ mapWM2EventId=‘8’
03/06/2022 10:49:56.204 DEBUG: time=‘10:49:56’ mapWM2EventId=‘41’
03/06/2022 10:49:56.250 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.282 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.329 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.360 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.406 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.438 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.454 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.484 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.516 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.532 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.563 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.579 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.609 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.625 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.656 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.672 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.703 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.719 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.735 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.766 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.797 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.813 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.844 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.860 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.891 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.906 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.937 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.953 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:56.984 DEBUG: time=‘10:49:56’ mapWM2EventId=‘40’
03/06/2022 10:49:57.001 DEBUG: time=‘10:49:57’ mapWM2EventId=‘40’
03/06/2022 10:49:57.032 DEBUG: time=‘10:49:57’ mapWM2EventId=‘40’
03/06/2022 10:49:57.047 DEBUG: time=‘10:49:57’ mapWM2EventId=‘40’
03/06/2022 10:49:57.079 DEBUG: time=‘10:49:57’ mapWM2EventId=‘40’
03/06/2022 10:49:57.094 DEBUG: time=‘10:49:57’ mapWM2EventId=‘40’
03/06/2022 10:49:57.125 DEBUG: time=‘10:49:57’ mapWM2EventId=‘40’
03/06/2022 10:49:57.157 DEBUG: time=‘10:49:57’ mapWM2EventId=‘40’
03/06/2022 10:49:57.172 DEBUG: time=‘10:49:57’ mapWM2EventId=‘40’
03/06/2022 10:49:57.203 DEBUG: time=‘10:49:57’ mapWM2EventId=‘40’
03/06/2022 10:49:57.219 DEBUG: time=‘10:49:57’ mapWM2EventId=‘40’
03/06/2022 10:49:57.250 DEBUG: time=‘10:49:57’ mapWM2EventId=‘40’
03/06/2022 10:49:57.266 DEBUG: time=‘10:49:57’ mapWM2EventId=‘40’
03/06/2022 10:49:57.313 DEBUG: time=‘10:49:57’ mapWM2EventId=‘40’
03/06/2022 10:49:57.344 DEBUG: time=‘10:49:57’ mapWM2EventId=‘44’
03/06/2022 10:49:57.344 DEBUG: time=‘10:49:57’ mapWM2EventId=‘16’
Rhinoceros 5.14 one pan gesture
03/06/2022 11:11:41.307 DEBUG: time=‘11:11:41’ mapWM2EventId=‘8’
03/06/2022 11:11:41.314 DEBUG: time=‘11:11:41’ mapWM2EventId=‘33’
03/06/2022 11:11:41.324 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.337 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.366 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.382 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.413 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.432 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.437 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.443 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.460 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.466 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.485 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.491 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.515 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.520 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.539 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.545 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.557 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.563 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.576 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.581 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.607 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.622 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.627 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.644 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.656 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.667 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.674 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.692 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.698 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.716 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.765 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.796 DEBUG: time=‘11:11:41’ mapWM2EventId=‘32’
03/06/2022 11:11:41.815 DEBUG: time=‘11:11:41’ mapWM2EventId=‘36’
03/06/2022 11:11:41.820 DEBUG: time=‘11:11:41’ mapWM2EventId=‘16’