Browse Source

Better handle input so that input events aren't skipped or in wrong order when window focus is changing

Marko Pintera 10 years ago
parent
commit
9699e887c4

+ 7 - 2
BansheeCore/Include/BsCoreApplication.h

@@ -88,9 +88,14 @@ namespace BansheeEngine
 
 
 	protected:
 	protected:
 		/**
 		/**
-		 * @brief	Called for each iteration of the main loop.
+		 * @brief	Called for each iteration of the main loop. Called before any game objects or plugins are updated.
 		 */
 		 */
-		virtual void update();
+		virtual void preUpdate();
+
+		/**
+		 * @brief	Called for each iteration of the main loop. Called after all game objects and plugins are updated.
+		 */
+		virtual void postUpdate();
 
 
 	private:
 	private:
 		/**
 		/**

+ 40 - 1
BansheeCore/Include/BsInput.h

@@ -41,6 +41,27 @@ namespace BansheeEngine
 			ButtonState keyStates[BC_Count];
 			ButtonState keyStates[BC_Count];
 		};
 		};
 
 
+		/**
+		 * @brief	Different types of possible input event callbacks.
+		 */
+		enum class EventType
+		{
+			ButtonUp, ButtonDown, PointerMoved, PointerUp, PointerDown, PointerDoubleClick, TextInput, Command
+		};
+
+		/**
+		 * @brief	Stores information about a queued input event that is to be triggered later.
+		 */
+		struct QueuedEvent
+		{
+			QueuedEvent(EventType type, UINT32 idx)
+				:type(type), idx(idx)
+			{ }
+
+			EventType type;
+			UINT32 idx;
+		};
+
 	public:
 	public:
 		Input();
 		Input();
 		~Input();
 		~Input();
@@ -94,12 +115,18 @@ namespace BansheeEngine
 		void _registerRawInputHandler(std::shared_ptr<RawInputHandler> inputHandler);
 		void _registerRawInputHandler(std::shared_ptr<RawInputHandler> inputHandler);
 
 
 		/**
 		/**
-		 * @brief	Called every frame. Dispatches any callbacks resulting from input by the user.
+		 * @brief	Called every frame. Detects button state changes and prepares callback events to trigger
+		 *			via a call to "_triggerCallbacks".
 		 *
 		 *
 		 * @note	Internal method.
 		 * @note	Internal method.
 		 */
 		 */
 		void _update();
 		void _update();
 
 
+		/**
+		 * @brief	Triggers any queued input event callbacks.
+		 */
+		void _triggerCallbacks();
+
 		/**
 		/**
 		 * @brief	Returns value of the specified input axis. Normally in range [-1.0, 1.0] but can be outside
 		 * @brief	Returns value of the specified input axis. Normally in range [-1.0, 1.0] but can be outside
 		 *			the range for devices with unbound axes (e.g. mouse).
 		 *			the range for devices with unbound axes (e.g. mouse).
@@ -241,6 +268,18 @@ namespace BansheeEngine
 		bool mPointerDoubleClicked;
 		bool mPointerDoubleClicked;
 		bool mLastPositionSet;
 		bool mLastPositionSet;
 
 
+		Vector<QueuedEvent> mQueuedEvents;
+
+		Vector<TextInputEvent> mTextInputEvents;
+		Vector<InputCommandType> mCommandEvents;
+		Vector<PointerEvent> mPointerDoubleClickEvents;
+		Vector<PointerEvent> mPointerReleasedEvents;
+		Vector<PointerEvent> mPointerPressedEvents;
+		Vector<PointerEvent> mPointerMovedEvents;
+
+		Vector<ButtonEvent> mButtonDownEvents;
+		Vector<ButtonEvent> mButtonUpEvents;
+
 		/************************************************************************/
 		/************************************************************************/
 		/* 								STATICS		                      		*/
 		/* 								STATICS		                      		*/
 		/************************************************************************/
 		/************************************************************************/

+ 15 - 3
BansheeCore/Source/BsCoreApplication.cpp

@@ -177,9 +177,16 @@ namespace BansheeEngine
 
 
 			Platform::_update();
 			Platform::_update();
 			DeferredCallManager::instance()._update();
 			DeferredCallManager::instance()._update();
-			RenderWindowManager::instance()._update();
 			gTime().update();
 			gTime().update();
 			gInput()._update();
 			gInput()._update();
+			// RenderWindowManager::update needs to happen after Input::update and before Input::_triggerCallbacks,
+			// so that all input is properly captured in case there is a focus change, and so that
+			// focus change is registered before input events are sent out (mouse press can result in code
+			// checking if a window is in focus, so it has to be up to date)
+			RenderWindowManager::instance()._update(); 
+			gInput()._triggerCallbacks();
+
+			preUpdate();
 
 
 			PROFILE_CALL(gCoreSceneManager()._update(), "SceneManager");
 			PROFILE_CALL(gCoreSceneManager()._update(), "SceneManager");
 
 
@@ -191,7 +198,7 @@ namespace BansheeEngine
 			for (auto& pluginUpdateFunc : mPluginUpdateFunctions)
 			for (auto& pluginUpdateFunc : mPluginUpdateFunctions)
 				pluginUpdateFunc.second();
 				pluginUpdateFunc.second();
 
 
