Bläddra i källkod

Input events marked as used (i.e. by GUI) can now be detected by C# code
Modified GUICurveEditor so it properly handles context menu clicking, without triggering normal input events

BearishSun 9 år sedan
förälder
incheckning
d81c51949e

+ 3 - 3
Source/BansheeEngine/Source/BsGUIManager.cpp

@@ -79,6 +79,9 @@ namespace BansheeEngine
 		, mCaretColor(1.0f, 0.6588f, 0.0f), mCaretBlinkInterval(0.5f), mCaretLastBlinkTime(0.0f), mIsCaretOn(false)
 		, mActiveCursor(CursorType::Arrow), mTextSelectionColor(0.0f, 114/255.0f, 188/255.0f)
 	{
+		// Note: Hidden dependency. GUI must receive input events before other systems, in order so it can mark them as used
+		// if required. e.g. clicking on a context menu should mark the event as used so that other non-GUI systems know
+		// that they probably should not process such event themselves.
 		mOnPointerMovedConn = gInput().onPointerMoved.connect(std::bind(&GUIManager::onPointerMoved, this, _1));
 		mOnPointerPressedConn = gInput().onPointerPressed.connect(std::bind(&GUIManager::onPointerPressed, this, _1));
 		mOnPointerReleasedConn = gInput().onPointerReleased.connect(std::bind(&GUIManager::onPointerReleased, this, _1));
@@ -1089,9 +1092,6 @@ namespace BansheeEngine
 			}
 		}
 
-		if(mElementsUnderPointer.size() > 0)
-			event.markAsUsed();
-
 		mElementsInFocus.swap(mNewElementsInFocus);
 
 		// If right click try to open context menu

+ 21 - 14
Source/MBansheeEditor/Input/EditorInput.cs

@@ -59,9 +59,10 @@ namespace BansheeEditor
         /// </summary>
         /// <param name="code">Code of the pressed button.</param>
         /// <param name="deviceIdx">Device the event originated from.</param>
