Prechádzať zdrojové kódy

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

Marko Pintera 10 rokov pred
rodič
commit
9699e887c4

+ 7 - 2
BansheeCore/Include/BsCoreApplication.h

@@ -88,9 +88,14 @@ namespace BansheeEngine
 
 	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:
 		/**

+ 40 - 1
BansheeCore/Include/BsInput.h

@@ -41,6 +41,27 @@ namespace BansheeEngine
 			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:
 		Input();
 		~Input();
@@ -94,12 +115,18 @@ namespace BansheeEngine
 		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.
 		 */
 		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
 		 *			the range for devices with unbound axes (e.g. mouse).
@@ -241,6 +268,18 @@ namespace BansheeEngine
 		bool mPointerDoubleClicked;
 		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		                      		*/
 		/************************************************************************/

+ 15 - 3
BansheeCore/Source/BsCoreApplication.cpp

@@ -177,9 +177,16 @@ namespace BansheeEngine
 
 			Platform::_update();
 			DeferredCallManager::instance()._update();
-			RenderWindowManager::instance()._update();
 			gTime().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");
 
@@ -191,7 +198,7 @@ namespace BansheeEngine
 			for (auto& pluginUpdateFunc : mPluginUpdateFunctions)
 				pluginUpdateFunc.second();
 
-			update();
+			postUpdate();
 
 			// Send out resource events in case any were loaded/destroyed/modified
 			ResourceListenerManager::instance().update();
@@ -240,7 +247,12 @@ namespace BansheeEngine
 		}
 	}
 
-	void CoreApplication::update()
+	void CoreApplication::preUpdate()
+	{
+		// Do nothing
+	}
+
+	void CoreApplication::postUpdate()
 	{
 		// Do nothing
 	}

+ 70 - 32
BansheeCore/Source/BsInput.cpp

@@ -99,6 +99,50 @@ namespace BansheeEngine
 			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)
 	{
 		if(mRawInputHandler != nullptr)
@@ -115,15 +159,13 @@ namespace BansheeEngine
 
 		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)
@@ -136,15 +178,13 @@ namespace BansheeEngine
 		else
 			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)
@@ -161,8 +201,8 @@ namespace BansheeEngine
 
 	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)
 			mPointerDelta = event.screenPos - mPointerPosition;
@@ -175,8 +215,8 @@ namespace BansheeEngine
 	{
 		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)
@@ -186,33 +226,31 @@ namespace BansheeEngine
 		else
 			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)
 	{
 		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)
 	{
-		if(!onInputCommand.empty())
-			onInputCommand(commandType);
+		mQueuedEvents.push_back(QueuedEvent(EventType::Command, (UINT32)mCommandEvents.size()));
+		mCommandEvents.push_back(commandType);
 	}
 
 	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

+ 10 - 8
BansheeCore/Source/BsRenderWindowManager.cpp

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

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

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

+ 2 - 1
BansheeEditor/Include/BsEditorApplication.h

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

+ 5 - 16
BansheeEditor/Include/BsEditorWidgetManager.h

@@ -17,6 +17,11 @@ namespace BansheeEngine
 		EditorWidgetManager();
 		~EditorWidgetManager();
 
+		/**
+		 * @brief	Called every frame.
+		 */
+		void update();
+
 		/**
 		 * @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.
@@ -78,21 +83,6 @@ namespace BansheeEngine
 		static void preRegisterWidget(const String& name, std::function<EditorWidgetBase*(EditorWidgetContainer&)> createCallback);
 
 	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.
 		 */
@@ -106,7 +96,6 @@ namespace BansheeEngine
 		Map<String, EditorWidgetBase*> mActiveWidgets;
 		Map<String, std::function<EditorWidgetBase*(EditorWidgetContainer&)>> mCreateCallbacks;
 
-		HEvent mOnPointerPressedConn;
 		HEvent mOnFocusLostConn;
 		HEvent mOnFocusGainedConn;
 

+ 9 - 2
BansheeEditor/Source/BsEditorApplication.cpp

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

+ 37 - 36
BansheeEditor/Source/BsEditorWidgetManager.cpp

@@ -29,14 +29,12 @@ namespace BansheeEngine
 			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));
 		mOnFocusGainedConn = RenderWindowManager::instance().onFocusGained.connect(std::bind(&EditorWidgetManager::onFocusGained, this, _1));
 	}
 
 	EditorWidgetManager::~EditorWidgetManager()
 	{
-		mOnPointerPressedConn.disconnect();
 		mOnFocusLostConn.disconnect();
 		mOnFocusGainedConn.disconnect();
 
@@ -46,6 +44,43 @@ namespace BansheeEngine
 			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)
 	{
 		auto iterFind = mCreateCallbacks.find(name);
@@ -234,40 +269,6 @@ namespace BansheeEngine
 			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)
 	{
 		// Do nothing, possibly regain focus on last focused widget?

+ 2 - 2
BansheeEngine/Include/BsApplication.h

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

+ 2 - 2
BansheeEngine/Source/BsApplication.cpp

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

+ 3 - 3
MBansheeEditor/Scene/SceneWindow.cs

@@ -147,7 +147,7 @@ namespace BansheeEditor
             sceneViewHandler.Update();
 
             bool handleActive = false;
-            if (Input.IsButtonUp(ButtonCode.MouseLeft))
+            if (Input.IsPointerButtonUp(PointerButton.Left))
             {
                 if (sceneViewHandler.IsHandleActive())
                 {
@@ -162,11 +162,11 @@ namespace BansheeEditor
             Vector2I scenePos;
             if (ScreenToScenePos(Input.PointerPosition, out scenePos))
             {
-                if (Input.IsButtonDown(ButtonCode.MouseLeft))
+                if (Input.IsPointerButtonDown(PointerButton.Left))
                 {
                     sceneViewHandler.TrySelectHandle(scenePos);
                 }
-                else if (Input.IsButtonUp(ButtonCode.MouseLeft))
+                else if (Input.IsPointerButtonUp(PointerButton.Left))
                 {
                     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):
 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.
+ - 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:
  - When opening up Color picker it is screwed up for a frame or two
@@ -126,9 +130,60 @@ Later:
  - 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#:
  - 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
 
-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. 
 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
    - I'm not sure whether to do this for all Resource::save calls or only ones originating from ProjectLIbrary?
 
+/*********************************************************************/
+/************************ LESS IMPORTANT *****************************/
+/*********************************************************************/
+
 ----------------------------------------------------------------------
 Mono notes