-			update();
+			postUpdate();
 
 
 			// Send out resource events in case any were loaded/destroyed/modified
 			// Send out resource events in case any were loaded/destroyed/modified
 			ResourceListenerManager::instance().update();
 			ResourceListenerManager::instance().update();
@@ -240,7 +247,12 @@ namespace BansheeEngine
 		}
 		}
 	}
 	}
 
 
-	void CoreApplication::update()
+	void CoreApplication::preUpdate()
+	{
+		// Do nothing
+	}
+
+	void CoreApplication::postUpdate()
 	{
 	{
 		// Do nothing
 		// Do nothing
 	}
 	}

+ 70 - 32
BansheeCore/Source/BsInput.cpp

@@ -99,6 +99,50 @@ namespace BansheeEngine
 			mOSInputHandler->_update();
 			mOSInputHandler->_update();
 	}
 	}
 
 
+	void Input::_triggerCallbacks()
+	{
+		for (auto& event : mQueuedEvents)
+		{
+			switch (event.type)
+			{
+			case EventType::ButtonDown:
+				onButtonDown(mButtonDownEvents[event.idx]);
+				break;
+			case EventType::ButtonUp:
+				onButtonUp(mButtonUpEvents[event.idx]);
+				break;
+			case EventType::PointerDown:
+				onPointerPressed(mPointerPressedEvents[event.idx]);
+				break;
+			case EventType::PointerUp:
+				onPointerReleased(mPointerReleasedEvents[event.idx]);
+				break;
+			case EventType::PointerDoubleClick:
+				onPointerDoubleClick(mPointerDoubleClickEvents[event.idx]);
+				break;
+			case EventType::PointerMoved:
+				onPointerMoved(mPointerMovedEvents[event.idx]);
+				break;
+			case EventType::TextInput:
+				onCharInput(mTextInputEvents[event.idx]);
+				break;
+			case EventType::Command:
+				onInputCommand(mCommandEvents[event.idx]);
+				break;
+			}
+		}
+
+		mQueuedEvents.clear();
+		mButtonDownEvents.clear();
+		mButtonUpEvents.clear();
+		mPointerPressedEvents.clear();
+		mPointerReleasedEvents.clear();
+		mPointerDoubleClickEvents.clear();
+		mPointerMovedEvents.clear();
+		mTextInputEvents.clear();
+		mCommandEvents.clear();
+	}
+
 	void Input::inputWindowChanged(RenderWindow& win)
 	void Input::inputWindowChanged(RenderWindow& win)
 	{
 	{
 		if(mRawInputHandler != nullptr)
 		if(mRawInputHandler != nullptr)
@@ -115,15 +159,13 @@ namespace BansheeEngine
 
 
 		mDevices[deviceIdx].keyStates[code & 0x0000FFFF] = ButtonState::ToggledOn;
 		mDevices[deviceIdx].keyStates[code & 0x0000FFFF] = ButtonState::ToggledOn;
 
 
-		if(!onButtonDown.empty())
-		{
-			ButtonEvent btnEvent;
-			btnEvent.buttonCode = code;
-			btnEvent.timestamp = timestamp;
-			btnEvent.deviceIdx = deviceIdx;
+		ButtonEvent btnEvent;
+		btnEvent.buttonCode = code;
+		btnEvent.timestamp = timestamp;
+		btnEvent.deviceIdx = deviceIdx;
 
 
-			onButtonDown(btnEvent);
-		}
+		mQueuedEvents.push_back(QueuedEvent(EventType::ButtonDown, (UINT32)mButtonDownEvents.size()));
+		mButtonDownEvents.push_back(btnEvent);
 	}
 	}
 
 
 	void Input::buttonUp(UINT32 deviceIdx, ButtonCode code, UINT64 timestamp)
 	void Input::buttonUp(UINT32 deviceIdx, ButtonCode code, UINT64 timestamp)
@@ -136,15 +178,13 @@ namespace BansheeEngine
 		else
 		else
 			mDevices[deviceIdx].keyStates[code & 0x0000FFFF] = ButtonState::ToggledOff;
 			mDevices[deviceIdx].keyStates[code & 0x0000FFFF] = ButtonState::ToggledOff;
 
 
-		if(!onButtonUp.empty())
-		{
-			ButtonEvent btnEvent;
-			btnEvent.buttonCode = code;
-			btnEvent.timestamp = timestamp;
-			btnEvent.deviceIdx = deviceIdx;
+		ButtonEvent btnEvent;
+		btnEvent.buttonCode = code;
+		btnEvent.timestamp = timestamp;
+		btnEvent.deviceIdx = deviceIdx;
 
 
-			onButtonUp(btnEvent);
-		}
+		mQueuedEvents.push_back(QueuedEvent(EventType::ButtonUp, (UINT32)mButtonUpEvents.size()));
+		mButtonUpEvents.push_back(btnEvent);
 	}
 	}
 
 
 	void Input::axisMoved(UINT32 deviceIdx, const RawAxisState& state, UINT32 axis)
 	void Input::axisMoved(UINT32 deviceIdx, const RawAxisState& state, UINT32 axis)