-        private static void Internal_TriggerButtonDown(ButtonCode code, int deviceIdx)
+        /// <param name="isUsed">Set to true if the event was handled previously by some internal system (like GUI).</param>
+        private static void Internal_TriggerButtonDown(ButtonCode code, int deviceIdx, bool isUsed)
         {
-            ButtonEvent ev = new ButtonEvent(code, deviceIdx);
+            ButtonEvent ev = new ButtonEvent(code, deviceIdx, isUsed);
 
             if (OnButtonDown != null)
                 OnButtonDown(ev);
@@ -72,9 +73,10 @@ namespace BansheeEditor
         /// </summary>
         /// <param name="code">Code of the released button.</param>
         /// <param name="deviceIdx">Device the event originated from.</param>
-        private static void Internal_TriggerButtonUp(ButtonCode code, int deviceIdx)
+        /// <param name="isUsed">Set to true if the event was handled previously by some internal system (like GUI).</param>
+        private static void Internal_TriggerButtonUp(ButtonCode code, int deviceIdx, bool isUsed)
         {
-            ButtonEvent ev = new ButtonEvent(code, deviceIdx);
+            ButtonEvent ev = new ButtonEvent(code, deviceIdx, isUsed);
 
             if (OnButtonUp != null)
                 OnButtonUp(ev);
@@ -84,9 +86,10 @@ namespace BansheeEditor
         /// Triggered by runtime when character is input.
         /// </summary>
         /// <param name="textChar">Code of input character.</param>
-        private static void Internal_TriggerCharInput(int textChar)
+        /// <param name="isUsed">Set to true if the event was handled previously by some internal system (like GUI).</param>
+        private static void Internal_TriggerCharInput(int textChar, bool isUsed)
         {
-            TextInputEvent ev = new TextInputEvent(textChar);
+            TextInputEvent ev = new TextInputEvent(textChar, isUsed);
 
             if (OnCharInput != null)
                 OnCharInput(ev);
@@ -104,10 +107,11 @@ namespace BansheeEditor
         /// <param name="alt">Is alt button on the keyboard being held down.</param>
         /// <param name="scrollAmount">If mouse wheel is being scrolled, what is the amount. Only relevant for 
         ///                            move events.</param>
+        /// <param name="isUsed">Set to true if the event was handled previously by some internal system (like GUI).</param>
         private static void Internal_TriggerPointerMove(Vector2I screenPos, Vector2I delta, PointerButton button, bool shift,
-            bool ctrl, bool alt, float scrollAmount)
+            bool ctrl, bool alt, float scrollAmount, bool isUsed)
         {
-            PointerEvent ev = new PointerEvent(screenPos, delta, button, shift, ctrl, alt, scrollAmount);
+            PointerEvent ev = new PointerEvent(screenPos, delta, button, shift, ctrl, alt, scrollAmount, isUsed);
 
             if (OnPointerMoved != null)
                 OnPointerMoved(ev);
@@ -125,10 +129,11 @@ namespace BansheeEditor
         /// <param name="alt">Is alt button on the keyboard being held down.</param>
         /// <param name="scrollAmount">If mouse wheel is being scrolled, what is the amount. Only relevant for 
         ///                            move events.</param>
+        /// <param name="isUsed">Set to true if the event was handled previously by some internal system (like GUI).</param>
         private static void Internal_TriggerPointerPressed(Vector2I screenPos, Vector2I delta, PointerButton button, bool shift,
-            bool ctrl, bool alt, float scrollAmount)
+            bool ctrl, bool alt, float scrollAmount, bool isUsed)
         {
-            PointerEvent ev = new PointerEvent(screenPos, delta, button, shift, ctrl, alt, scrollAmount);
+            PointerEvent ev = new PointerEvent(screenPos, delta, button, shift, ctrl, alt, scrollAmount, isUsed);
 
             if (OnPointerPressed != null)
                 OnPointerPressed(ev);
@@ -146,10 +151,11 @@ namespace BansheeEditor
         /// <param name="alt">Is alt button on the keyboard being held down.</param>
         /// <param name="scrollAmount">If mouse wheel is being scrolled, what is the amount. Only relevant for 
         ///                            move events.</param>
+        /// <param name="isUsed">Set to true if the event was handled previously by some internal system (like GUI).</param>
         private static void Internal_TriggerPointerReleased(Vector2I screenPos, Vector2I delta, PointerButton button, bool shift,
-            bool ctrl, bool alt, float scrollAmount)
+            bool ctrl, bool alt, float scrollAmount, bool isUsed)
         {
-            PointerEvent ev = new PointerEvent(screenPos, delta, button, shift, ctrl, alt, scrollAmount);
+            PointerEvent ev = new PointerEvent(screenPos, delta, button, shift, ctrl, alt, scrollAmount, isUsed);
 
             if (OnPointerReleased != null)
                 OnPointerReleased(ev);
@@ -167,10 +173,11 @@ namespace BansheeEditor
         /// <param name="alt">Is alt button on the keyboard being held down.</param>
         /// <param name="scrollAmount">If mouse wheel is being scrolled, what is the amount. Only relevant for 
         ///                            move events.</param>
+        /// <param name="isUsed">Set to true if the event was handled previously by some internal system (like GUI).</param>
         private static void Internal_TriggerPointerDoubleClick(Vector2I screenPos, Vector2I delta, PointerButton button, bool shift,
-            bool ctrl, bool alt, float scrollAmount)
+            bool ctrl, bool alt, float scrollAmount, bool isUsed)
         {
-            PointerEvent ev = new PointerEvent(screenPos, delta, button, shift, ctrl, alt, scrollAmount);
+            PointerEvent ev = new PointerEvent(screenPos, delta, button, shift, ctrl, alt, scrollAmount, isUsed);
 
             if (OnPointerDoubleClick != null)
                 OnPointerDoubleClick(ev);

+ 20 - 13
Source/MBansheeEditor/Utility/EdAnimationCurve.cs

@@ -5,18 +5,25 @@ using BansheeEngine;
 
 namespace BansheeEditor
 {
+    internal enum TangentTypes
+    {
+        In = 1 << 0,
+        Out = 1 << 1
+    }
+
+    [Flags]
     internal enum TangentMode
     {
         Auto        = 0,
-        Free        = 1 << 0,
-        InAuto      = 1 << 1,
-        OutAuto     = 1 << 2,
-        InFree      = 1 << 3,
-        OutFree     = 1 << 4,
-        InLinear    = 1 << 5,
-        OutLinear   = 1 << 6,
-        InStep      = 1 << 7,
-        OutStep     = 1 << 8
+        InAuto      = TangentTypes.In | 1 << 2,
+        InFree      = TangentTypes.In | 1 << 3,
+        InLinear    = TangentTypes.In | 1 << 4,
+        InStep      = TangentTypes.In | 1 << 5,
+        OutAuto     = TangentTypes.Out | 1 << 6,
+        OutFree     = TangentTypes.Out | 1 << 7,
+        OutLinear   = TangentTypes.Out | 1 << 8,
+        OutStep     = TangentTypes.Out | 1 << 9,
+        Free        = 1 << 10,
     }
 
     internal class EdAnimationCurve
@@ -164,12 +171,12 @@ namespace BansheeEditor
                 keyThis.inTangent = 0.0f;
 
                 TangentMode tangentMode = tangentModes[0];
-                if (tangentMode == TangentMode.Auto || tangentMode == TangentMode.OutAuto || tangentMode == TangentMode.OutLinear)
+                if (tangentMode == TangentMode.Auto || tangentMode.HasFlag(TangentMode.OutAuto) || tangentMode.HasFlag(TangentMode.OutLinear))
                 {
                     float diff = keyNext.time - keyThis.time;
                     keyThis.outTangent = (keyNext.value - keyThis.value) / diff;
                 }
-                else if (tangentMode == TangentMode.OutStep)
+                else if (tangentMode.HasFlag(TangentMode.OutStep))
                 {
                     keyThis.outTangent = float.PositiveInfinity;
                 }
@@ -243,12 +250,12 @@ namespace BansheeEditor
                 keyThis.outTangent = 0.0f;
 
                 TangentMode tangentMode = tangentModes[tangentModes.Length - 1];
-                if (tangentMode == TangentMode.Auto || tangentMode == TangentMode.InAuto || tangentMode == TangentMode.InLinear)
+                if (tangentMode == TangentMode.Auto || tangentMode.HasFlag(TangentMode.InAuto) || tangentMode.HasFlag(TangentMode.InLinear))
                 {
                     float diff = keyThis.time - keyPrev.time;
                     keyThis.inTangent = (keyThis.value - keyPrev.value) / diff;
                 }
-                else if (tangentMode == TangentMode.InStep)
+                else if (tangentMode.HasFlag(TangentMode.InStep))
                 {
                     keyThis.inTangent = float.PositiveInfinity;
                 }

+ 1 - 1
Source/MBansheeEditor/Window/EditorWindow.cs

@@ -66,7 +66,7 @@ namespace BansheeEditor
         /// </summary>
         public bool Active { get { return Internal_IsActive(mCachedPtr); } }
 
-        protected GUIPanel GUI;
+        public GUIPanel GUI;
 
         /// <summary>
         /// Opens an editor window. If window is already open it returns the existing instance.

+ 165 - 77
Source/MBansheeEditor/Windows/Animation/GUICurveEditor.cs

@@ -27,6 +27,7 @@ namespace BansheeEditor
         private const int TIMELINE_HEIGHT = 20;
         private const int SIDEBAR_WIDTH = 30;
 
+        private EditorWindow window;
         private GUILayout gui;
         private GUIPanel drawingPanel;
         private GUIGraphTime guiTimeline;
@@ -42,12 +43,14 @@ namespace BansheeEditor
         private int markedFrameIdx;
         private List<KeyframeRef> selectedKeyframes = new List<KeyframeRef>();
 
+        private bool isPointerHeld;
         private bool isMousePressedOverKey;
         private KeyFrame[] draggedKeyframes;
         private Vector2 dragStart;
 
-        public GUICurveEditor(GUILayout gui, int width, int height)
+        public GUICurveEditor(EditorWindow window, GUILayout gui, int width, int height)
         {
+            this.window = window;
             this.gui = gui;
 
             blankContextMenu = new ContextMenu();
@@ -80,6 +83,128 @@ namespace BansheeEditor
 
             guiSidebar = new GUIGraphValues(sidebarPanel, SIDEBAR_WIDTH, height - TIMELINE_HEIGHT);
             guiSidebar.SetRange(-10.0f, 10.0f);
+
+            EditorInput.OnPointerPressed += OnPointerPressed;
+            EditorInput.OnPointerMoved += OnPointerMoved;
+            EditorInput.OnPointerReleased += OnPointerReleased;
+            EditorInput.OnButtonUp += OnButtonUp;
+        }
+
+        private void OnPointerPressed(PointerEvent ev)
+        {
+            if (ev.IsUsed)
+                return;
+
+            Vector2I windowPos = window.ScreenToWindowPos(ev.ScreenPos);
+
+            Rect2I elementBounds = GUIUtility.CalculateBounds(gui, window.GUI);
+            Vector2I pointerPos = windowPos - new Vector2I(elementBounds.x, elementBounds.y);
+
+            Rect2I drawingBounds = drawingPanel.Bounds;
+            Vector2I drawingPos = pointerPos - new Vector2I(drawingBounds.x, drawingBounds.y);
+
+            if (ev.Button == PointerButton.Left)
+            {
+                Vector2 curveCoord;
+                int curveIdx;
+                int keyIdx;
+                if (guiCurveDrawing.GetCoordInfo(drawingPos, out curveCoord, out curveIdx, out keyIdx))
+                {
+                    if (keyIdx == -1)
+                        ClearSelection();
+                    else
+                    {
+                        if (!Input.IsButtonHeld(ButtonCode.LeftShift) && !Input.IsButtonHeld(ButtonCode.RightShift))
+                            ClearSelection();
+
+                        SelectKeyframe(curveIdx, keyIdx);
+
+                        isMousePressedOverKey = true;
+                        dragStart = curveCoord;
+                    }
+
+                    guiCurveDrawing.Rebuild();
+                }
+                else
+                {
+                    int frameIdx = guiTimeline.GetFrame(pointerPos);
+
+                    if (frameIdx != -1)
+                        SetMarkedFrame(frameIdx);
+                }
+
+                isPointerHeld = true;
+            }
+            else if (ev.Button == PointerButton.Right)
+            {
+                Vector2 curveCoord;
+                int curveIdx;
+                int keyIdx;
+                if (guiCurveDrawing.GetCoordInfo(drawingPos, out curveCoord, out curveIdx, out keyIdx))
+                {
+                    contextClickPosition = pointerPos;
+
+                    if (keyIdx == -1)
+                    {
+                        ClearSelection();
+
+                        blankContextMenu.Open(contextClickPosition, gui);
+                    }
+                    else
+                    {
+                        // If clicked outside of current selection, just select the one keyframe
+                        if (!IsSelected(curveIdx, keyIdx))
+                        {
+                            ClearSelection();
+                            SelectKeyframe(curveIdx, keyIdx);
+
+                            guiCurveDrawing.Rebuild();
+                        }
+
+                        keyframeContextMenu.Open(contextClickPosition, gui);
+                    }
+                }
+            }
+        }
+
+        private void OnPointerMoved(PointerEvent ev)
+        {
+            if (ev.Button != PointerButton.Left)
+                return;
+
+            if (isPointerHeld)
+            {
+                if (isMousePressedOverKey)
+                {
+                    // TODO - Check if pointer moves some minimal amount
+                    // - If so, start drag. Record all current positions
+                    // - Calculate offset in curve space and apply to all keyframes
+                }
+                else
+                {
+                    Vector2I windowPos = window.ScreenToWindowPos(ev.ScreenPos);
+
+                    Rect2I elementBounds = GUIUtility.CalculateBounds(gui, window.GUI);
+                    Vector2I pointerPos = windowPos - new Vector2I(elementBounds.x, elementBounds.y);
+
+                    int frameIdx = guiTimeline.GetFrame(pointerPos);
+
+                    if (frameIdx != -1)
+                        SetMarkedFrame(frameIdx);
+                }
+            }
+        }
+
+        private void OnPointerReleased(PointerEvent ev)
+        {
+            isPointerHeld = false;
+            isMousePressedOverKey = false;
+        }
+
+        private void OnButtonUp(ButtonEvent ev)
+        {
+            if(ev.Button == ButtonCode.Delete)
+                DeleteSelectedKeyframes();
         }
 
         /// <summary>
@@ -174,101 +299,64 @@ namespace BansheeEditor
             guiTimeline.Rebuild();
             guiSidebar.Rebuild();
         }
-
-        public void HandleInput(Vector2I pointerPos)
+        
+        private void ChangeSelectionTangentMode(TangentMode mode)
         {
-            Rect2I drawingBounds = drawingPanel.Bounds;
-            Vector2I drawingPos = pointerPos - new Vector2I(drawingBounds.x, drawingBounds.y);
-
-            if (Input.IsPointerButtonDown(PointerButton.Left))
+            foreach (var keyframe in selectedKeyframes)
             {
-                Vector2 curveCoord;
-                int curveIdx;
-                int keyIdx;
-                if (guiCurveDrawing.GetCoordInfo(drawingPos, out curveCoord, out curveIdx, out keyIdx))
-                {
-                    if (keyIdx == -1)
-                        ClearSelection();
-                    else
-                    {
-                        if (!Input.IsButtonHeld(ButtonCode.LeftShift) && !Input.IsButtonHeld(ButtonCode.RightShift))
-                            ClearSelection();
-
-                        SelectKeyframe(curveIdx, keyIdx);
-
-                        isMousePressedOverKey = true;
-                        dragStart = curveCoord;
-                    }
-
-                    guiCurveDrawing.Rebuild();
-                }
-            }
+                EdAnimationCurve curve = curves[keyframe.curveIdx];
 
-            if (Input.IsPointerButtonHeld(PointerButton.Left))
-            {
-                if (isMousePressedOverKey)
-                {
-                    // TODO - Check if pointer moves some minimal amount
-                    // - If so, start drag. Record all current positions
-                    // - Calculate offset in curve space and apply to all keyframes
-                }
+                if (mode == TangentMode.Auto || mode == TangentMode.Free)
+                    curve.SetTangentMode(keyframe.keyIdx, mode);
                 else
                 {
-                    int frameIdx = guiTimeline.GetFrame(pointerPos);
-
-                    if (frameIdx != -1)
-                        SetMarkedFrame(frameIdx);
-                }
-            }
-
-            if (Input.IsPointerButtonUp(PointerButton.Left))
-            {
-                isMousePressedOverKey = false;
-            }
-
-            if (Input.IsPointerButtonDown(PointerButton.Right))
-            {
-                Vector2 curveCoord;
-                int curveIdx;
-                int keyIdx;
-                if (guiCurveDrawing.GetCoordInfo(drawingPos, out curveCoord, out curveIdx, out keyIdx))
-                {
-                    contextClickPosition = pointerPos;
+                    TangentMode newMode = curve.TangentModes[keyframe.keyIdx];
 
-                    if (keyIdx == -1)
+                    if (mode.HasFlag(TangentTypes.In))
                     {
-                        ClearSelection();
+                        // Replace only the in tangent mode, keeping the out tangent as is
+                        TangentMode inFlags = (TangentMode.InAuto | TangentMode.InFree | TangentMode.InLinear |
+                                               TangentMode.InAuto);
 
-                        blankContextMenu.Open(contextClickPosition, gui);
+                        newMode &= ~inFlags;
+                        newMode |= (mode & inFlags);
                     }
                     else
                     {
-                        // If clicked outside of current selection, just select the one keyframe
-                        if (!IsSelected(curveIdx, keyIdx))
-                        {
-                            ClearSelection();
-                            SelectKeyframe(curveIdx, keyIdx);
-
-                            guiCurveDrawing.Rebuild();
-                        }
+                        // Replace only the out tangent mode, keeping the in tangent as is
+                        TangentMode outFlags = (TangentMode.OutAuto | TangentMode.OutFree | TangentMode.OutLinear |
+                                               TangentMode.OutAuto);
 
-                        keyframeContextMenu.Open(contextClickPosition, gui);
+                        newMode &= ~outFlags;
+                        newMode |= (mode & outFlags);
                     }
+
+                    curve.SetTangentMode(keyframe.keyIdx, newMode);
                 }
             }
-
-            if (Input.IsButtonUp(ButtonCode.Delete))
-                DeleteSelectedKeyframes();
-        }
-
-        private void ChangeSelectionTangentMode(TangentMode mode)
-        {
-            // TODO - When changing just left or right, make sure to persist opposite tangent mode (or switch to equivalent broken mode if it wasn't broken)
         }
 
         private void AddKeyframeAtPosition()
         {
-            // TODO
+            Vector2 curveCoord;
+            int curveIdx;
+            int keyIdx;
+            if (guiCurveDrawing.GetCoordInfo(contextClickPosition, out curveCoord, out curveIdx, out keyIdx))
+            {
+                ClearSelection();
+
+                foreach (var curve in curves)
+                {
+                    float t = curveCoord.x;
+                    float value = curveCoord.y;
+
+                    curve.AddKeyframe(t, value);
+                }
+
+                // TODO - UNDOREDO
+
+                guiCurveDrawing.Rebuild();
+            }
         }
 
         private void AddEventAtPosition()

+ 2 - 6
Source/MBansheeEditor/Windows/AnimationWindow.cs

@@ -85,7 +85,7 @@ namespace BansheeEditor
             editorPanel = mainLayout.AddPanel();
             buttonLayoutHeight = lengthField.Bounds.height;
 
-            guiCurveEditor = new GUICurveEditor(editorPanel, Width, Height - buttonLayoutHeight);
+            guiCurveEditor = new GUICurveEditor(this, editorPanel, Width, Height - buttonLayoutHeight);
             guiCurveEditor.SetCurves(CreateDummyCurves());
             guiCurveEditor.Redraw();
         }
@@ -111,11 +111,7 @@ namespace BansheeEditor
 
         private void OnEditorUpdate()
         {
-            Vector2I windowPos = ScreenToWindowPos(Input.PointerPosition);
-            Rect2I curveEditorBounds = editorPanel.Bounds;
-
-            Vector2I offset = new Vector2I(curveEditorBounds.x, curveEditorBounds.y);
-            guiCurveEditor.HandleInput(windowPos - offset);
+            
         }
     }
 

+ 70 - 39
Source/MBansheeEngine/Input/Input.cs

@@ -15,16 +15,19 @@ namespace BansheeEngine
 	{
 		internal ButtonCode buttonCode;
         internal int deviceIdx;
+        internal bool isUsed;
 
         /// <summary>
         /// Creates a new button input event. For runtime use only.
         /// </summary>
         /// <param name="buttonCode">Button code this event is referring to.</param>
         /// <param name="deviceIdx">Index of the device that the event originated from.</param>
-	    internal ButtonEvent(ButtonCode buttonCode, int deviceIdx)
+        /// <param name="isUsed">Set to true if the event was handled previously by some internal system (like GUI).</param>
+	    internal ButtonEvent(ButtonCode buttonCode, int deviceIdx, bool isUsed)
 	    {
 	        this.buttonCode = buttonCode;
 	        this.deviceIdx = deviceIdx;
+            this.isUsed = isUsed;
 	    }
 
         /// <summary>
@@ -51,7 +54,12 @@ namespace BansheeEngine
         /// Query is the pressed button a gamepad button.
         /// </summary>
         public bool IsGamepad { get { return ((int)buttonCode & 0x40000000) != 0; } }
-	};
+
+        /// <summary>
+        /// Returns true if the event was handled previously by some internal system (like GUI).
+        /// </summary>
+        public bool IsUsed { get { return isUsed; } }
+    };
 
     /// <summary>
     /// Pointer buttons. Generally these correspond to mouse buttons, but may be used in some form for touch input as well.