@@ -161,8 +201,8 @@ namespace BansheeEngine
 
 
 	void Input::cursorMoved(const PointerEvent& event)
 	void Input::cursorMoved(const PointerEvent& event)
 	{
 	{
-		if(!onPointerMoved.empty())
-			onPointerMoved(event);
+		mQueuedEvents.push_back(QueuedEvent(EventType::PointerMoved, (UINT32)mPointerMovedEvents.size()));
+		mPointerMovedEvents.push_back(event);
 
 
 		if (mLastPositionSet)
 		if (mLastPositionSet)
 			mPointerDelta = event.screenPos - mPointerPosition;
 			mPointerDelta = event.screenPos - mPointerPosition;
@@ -175,8 +215,8 @@ namespace BansheeEngine
 	{
 	{
 		mPointerButtonStates[(UINT32)event.button] = ButtonState::ToggledOn;
 		mPointerButtonStates[(UINT32)event.button] = ButtonState::ToggledOn;
 
 
-		if(!onPointerPressed.empty())
-			onPointerPressed(event);
+		mQueuedEvents.push_back(QueuedEvent(EventType::PointerDown, (UINT32)mPointerPressedEvents.size()));
+		mPointerPressedEvents.push_back(event);
 	}
 	}
 
 
 	void Input::cursorReleased(const PointerEvent& event)
 	void Input::cursorReleased(const PointerEvent& event)
@@ -186,33 +226,31 @@ namespace BansheeEngine
 		else
 		else
 			mPointerButtonStates[(UINT32)event.button] = ButtonState::ToggledOff;
 			mPointerButtonStates[(UINT32)event.button] = ButtonState::ToggledOff;
 
 
-		if(!onPointerReleased.empty())
-			onPointerReleased(event);
+		mQueuedEvents.push_back(QueuedEvent(EventType::PointerUp, (UINT32)mPointerReleasedEvents.size()));
+		mPointerReleasedEvents.push_back(event);
 	}
 	}
 
 
 	void Input::cursorDoubleClick(const PointerEvent& event)
 	void Input::cursorDoubleClick(const PointerEvent& event)
 	{
 	{
 		mPointerDoubleClicked = true;
 		mPointerDoubleClicked = true;
 
 
-		if(!onPointerDoubleClick.empty())
-			onPointerDoubleClick(event);
+		mQueuedEvents.push_back(QueuedEvent(EventType::PointerDoubleClick, (UINT32)mPointerDoubleClickEvents.size()));
+		mPointerDoubleClickEvents.push_back(event);
 	}
 	}
 
 
 	void Input::inputCommandEntered(InputCommandType commandType)
 	void Input::inputCommandEntered(InputCommandType commandType)
 	{
 	{
-		if(!onInputCommand.empty())
-			onInputCommand(commandType);
+		mQueuedEvents.push_back(QueuedEvent(EventType::Command, (UINT32)mCommandEvents.size()));
+		mCommandEvents.push_back(commandType);
 	}
 	}
 
 
 	void Input::charInput(UINT32 chr)
 	void Input::charInput(UINT32 chr)
 	{
 	{
-		if(!onCharInput.empty())
-		{
-			TextInputEvent textInputEvent;
-			textInputEvent.textChar = chr;
+		TextInputEvent textInputEvent;
+		textInputEvent.textChar = chr;
 
 
-			onCharInput(textInputEvent);
-		}
+		mQueuedEvents.push_back(QueuedEvent(EventType::TextInput, (UINT32)mTextInputEvents.size()));
+		mTextInputEvents.push_back(textInputEvent);
 	}
 	}
 
 
 	float Input::getAxisValue(UINT32 type, UINT32 deviceIdx) const
 	float Input::getAxisValue(UINT32 type, UINT32 deviceIdx) const

+ 10 - 8
BansheeCore/Source/BsRenderWindowManager.cpp

@@ -51,9 +51,8 @@ namespace BansheeEngine
 				mMovedOrResizedWindows.erase(iterFind2);
 				mMovedOrResizedWindows.erase(iterFind2);
 
 
 			mCoreToNonCoreMap.erase(window->getCore().get());
 			mCoreToNonCoreMap.erase(window->getCore().get());
+			mDirtyProperties.erase(window);
 		}
 		}
-
-		mDirtyProperties.erase(window);
 	}
 	}
 
 
 	void RenderWindowManager::notifyFocusReceived(RenderWindowCore* coreWindow)
 	void RenderWindowManager::notifyFocusReceived(RenderWindowCore* coreWindow)
@@ -162,12 +161,12 @@ namespace BansheeEngine
 
 
 			mouseLeftWindows = mMouseLeftWindows;
 			mouseLeftWindows = mMouseLeftWindows;
 			mMouseLeftWindows.clear();
 			mMouseLeftWindows.clear();
-		}
 
 
-		for (auto& dirtyPropertyWindow : mDirtyProperties)
-			dirtyPropertyWindow->syncProperties();
+			for (auto& dirtyPropertyWindow : mDirtyProperties)
+				dirtyPropertyWindow->syncProperties();
 
 
-		mDirtyProperties.clear();
+			mDirtyProperties.clear();
+		}
 
 
 		if(mWindowInFocus != newWinInFocus)
 		if(mWindowInFocus != newWinInFocus)
 		{
 		{
@@ -219,6 +218,8 @@ namespace BansheeEngine
 
 
 	void RenderWindowCoreManager::_update()
 	void RenderWindowCoreManager::_update()
 	{
 	{
+		BS_LOCK_MUTEX(mWindowMutex);
+
 		for (auto& dirtyPropertyWindow : mDirtyProperties)
 		for (auto& dirtyPropertyWindow : mDirtyProperties)
 			dirtyPropertyWindow->syncProperties();
 			dirtyPropertyWindow->syncProperties();
 
 
@@ -243,9 +244,8 @@ namespace BansheeEngine
 				BS_EXCEPT(InternalErrorException, "Trying to destroy a window that is not in the created windows list.");
 				BS_EXCEPT(InternalErrorException, "Trying to destroy a window that is not in the created windows list.");
 
 
 			mCreatedWindows.erase(iterFind);
 			mCreatedWindows.erase(iterFind);
+			mDirtyProperties.erase(window);
 		}
 		}
-
-		mDirtyProperties.erase(window);
 	}
 	}
 
 
 	Vector<RenderWindowCore*> RenderWindowCoreManager::getRenderWindows() const
 	Vector<RenderWindowCore*> RenderWindowCoreManager::getRenderWindows() const
@@ -257,6 +257,8 @@ namespace BansheeEngine
 
 
 	void RenderWindowCoreManager::notifySyncDataDirty(RenderWindowCore* window)
 	void RenderWindowCoreManager::notifySyncDataDirty(RenderWindowCore* window)
 	{
 	{
+		BS_LOCK_MUTEX(mWindowMutex);
+
 		mDirtyProperties.insert(window);
 		mDirtyProperties.insert(window);
 	}
 	}
 }
 }

+ 16 - 16
BansheeCore/Source/Win32/BsWin32Platform.cpp

@@ -658,7 +658,7 @@ namespace BansheeEngine
 				if( active )
 				if( active )
 					win->setActive(true);
 					win->setActive(true);
 
 
-				break;
+				return 0;
 			}
 			}
 		case WM_DESTROY:
 		case WM_DESTROY:
 			{
 			{
@@ -700,21 +700,21 @@ namespace BansheeEngine
 					}
 					}
 				}
 				}
 
 
-				break;
+				return 0;
 			}
 			}
 		case WM_SETFOCUS:
 		case WM_SETFOCUS:
 			{
 			{
 				if (!win->getProperties().hasFocus())
 				if (!win->getProperties().hasFocus())
 					win->_windowFocusReceived();
 					win->_windowFocusReceived();
 
 
-				break;
+				return 0;
 			}
 			}
 		case WM_KILLFOCUS:
 		case WM_KILLFOCUS:
 			{
 			{
 				if (win->getProperties().hasFocus())
 				if (win->getProperties().hasFocus())
 					win->_windowFocusLost();
 					win->_windowFocusLost();
 
 
-				break;
+				return 0;
 			}
 			}
 		case WM_SYSCHAR:
 		case WM_SYSCHAR:
 			// return zero to bypass defProc and signal we processed the message, unless it's an ALT-space
 			// return zero to bypass defProc and signal we processed the message, unless it's an ALT-space
@@ -723,7 +723,7 @@ namespace BansheeEngine
 			break;
 			break;
 		case WM_MOVE:
 		case WM_MOVE:
 			win->_windowMovedOrResized();
 			win->_windowMovedOrResized();
-			break;
+			return 0;
 		case WM_DISPLAYCHANGE:
 		case WM_DISPLAYCHANGE:
 			win->_windowMovedOrResized();
 			win->_windowMovedOrResized();
 			break;
 			break;
@@ -737,7 +737,7 @@ namespace BansheeEngine
 			else if (wParam == SIZE_RESTORED)
 			else if (wParam == SIZE_RESTORED)
 				win->_notifyRestored();
 				win->_notifyRestored();
 
 
-			break;
+			return 0;
 		case WM_SETCURSOR:
 		case WM_SETCURSOR:
 			if(isCursorHidden())
 			if(isCursorHidden())
 				SetCursor(nullptr);
 				SetCursor(nullptr);
@@ -859,7 +859,7 @@ namespace BansheeEngine
 				if (!onMouseLeftWindow.empty())
 				if (!onMouseLeftWindow.empty())
 					onMouseLeftWindow(win);
 					onMouseLeftWindow(win);
 			}
 			}
-			break;
+			return 0;
 		case WM_LBUTTONUP:
 		case WM_LBUTTONUP:
 			{
 			{
 				ReleaseCapture();
 				ReleaseCapture();
@@ -872,7 +872,7 @@ namespace BansheeEngine
 				if(!onCursorButtonReleased.empty())
 				if(!onCursorButtonReleased.empty())
 					onCursorButtonReleased(intMousePos, OSMouseButton::Left, btnStates);
 					onCursorButtonReleased(intMousePos, OSMouseButton::Left, btnStates);
 			}
 			}
-			break;
+			return 0;
 		case WM_MBUTTONUP:
 		case WM_MBUTTONUP:
 			{
 			{
 				ReleaseCapture();
 				ReleaseCapture();
@@ -885,7 +885,7 @@ namespace BansheeEngine
 				if(!onCursorButtonReleased.empty())
 				if(!onCursorButtonReleased.empty())
 					onCursorButtonReleased(intMousePos, OSMouseButton::Middle, btnStates);
 					onCursorButtonReleased(intMousePos, OSMouseButton::Middle, btnStates);
 			}
 			}