@@ -67,15 +75,16 @@ namespace BansheeEngine
     /// </summary>
     public struct PointerEvent
     {
-        internal Vector2I _screenPos;
-        internal Vector2I _delta;
-        internal PointerButton _button;
+        internal Vector2I screenPos;
+        internal Vector2I delta;
+        internal PointerButton button;
 
-        internal bool _shift;
-        internal bool _control;
-        internal bool _alt;
+        internal bool shift;
+        internal bool control;
+        internal bool alt;
 
-        internal float _mouseWheelScrollAmount;
+        internal float mouseWheelScrollAmount;
+        internal bool isUsed;
 
         /// <summary>
         /// Creates a new pointer event. For runtime use only.
@@ -89,55 +98,62 @@ namespace BansheeEngine
         /// <param name="alt">Is alt button on the keyboard being held down.</param>
         /// <param name="mouseWheelScrollAmount">If mouse wheel is being scrolled, what is the amount. Only relevant for 
         ///                                      move events.</param>
+        /// <param name="isUsed">Set to true if the event was handled previously by some internal system (like GUI).</param>
         internal PointerEvent(Vector2I screenPos, Vector2I delta, PointerButton button,
-            bool shift, bool control, bool alt, float mouseWheelScrollAmount)
+            bool shift, bool control, bool alt, float mouseWheelScrollAmount, bool isUsed)
         {
-            _screenPos = screenPos;
-            _delta = delta;
-            _button = button;
+            this.screenPos = screenPos;
+            this.delta = delta;
+            this.button = button;
 
-            _shift = shift;
-            _control = control;
-            _alt = alt;
+            this.shift = shift;
+            this.control = control;
+            this.alt = alt;
 
-            _mouseWheelScrollAmount = mouseWheelScrollAmount;
+            this.mouseWheelScrollAmount = mouseWheelScrollAmount;
+            this.isUsed = isUsed;
         }
 
         /// <summary>
         /// Screen position where the input event occurred.
         /// </summary>