-			break;
+			return 0;
 		case WM_RBUTTONUP:
 		case WM_RBUTTONUP:
 			{
 			{
 				ReleaseCapture();
 				ReleaseCapture();
@@ -898,7 +898,7 @@ namespace BansheeEngine
 				if(!onCursorButtonReleased.empty())
 				if(!onCursorButtonReleased.empty())
 					onCursorButtonReleased(intMousePos, OSMouseButton::Right, btnStates);
 					onCursorButtonReleased(intMousePos, OSMouseButton::Right, btnStates);
 			}
 			}
-			break;
+			return 0;
 		case WM_LBUTTONDOWN:
 		case WM_LBUTTONDOWN:
 			{
 			{
 				SetCapture(hWnd);
 				SetCapture(hWnd);
@@ -911,7 +911,7 @@ namespace BansheeEngine
 				if(!onCursorButtonPressed.empty())
 				if(!onCursorButtonPressed.empty())
 					onCursorButtonPressed(intMousePos, OSMouseButton::Left, btnStates);
 					onCursorButtonPressed(intMousePos, OSMouseButton::Left, btnStates);
 			}
 			}
-			break;
+			return 0;
 		case WM_MBUTTONDOWN:
 		case WM_MBUTTONDOWN:
 			{
 			{
 				SetCapture(hWnd);
 				SetCapture(hWnd);
@@ -924,7 +924,7 @@ namespace BansheeEngine
 				if(!onCursorButtonPressed.empty())
 				if(!onCursorButtonPressed.empty())
 					onCursorButtonPressed(intMousePos, OSMouseButton::Middle, btnStates);
 					onCursorButtonPressed(intMousePos, OSMouseButton::Middle, btnStates);
 			}
 			}
-			break;
+			return 0;
 		case WM_RBUTTONDOWN:
 		case WM_RBUTTONDOWN:
 			{
 			{
 				SetCapture(hWnd);
 				SetCapture(hWnd);
@@ -937,7 +937,7 @@ namespace BansheeEngine
 				if(!onCursorButtonPressed.empty())
 				if(!onCursorButtonPressed.empty())
 					onCursorButtonPressed(intMousePos, OSMouseButton::Right, btnStates);
 					onCursorButtonPressed(intMousePos, OSMouseButton::Right, btnStates);
 			}
 			}
-			break;
+			return 0;
 		case WM_LBUTTONDBLCLK:
 		case WM_LBUTTONDBLCLK:
 			{
 			{
 				Vector2I intMousePos;
 				Vector2I intMousePos;
@@ -948,7 +948,7 @@ namespace BansheeEngine
 				if(!onCursorDoubleClick.empty())
 				if(!onCursorDoubleClick.empty())
 					onCursorDoubleClick(intMousePos, btnStates);
 					onCursorDoubleClick(intMousePos, btnStates);
 			}
 			}
-			break;
+			return 0;
 		case WM_NCMOUSEMOVE:
 		case WM_NCMOUSEMOVE:
 		case WM_MOUSEMOVE:
 		case WM_MOUSEMOVE:
 			{
 			{
@@ -1026,7 +1026,7 @@ namespace BansheeEngine
 					isCtrlPressed = false;
 					isCtrlPressed = false;
 				}
 				}
 
 
-				break;
+				return 0;
 			}
 			}
 		case WM_CHAR:
 		case WM_CHAR:
 			{
 			{
@@ -1077,7 +1077,7 @@ namespace BansheeEngine
 		case WM_CAPTURECHANGED:
 		case WM_CAPTURECHANGED:
 			if(!onMouseCaptureChanged.empty())
 			if(!onMouseCaptureChanged.empty())
 				onMouseCaptureChanged();
 				onMouseCaptureChanged();
-			break;
+			return 0;
 		}
 		}
 
 
 		return DefWindowProc( hWnd, uMsg, wParam, lParam );
 		return DefWindowProc( hWnd, uMsg, wParam, lParam );

+ 2 - 1
BansheeEditor/Include/BsEditorApplication.h

@@ -39,7 +39,8 @@ namespace BansheeEngine
 	private:
 	private:
 		virtual void onStartUp();
 		virtual void onStartUp();
 		virtual void onShutDown();
 		virtual void onShutDown();
-		virtual void update();
+		virtual void preUpdate() override;
+		virtual void postUpdate() override;
 
 
 		EditorWidgetLayoutPtr loadWidgetLayout();
 		EditorWidgetLayoutPtr loadWidgetLayout();
 		void saveWidgetLayout(const EditorWidgetLayoutPtr& layout);
 		void saveWidgetLayout(const EditorWidgetLayoutPtr& layout);

+ 5 - 16
BansheeEditor/Include/BsEditorWidgetManager.h

@@ -17,6 +17,11 @@ namespace BansheeEngine
 		EditorWidgetManager();
 		EditorWidgetManager();
 		~EditorWidgetManager();
 		~EditorWidgetManager();
 
 
+		/**
+		 * @brief	Called every frame.
+		 */
+		void update();
+
 		/**
 		/**
 		 * @brief	Registers a widget that can then be opened by calling "open". When loading
 		 * @brief	Registers a widget that can then be opened by calling "open". When loading
 		 * 			a widget layout this name and callback will be used to attempt creating the widget.
 		 * 			a widget layout this name and callback will be used to attempt creating the widget.
@@ -78,21 +83,6 @@ namespace BansheeEngine
 		static void preRegisterWidget(const String& name, std::function<EditorWidgetBase*(EditorWidgetContainer&)> createCallback);
 		static void preRegisterWidget(const String& name, std::function<EditorWidgetBase*(EditorWidgetContainer&)> createCallback);
 
 
 	private:
 	private:
-		/**
-		 * @brief	Called whenever a pointer (e.g. mouse cursor) is moved.
-		 */
-		void onPointerMoved(const PointerEvent& event);
-
-		/**
-		 * @brief	Called whenever a pointer button (e.g. mouse button) is released.
-		 */
-		void onPointerReleased(const PointerEvent& event);
-
-		/**
-		 * @brief	Called whenever a pointer button (e.g. mouse button) is pressed.
-		 */
-		void onPointerPressed(const PointerEvent& event);
-
 		/**
 		/**
 		 * @brief	Triggered whenever a window gains focus.
 		 * @brief	Triggered whenever a window gains focus.
 		 */
 		 */
@@ -106,7 +96,6 @@ namespace BansheeEngine
 		Map<String, EditorWidgetBase*> mActiveWidgets;
 		Map<String, EditorWidgetBase*> mActiveWidgets;
 		Map<String, std::function<EditorWidgetBase*(EditorWidgetContainer&)>> mCreateCallbacks;
 		Map<String, std::function<EditorWidgetBase*(EditorWidgetContainer&)>> mCreateCallbacks;
 
 
-		HEvent mOnPointerPressedConn;
 		HEvent mOnFocusLostConn;
 		HEvent mOnFocusLostConn;
 		HEvent mOnFocusGainedConn;
 		HEvent mOnFocusGainedConn;
 
 

+ 9 - 2
BansheeEditor/Source/BsEditorApplication.cpp

@@ -401,9 +401,16 @@ namespace BansheeEngine
 		window->destroy();
 		window->destroy();
 	}
 	}
 
 
-	void EditorApplication::update()
+	void EditorApplication::preUpdate()
 	{
 	{
-		Application::update();
+		Application::preUpdate();
+
+		EditorWidgetManager::instance().update();
+	}
+
+	void EditorApplication::postUpdate()
+	{
+		Application::postUpdate();
 
 
 		ProjectLibrary::instance().update();
 		ProjectLibrary::instance().update();
 		EditorWindowManager::instance().update();	
 		EditorWindowManager::instance().update();	

+ 37 - 36
BansheeEditor/Source/BsEditorWidgetManager.cpp

@@ -29,14 +29,12 @@ namespace BansheeEngine
 			registerWidget(curElement.first, curElement.second);
 			registerWidget(curElement.first, curElement.second);
 		}
 		}
 
 
-		mOnPointerPressedConn = gInput().onPointerPressed.connect(std::bind(&EditorWidgetManager::onPointerPressed, this, _1));
 		mOnFocusLostConn = RenderWindowManager::instance().onFocusLost.connect(std::bind(&EditorWidgetManager::onFocusLost, this, _1));
 		mOnFocusLostConn = RenderWindowManager::instance().onFocusLost.connect(std::bind(&EditorWidgetManager::onFocusLost, this, _1));
 		mOnFocusGainedConn = RenderWindowManager::instance().onFocusGained.connect(std::bind(&EditorWidgetManager::onFocusGained, this, _1));
 		mOnFocusGainedConn = RenderWindowManager::instance().onFocusGained.connect(std::bind(&EditorWidgetManager::onFocusGained, this, _1));
 	}
 	}
 
 
 	EditorWidgetManager::~EditorWidgetManager()
 	EditorWidgetManager::~EditorWidgetManager()
 	{
 	{
-		mOnPointerPressedConn.disconnect();
 		mOnFocusLostConn.disconnect();
 		mOnFocusLostConn.disconnect();
 		mOnFocusGainedConn.disconnect();
 		mOnFocusGainedConn.disconnect();
 
 
@@ -46,6 +44,43 @@ namespace BansheeEngine
 			widget.second->close();
 			widget.second->close();
 	}
 	}
 
 