-        public Vector2I ScreenPos { get { return _screenPos; } }
+        public Vector2I ScreenPos { get { return screenPos; } }
 
         /// <summary>
         /// Change in movement since last sent event.
         /// </summary>
-        public Vector2I Delta { get { return _delta; } }
+        public Vector2I Delta { get { return delta; } }
 
         /// <summary>
         /// Button that triggered the pointer event. Might be irrelevant depending on event type. 
         /// (for example move events don't correspond to a button.
         /// </summary>
-        public PointerButton Button { get { return _button; } }
+        public PointerButton Button { get { return button; } }
 
         /// <summary>
         /// Is shift button on the keyboard being held down.
         /// </summary>
-        public bool Shift { get { return _shift; } }
+        public bool Shift { get { return shift; } }
 
         /// <summary>
         /// Is control button on the keyboard being held down.
         /// </summary>
-        public bool Control { get { return _control; } }
+        public bool Control { get { return control; } }
 
         /// <summary>
         /// Is alt button on the keyboard being held down.
         /// </summary>
-        public bool Alt { get { return _alt; } }
+        public bool Alt { get { return alt; } }
 
         /// <summary>
         /// If mouse wheel is being scrolled, what is the amount. Only relevant for move events.
         /// </summary>
-        public float ScrollAmount { get { return _mouseWheelScrollAmount; } }
+        public float ScrollAmount { get { return mouseWheelScrollAmount; } }
+
+        /// <summary>
+        /// Returns true if the event was handled previously by some internal system (like GUI).
+        /// </summary>
+        public bool IsUsed { get { return isUsed; } }
     }
 
     /// <summary>