+	void EditorWidgetManager::update()
+	{
+		if (gInput().isPointerButtonDown(PointerEventButton::Left))
+		{
+			for (auto& widgetData : mActiveWidgets)
+			{
+				EditorWidgetBase* widget = widgetData.second;
+				EditorWidgetContainer* parentContainer = widget->_getParent();
+				EditorWindowBase* parentWindow = parentContainer->getParentWindow();
+				RenderWindowPtr parentRenderWindow = parentWindow->getRenderWindow();
+				const RenderWindowProperties& props = parentRenderWindow->getProperties();
+
+				if (!props.hasFocus())
+				{
+					widget->_setHasFocus(false);
+					continue;
+				}
+
+				if (parentContainer->getActiveWidget() != widget)
+				{
+					widget->_setHasFocus(false);
+					continue;
+				}
+
+				Vector2I widgetPos = widget->screenToWidgetPos(gInput().getPointerPosition());
+				if (widgetPos.x >= 0 && widgetPos.y >= 0
+					&& widgetPos.x < (INT32)widget->getWidth()
+					&& widgetPos.y < (INT32)widget->getHeight())
+				{
+					widget->_setHasFocus(true);
+				}
+				else
+					widget->_setHasFocus(false);
+			}
+		}
+	}
+
 	void EditorWidgetManager::registerWidget(const String& name, std::function<EditorWidgetBase*(EditorWidgetContainer&)> createCallback)
 	void EditorWidgetManager::registerWidget(const String& name, std::function<EditorWidgetBase*(EditorWidgetContainer&)> createCallback)
 	{
 	{
 		auto iterFind = mCreateCallbacks.find(name);
 		auto iterFind = mCreateCallbacks.find(name);
@@ -234,40 +269,6 @@ namespace BansheeEngine
 			mainWindow->getRenderWindow()->maximize(gCoreAccessor());
 			mainWindow->getRenderWindow()->maximize(gCoreAccessor());
 	}
 	}
 
 
-	void EditorWidgetManager::onPointerPressed(const PointerEvent& event)
-	{
-		for (auto& widgetData : mActiveWidgets)
-		{
-			EditorWidgetBase* widget = widgetData.second;
-			EditorWidgetContainer* parentContainer = widget->_getParent();
-			EditorWindowBase* parentWindow = parentContainer->getParentWindow();
-			RenderWindowPtr parentRenderWindow = parentWindow->getRenderWindow();
-			const RenderWindowProperties& props = parentRenderWindow->getProperties();
-
-			if (!props.hasFocus())
-			{
-				widget->_setHasFocus(false);
-				continue;
-			}
-
-			if (parentContainer->getActiveWidget() != widget)
-			{
-				widget->_setHasFocus(false);
-				continue;
-			}
-
-			Vector2I widgetPos = widget->screenToWidgetPos(event.screenPos);
-			if (widgetPos.x >= 0 && widgetPos.y >= 0 
-				&& widgetPos.x < (INT32)widget->getWidth() 
-				&& widgetPos.y < (INT32)widget->getHeight())
-			{
-				widget->_setHasFocus(true);
-			}
-			else
-				widget->_setHasFocus(false);
-		}
-	}
-
 	void EditorWidgetManager::onFocusGained(const RenderWindow& window)
 	void EditorWidgetManager::onFocusGained(const RenderWindow& window)
 	{
 	{
 		// Do nothing, possibly regain focus on last focused widget?
 		// Do nothing, possibly regain focus on last focused widget?

+ 2 - 2
BansheeEngine/Include/BsApplication.h

@@ -76,9 +76,9 @@ namespace BansheeEngine
 		virtual void onStartUp();
 		virtual void onStartUp();
 
 
 		/**
 		/**
-		 * @copydoc	CoreApplication::update.
+		 * @copydoc	CoreApplication::postUpdate.
 		 */
 		 */
-		virtual void update();
+		virtual void postUpdate() override;
 
 
 	private:
 	private:
 		/**
 		/**

+ 2 - 2
BansheeEngine/Source/BsApplication.cpp

@@ -100,9 +100,9 @@ namespace BansheeEngine
 		CoreApplication::startUp<Application>(primaryWindowDesc, renderSystem, renderer);
 		CoreApplication::startUp<Application>(primaryWindowDesc, renderSystem, renderer);
 	}
 	}
 
 
-	void Application::update()
+	void Application::postUpdate()
 	{
 	{
-		CoreApplication::update();
+		CoreApplication::postUpdate();
 
 
 		VirtualInput::instance()._update();
 		VirtualInput::instance()._update();
 		PROFILE_CALL(GUIManager::instance().update(), "GUI");
 		PROFILE_CALL(GUIManager::instance().update(), "GUI");

+ 3 - 3
MBansheeEditor/Scene/SceneWindow.cs

@@ -147,7 +147,7 @@ namespace BansheeEditor
             sceneViewHandler.Update();
             sceneViewHandler.Update();
 
 
             bool handleActive = false;
             bool handleActive = false;
-            if (Input.IsButtonUp(ButtonCode.MouseLeft))
+            if (Input.IsPointerButtonUp(PointerButton.Left))
             {
             {
                 if (sceneViewHandler.IsHandleActive())
                 if (sceneViewHandler.IsHandleActive())
                 {
                 {
@@ -162,11 +162,11 @@ namespace BansheeEditor
             Vector2I scenePos;
             Vector2I scenePos;
             if (ScreenToScenePos(Input.PointerPosition, out scenePos))
             if (ScreenToScenePos(Input.PointerPosition, out scenePos))
             {
             {
-                if (Input.IsButtonDown(ButtonCode.MouseLeft))
+                if (Input.IsPointerButtonDown(PointerButton.Left))
                 {
                 {
                     sceneViewHandler.TrySelectHandle(scenePos);
                     sceneViewHandler.TrySelectHandle(scenePos);
                 }
                 }
-                else if (Input.IsButtonUp(ButtonCode.MouseLeft))
+                else if (Input.IsPointerButtonUp(PointerButton.Left))
                 {
                 {
                     if (!handleActive)
                     if (!handleActive)
                     {
                     {

+ 61 - 6
TODO.txt

@@ -104,6 +104,10 @@ in focus. It receives focus properly next frame. It's possible mouse up event ha
 ISSUE #3 (needs verification):
 ISSUE #3 (needs verification):
 When clicking on an unfocused EditorWindow it seems that button up event for LMB (possibly other keys) rarely doesn't ever get triggered. This goes
 When clicking on an unfocused EditorWindow it seems that button up event for LMB (possibly other keys) rarely doesn't ever get triggered. This goes
 as deep as Input class, possibly a problem in OIS.
 as deep as Input class, possibly a problem in OIS.
+ - Potentially this happens if focus change is issued after button down event, but before button up event (only if button up happens on that frame, otherwise it does get recorded)
+  - If so, then this should be fixed once I move the input polling to core, but I need to pay special attention to this. Optionally I could solve it
+    by making sure I call Input::update() right before the context switch to make sure all current input has been received (there could still be a tiny % chance
+	something gets queued in between, but I don't think there's a way around that, nor that it is important enough)
 
 
 Minor issues:
 Minor issues:
  - When opening up Color picker it is screwed up for a frame or two
  - When opening up Color picker it is screwed up for a frame or two
@@ -126,9 +130,60 @@ Later:
  - Raycast snapping Ribek suggested
  - Raycast snapping Ribek suggested
 
 
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
-Scene View
+C# Material
+
+C# material interface:
+ - Pass
+ - GpuProgram
+ - DepthStencilState
+ - RasterizerState
+ - BlendState
+ - SamplerState
+ - Technique
+ - Shader
+ - Material
+
+--------
+
+Consider creating a shader effect language first?
+ - It needs support for multiple gpu programs (vertex, fragment, etc)
+ - Multiple passes, each with options to set up blend/rasterizer/depth-stencil states
+ - Actual GpuProgram code can be marked with HLSL, HLSL11, GLSL (and later BSL) to compile for only specific API
+ - Also needs to support techniques
+ - Also render queue, priority, renderer semantics
+ - Default parameter values?
+ - Parameter ranges?
+
+Then do I not need C# interface for anything but a Shader and Material. Shader doesn't need many (any?) properties and Material only needs property setters. Also potentially it also needs param block buffers and sampler states.
+
+While I'm at this I might also consider dealing with includes and shader combinations
+
+Questions:
+ - How to deal with sampler states? Allow default ones in shader?
+   - Yes, allow default ones. Plus allow sampler states to be tweaked from Material directly (without separate SamplerState class)
+ - PRoblem where DX9 requires sampler to have the same name as texture, but DX11 has them separate. For now go with DX11 approach
+   where you need to define the sampler separately, potentially allow "alias()" attribute so the system knows to look for other names.
+
+Check D:\ExampleBFX.txt for an example effects file
+
+Import:
+ - A special importer for BFX files. It generates a HShader as its output. I MIGHT want to disable separate GpuProgram and state saving so
+  I don't need to deal with multi-resources yet.
+
+How to deal with includes?
+ - You can do Include = "Path/to/CommonShader"
+   - Path is relative to executable
+ - CommonShader might not contain any techniques or passes, but instead just code.
+  - It can use special Include & Code {} tags that allow it to specify various code
+    for some language
+ - How does the runtime resolve those shaders?
+  - They are only resolved during import, after which just resource references are kept (i.e. its GUIDs)
+  - The runtime should then be able to resolve the GUIDs easily
+
+Use Bison to avoid the need to reference C# code from C++?
 
 
-IMPROVE SceneGrid LOOK - Use the shader created in Unity
+----------------------------------------------------------------------
+Scene View
 
 
 AFTER I have scene widget in C#:
 AFTER I have scene widget in C#:
  - Test custom handles from C#
  - Test custom handles from C#
@@ -141,10 +196,6 @@ Need a way to drag and drop items from Scene tree view to Scene view
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 Other
 Other
 
 
-Constant errors when rendering in DX11, and assert in DX9 when attempting to bind a texture to a shader
-Check in new grid and selection shaders
- - Test DX9 grid shader
-
 Got a crash on shutdown that was caused by locking a mutex in an Event destructor. Event was Platform::onMouseCaptureChanged. 
 Got a crash on shutdown that was caused by locking a mutex in an Event destructor. Event was Platform::onMouseCaptureChanged. 
 Issue happened when I closed the app via the X button (if that's relevant). It doesn't seem to happen always.
 Issue happened when I closed the app via the X button (if that's relevant). It doesn't seem to happen always.
 
 
@@ -163,6 +214,10 @@ Multi-resource saving
    - If it already exists in the manifest at a different location do it anyway, keep the other copy as-is in case user wanted it that way
    - If it already exists in the manifest at a different location do it anyway, keep the other copy as-is in case user wanted it that way
    - I'm not sure whether to do this for all Resource::save calls or only ones originating from ProjectLIbrary?
    - I'm not sure whether to do this for all Resource::save calls or only ones originating from ProjectLIbrary?
 
 
+/*********************************************************************/
+/************************ LESS IMPORTANT *****************************/
+/*********************************************************************/
+
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 Mono notes
 Mono notes