@@ -147,20 +163,28 @@ namespace BansheeEngine
     public struct TextInputEvent
     {
         internal int textChar;
+        internal bool isUsed;
 
         /// <summary>
         /// Creates a new text input event. For runtime use only.
         /// </summary>
         /// <param name="textChar">Character the that was input.</param>
-        internal TextInputEvent(int textChar)
+        /// <param name="isUsed">Set to true if the event was handled previously by some internal system (like GUI).</param>
+        internal TextInputEvent(int textChar, bool isUsed)
         {
             this.textChar = textChar;
+            this.isUsed = isUsed;
         }
 
         /// <summary>
         /// Character the that was input.
         /// </summary>
         public int Char { get { return textChar; } }
+
+        /// <summary>
+        /// Returns true if the event was handled previously by some internal system (like GUI).
+        /// </summary>
+        public bool IsUsed { get { return isUsed; } }
     }
 
     /// <summary>
@@ -322,9 +346,10 @@ namespace BansheeEngine
         /// </summary>
         /// <param name="code">Code of the pressed button.</param>
         /// <param name="deviceIdx">Device the event originated from.</param>
-        private static void Internal_TriggerButtonDown(ButtonCode code, int deviceIdx)
+        /// <param name="isUsed">Set to true if the event was handled previously by some internal system (like GUI).</param>
+        private static void Internal_TriggerButtonDown(ButtonCode code, int deviceIdx, bool isUsed)
         {
-            ButtonEvent ev = new ButtonEvent(code, deviceIdx);
+            ButtonEvent ev = new ButtonEvent(code, deviceIdx, isUsed);
 
             if (OnButtonDown != null)
                 OnButtonDown(ev);
@@ -335,9 +360,10 @@ namespace BansheeEngine
         /// </summary>
         /// <param name="code">Code of the released button.</param>
         /// <param name="deviceIdx">Device the event originated from.</param>
-        private static void Internal_TriggerButtonUp(ButtonCode code, int deviceIdx)
+        /// <param name="isUsed">Set to true if the event was handled previously by some internal system (like GUI).</param>
+        private static void Internal_TriggerButtonUp(ButtonCode code, int deviceIdx, bool isUsed)
         {
-            ButtonEvent ev = new ButtonEvent(code, deviceIdx);
+            ButtonEvent ev = new ButtonEvent(code, deviceIdx, isUsed);
 
             if (OnButtonUp != null)
                 OnButtonUp(ev);
@@ -347,9 +373,10 @@ namespace BansheeEngine
         /// Triggered by runtime when character is input.
         /// </summary>
         /// <param name="textChar">Code of input character.</param>
-        private static void Internal_TriggerCharInput(int textChar)
+        /// <param name="isUsed">Set to true if the event was handled previously by some internal system (like GUI).</param>
+        private static void Internal_TriggerCharInput(int textChar, bool isUsed)
         {
-            TextInputEvent ev = new TextInputEvent(textChar);
+            TextInputEvent ev = new TextInputEvent(textChar, isUsed);
 
             if (OnCharInput != null)
                 OnCharInput(ev);
@@ -367,10 +394,11 @@ namespace BansheeEngine
         /// <param name="alt">Is alt button on the keyboard being held down.</param>
         /// <param name="scrollAmount">If mouse wheel is being scrolled, what is the amount. Only relevant for 
         ///                            move events.</param>
+        /// <param name="isUsed">Set to true if the event was handled previously by some internal system (like GUI).</param>
         private static void Internal_TriggerPointerMove(Vector2I screenPos, Vector2I delta, PointerButton button, bool shift, 
-            bool ctrl, bool alt, float scrollAmount)
+            bool ctrl, bool alt, float scrollAmount, bool isUsed)
         {
-            PointerEvent ev = new PointerEvent(screenPos, delta, button, shift, ctrl, alt, scrollAmount);
+            PointerEvent ev = new PointerEvent(screenPos, delta, button, shift, ctrl, alt, scrollAmount, isUsed);
 
             if (OnPointerMoved != null)
                 OnPointerMoved(ev);
@@ -388,10 +416,11 @@ namespace BansheeEngine
         /// <param name="alt">Is alt button on the keyboard being held down.</param>
         /// <param name="scrollAmount">If mouse wheel is being scrolled, what is the amount. Only relevant for 
         ///                            move events.</param>
+        /// <param name="isUsed">Set to true if the event was handled previously by some internal system (like GUI).</param>
         private static void Internal_TriggerPointerPressed(Vector2I screenPos, Vector2I delta, PointerButton button, bool shift,
-            bool ctrl, bool alt, float scrollAmount)
+            bool ctrl, bool alt, float scrollAmount, bool isUsed)
         {
-            PointerEvent ev = new PointerEvent(screenPos, delta, button, shift, ctrl, alt, scrollAmount);
+            PointerEvent ev = new PointerEvent(screenPos, delta, button, shift, ctrl, alt, scrollAmount, isUsed);
 
             if (OnPointerPressed != null)
                 OnPointerPressed(ev);
@@ -409,10 +438,11 @@ namespace BansheeEngine
         /// <param name="alt">Is alt button on the keyboard being held down.</param>
         /// <param name="scrollAmount">If mouse wheel is being scrolled, what is the amount. Only relevant for 
         ///                            move events.</param>
+        /// <param name="isUsed">Set to true if the event was handled previously by some internal system (like GUI).</param>
         private static void Internal_TriggerPointerReleased(Vector2I screenPos, Vector2I delta, PointerButton button, bool shift,
-            bool ctrl, bool alt, float scrollAmount)
+            bool ctrl, bool alt, float scrollAmount, bool isUsed)
         {
-            PointerEvent ev = new PointerEvent(screenPos, delta, button, shift, ctrl, alt, scrollAmount);
+            PointerEvent ev = new PointerEvent(screenPos, delta, button, shift, ctrl, alt, scrollAmount, isUsed);
 
             if (OnPointerReleased != null)
                 OnPointerReleased(ev);
@@ -430,10 +460,11 @@ namespace BansheeEngine
         /// <param name="alt">Is alt button on the keyboard being held down.</param>
         /// <param name="scrollAmount">If mouse wheel is being scrolled, what is the amount. Only relevant for 
         ///                            move events.</param>
+        /// <param name="isUsed">Set to true if the event was handled previously by some internal system (like GUI).</param>
         private static void Internal_TriggerPointerDoubleClick(Vector2I screenPos, Vector2I delta, PointerButton button, bool shift,
-            bool ctrl, bool alt, float scrollAmount)
+            bool ctrl, bool alt, float scrollAmount, bool isUsed)
         {
-            PointerEvent ev = new PointerEvent(screenPos, delta, button, shift, ctrl, alt, scrollAmount);
+            PointerEvent ev = new PointerEvent(screenPos, delta, button, shift, ctrl, alt, scrollAmount, isUsed);
 
             if (OnPointerDoubleClick != null)
                 OnPointerDoubleClick(ev);

+ 3 - 3
Source/SBansheeEditor/Include/BsScriptEditorInput.h

@@ -59,10 +59,10 @@ namespace BansheeEngine
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
 
-		typedef void(__stdcall *OnButtonEventThunkDef) (ButtonCode, UINT32, MonoException**);
-		typedef void(__stdcall *OnCharInputEventThunkDef) (UINT32, MonoException**);
+		typedef void(__stdcall *OnButtonEventThunkDef) (ButtonCode, UINT32, bool, MonoException**);
+		typedef void(__stdcall *OnCharInputEventThunkDef) (UINT32, bool, MonoException**);
 		typedef void(__stdcall *OnPointerEventThunkDef) (MonoObject*, MonoObject*, PointerEventButton,
-			bool, bool, bool, float, MonoException**);
+			bool, bool, bool, float, bool, MonoException**);
 
 		static OnButtonEventThunkDef OnButtonPressedThunk;
 		static OnButtonEventThunkDef OnButtonReleasedThunk;

+ 14 - 14
Source/SBansheeEditor/Source/BsScriptEditorInput.cpp

@@ -33,13 +33,13 @@ namespace BansheeEngine
 
 	void ScriptEditorInput::initRuntimeData()
 	{
-		OnButtonPressedThunk = (OnButtonEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerButtonDown", "ButtonCode,int")->getThunk();
-		OnButtonReleasedThunk = (OnButtonEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerButtonUp", "ButtonCode,int")->getThunk();
-		OnCharInputThunk = (OnCharInputEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerCharInput", "int")->getThunk();
-		OnPointerPressedThunk = (OnPointerEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerPointerPressed", "Vector2I,Vector2I,PointerButton,bool,bool,bool,single")->getThunk();
-		OnPointerReleasedThunk = (OnPointerEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerPointerReleased", "Vector2I,Vector2I,PointerButton,bool,bool,bool,single")->getThunk();
-		OnPointerMovedThunk = (OnPointerEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerPointerMove", "Vector2I,Vector2I,PointerButton,bool,bool,bool,single")->getThunk();
-		OnPointerDoubleClickThunk = (OnPointerEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerPointerDoubleClick", "Vector2I,Vector2I,PointerButton,bool,bool,bool,single")->getThunk();
+		OnButtonPressedThunk = (OnButtonEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerButtonDown", "ButtonCode,int,bool")->getThunk();
+		OnButtonReleasedThunk = (OnButtonEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerButtonUp", "ButtonCode,int,bool")->getThunk();
+		OnCharInputThunk = (OnCharInputEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerCharInput", "int,bool")->getThunk();
+		OnPointerPressedThunk = (OnPointerEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerPointerPressed", "Vector2I,Vector2I,PointerButton,bool,bool,bool,single,bool")->getThunk();
+		OnPointerReleasedThunk = (OnPointerEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerPointerReleased", "Vector2I,Vector2I,PointerButton,bool,bool,bool,single,bool")->getThunk();
+		OnPointerMovedThunk = (OnPointerEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerPointerMove", "Vector2I,Vector2I,PointerButton,bool,bool,bool,single,bool")->getThunk();
+		OnPointerDoubleClickThunk = (OnPointerEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerPointerDoubleClick", "Vector2I,Vector2I,PointerButton,bool,bool,bool,single,bool")->getThunk();
 	}
 
 	void ScriptEditorInput::startUp()
@@ -68,17 +68,17 @@ namespace BansheeEngine
 
 	void ScriptEditorInput::onButtonDown(const ButtonEvent& ev)
 	{
-		MonoUtil::invokeThunk(OnButtonPressedThunk, ev.buttonCode, ev.deviceIdx);
+		MonoUtil::invokeThunk(OnButtonPressedThunk, ev.buttonCode, ev.deviceIdx, ev.isUsed());
 	}
 
 	void ScriptEditorInput::onButtonUp(const ButtonEvent& ev)
 	{
-		MonoUtil::invokeThunk(OnButtonReleasedThunk, ev.buttonCode, ev.deviceIdx);
+		MonoUtil::invokeThunk(OnButtonReleasedThunk, ev.buttonCode, ev.deviceIdx, ev.isUsed());
 	}
 
 	void ScriptEditorInput::onCharInput(const TextInputEvent& ev)
 	{
-		MonoUtil::invokeThunk(OnCharInputThunk, ev.textChar);
+		MonoUtil::invokeThunk(OnCharInputThunk, ev.textChar, ev.isUsed());
 	}
 
 	void ScriptEditorInput::onPointerMoved(const PointerEvent& ev)
@@ -87,7 +87,7 @@ namespace BansheeEngine
 		MonoObject* delta = ScriptVector2I::box(ev.delta);
 
 		MonoUtil::invokeThunk(OnPointerMovedThunk, screenPos, delta,
-			ev.button, ev.shift, ev.control, ev.alt, ev.mouseWheelScrollAmount);
+			ev.button, ev.shift, ev.control, ev.alt, ev.mouseWheelScrollAmount, ev.isUsed());
 	}
 
 	void ScriptEditorInput::onPointerPressed(const PointerEvent& ev)
@@ -96,7 +96,7 @@ namespace BansheeEngine
 		MonoObject* delta = ScriptVector2I::box(ev.delta);
 
 		MonoUtil::invokeThunk(OnPointerPressedThunk, screenPos, delta,
-			ev.button, ev.shift, ev.control, ev.alt, ev.mouseWheelScrollAmount);
+			ev.button, ev.shift, ev.control, ev.alt, ev.mouseWheelScrollAmount, ev.isUsed());
 	}
 
 	void ScriptEditorInput::onPointerReleased(const PointerEvent& ev)
@@ -105,7 +105,7 @@ namespace BansheeEngine
 		MonoObject* delta = ScriptVector2I::box(ev.delta);
 
 		MonoUtil::invokeThunk(OnPointerReleasedThunk, screenPos, delta,
-			ev.button, ev.shift, ev.control, ev.alt, ev.mouseWheelScrollAmount);
+			ev.button, ev.shift, ev.control, ev.alt, ev.mouseWheelScrollAmount, ev.isUsed());
 	}
 
 	void ScriptEditorInput::onPointerDoubleClick(const PointerEvent& ev)
@@ -114,6 +114,6 @@ namespace BansheeEngine
 		MonoObject* delta = ScriptVector2I::box(ev.delta);
 
 		MonoUtil::invokeThunk(OnPointerDoubleClickThunk, screenPos, delta,
-			ev.button, ev.shift, ev.control, ev.alt, ev.mouseWheelScrollAmount);
+			ev.button, ev.shift, ev.control, ev.alt, ev.mouseWheelScrollAmount, ev.isUsed());
 	}
 }

+ 3 - 3
Source/SBansheeEngine/Include/BsScriptInput.h

@@ -69,10 +69,10 @@ namespace BansheeEngine
 		static void internal_getPointerPosition(Vector2I* position);
 		static void internal_getPointerDelta(Vector2I* position);
 
-		typedef void(__stdcall *OnButtonEventThunkDef) (ButtonCode, UINT32, MonoException**);
-		typedef void(__stdcall *OnCharInputEventThunkDef) (UINT32, MonoException**);
+		typedef void(__stdcall *OnButtonEventThunkDef) (ButtonCode, UINT32, bool, MonoException**);
+		typedef void(__stdcall *OnCharInputEventThunkDef) (UINT32, bool, MonoException**);
 		typedef void(__stdcall *OnPointerEventThunkDef) (MonoObject*, MonoObject*, PointerEventButton,
-			bool, bool, bool, float, MonoException**);
+			bool, bool, bool, float, bool, MonoException**);
 
 		static OnButtonEventThunkDef OnButtonPressedThunk;
 		static OnButtonEventThunkDef OnButtonReleasedThunk;

+ 14 - 14
Source/SBansheeEngine/Source/BsScriptInput.cpp

@@ -44,13 +44,13 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_GetPointerPosition", &ScriptInput::internal_getPointerPosition);
 		metaData.scriptClass->addInternalCall("Internal_GetPointerDelta", &ScriptInput::internal_getPointerDelta);
 
-		OnButtonPressedThunk = (OnButtonEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerButtonDown", "ButtonCode,int")->getThunk();
-		OnButtonReleasedThunk = (OnButtonEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerButtonUp", "ButtonCode,int")->getThunk();
-		OnCharInputThunk = (OnCharInputEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerCharInput", "int")->getThunk();
-		OnPointerPressedThunk = (OnPointerEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerPointerPressed", "Vector2I,Vector2I,PointerButton,bool,bool,bool,single")->getThunk();
-		OnPointerReleasedThunk = (OnPointerEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerPointerReleased", "Vector2I,Vector2I,PointerButton,bool,bool,bool,single")->getThunk();
-		OnPointerMovedThunk = (OnPointerEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerPointerMove", "Vector2I,Vector2I,PointerButton,bool,bool,bool,single")->getThunk();
-		OnPointerDoubleClickThunk = (OnPointerEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerPointerDoubleClick", "Vector2I,Vector2I,PointerButton,bool,bool,bool,single")->getThunk();
+		OnButtonPressedThunk = (OnButtonEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerButtonDown", "ButtonCode,int,bool")->getThunk();
+		OnButtonReleasedThunk = (OnButtonEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerButtonUp", "ButtonCode,int,bool")->getThunk();
+		OnCharInputThunk = (OnCharInputEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerCharInput", "int,bool")->getThunk();
+		OnPointerPressedThunk = (OnPointerEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerPointerPressed", "Vector2I,Vector2I,PointerButton,bool,bool,bool,single,bool")->getThunk();
+		OnPointerReleasedThunk = (OnPointerEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerPointerReleased", "Vector2I,Vector2I,PointerButton,bool,bool,bool,single,bool")->getThunk();
+		OnPointerMovedThunk = (OnPointerEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerPointerMove", "Vector2I,Vector2I,PointerButton,bool,bool,bool,single,bool")->getThunk();
+		OnPointerDoubleClickThunk = (OnPointerEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerPointerDoubleClick", "Vector2I,Vector2I,PointerButton,bool,bool,bool,single,bool")->getThunk();
 	}
 
 	void ScriptInput::startUp()
@@ -82,7 +82,7 @@ namespace BansheeEngine
 		if (PlayInEditorManager::instance().getState() != PlayInEditorState::Playing)
 			return;
 
-		MonoUtil::invokeThunk(OnButtonPressedThunk, ev.buttonCode, ev.deviceIdx);
+		MonoUtil::invokeThunk(OnButtonPressedThunk, ev.buttonCode, ev.deviceIdx, ev.isUsed());
 	}
 
 	void ScriptInput::onButtonUp(const ButtonEvent& ev)
@@ -90,7 +90,7 @@ namespace BansheeEngine
 		if (PlayInEditorManager::instance().getState() != PlayInEditorState::Playing)
 			return;
 
-		MonoUtil::invokeThunk(OnButtonReleasedThunk, ev.buttonCode, ev.deviceIdx);
+		MonoUtil::invokeThunk(OnButtonReleasedThunk, ev.buttonCode, ev.deviceIdx, ev.isUsed());
 	}
 
 	void ScriptInput::onCharInput(const TextInputEvent& ev)
@@ -98,7 +98,7 @@ namespace BansheeEngine
 		if (PlayInEditorManager::instance().getState() != PlayInEditorState::Playing)
 			return;
 
-		MonoUtil::invokeThunk(OnCharInputThunk, ev.textChar);
+		MonoUtil::invokeThunk(OnCharInputThunk, ev.textChar, ev.isUsed());
 	}
 
 	void ScriptInput::onPointerMoved(const PointerEvent& ev)
@@ -110,7 +110,7 @@ namespace BansheeEngine
 		MonoObject* delta = ScriptVector2I::box(ev.delta);
 
 		MonoUtil::invokeThunk(OnPointerMovedThunk, screenPos, delta, 
-			ev.button, ev.shift, ev.control, ev.alt, ev.mouseWheelScrollAmount);
+			ev.button, ev.shift, ev.control, ev.alt, ev.mouseWheelScrollAmount, ev.isUsed());
 	}
 
 	void ScriptInput::onPointerPressed(const PointerEvent& ev)
@@ -122,7 +122,7 @@ namespace BansheeEngine
 		MonoObject* delta = ScriptVector2I::box(ev.delta);
 
 		MonoUtil::invokeThunk(OnPointerPressedThunk, screenPos, delta, 
-			ev.button, ev.shift, ev.control, ev.alt, ev.mouseWheelScrollAmount);
+			ev.button, ev.shift, ev.control, ev.alt, ev.mouseWheelScrollAmount, ev.isUsed());
 	}
 
 	void ScriptInput::onPointerReleased(const PointerEvent& ev)
@@ -134,7 +134,7 @@ namespace BansheeEngine
 		MonoObject* delta = ScriptVector2I::box(ev.delta);
 
 		MonoUtil::invokeThunk(OnPointerReleasedThunk, screenPos, delta, 
-			ev.button, ev.shift, ev.control, ev.alt, ev.mouseWheelScrollAmount);
+			ev.button, ev.shift, ev.control, ev.alt, ev.mouseWheelScrollAmount, ev.isUsed());
 	}
 
 	void ScriptInput::onPointerDoubleClick(const PointerEvent& ev)
@@ -146,7 +146,7 @@ namespace BansheeEngine
 		MonoObject* delta = ScriptVector2I::box(ev.delta);
 
 		MonoUtil::invokeThunk(OnPointerDoubleClickThunk, screenPos, delta, 
-			ev.button, ev.shift, ev.control, ev.alt, ev.mouseWheelScrollAmount);
+			ev.button, ev.shift, ev.control, ev.alt, ev.mouseWheelScrollAmount, ev.isUsed());
 	}
 
 	bool ScriptInput::internal_isButtonHeld(ButtonCode code, UINT32 deviceIdx)