Pārlūkot izejas kodu

WIP: Linux port
- More work on X11 windowing

Marko Pintera 8 gadi atpakaļ
vecāks
revīzija
580aa20340

+ 4 - 4
Source/BansheeCore/CMakeSources.cmake

@@ -598,13 +598,13 @@ set(BS_BANSHEECORE_SRC_PLATFORM_WIN32
 )
 )
 
 
 set(BS_BANSHEECORE_INC_PLATFORM_UNIX
 set(BS_BANSHEECORE_INC_PLATFORM_UNIX
-	"Unix/BsUnixPlatform.h"
-	"Unix/BsUnixWindow.h"
+	"Linux/BsLinuxPlatform.h"
+	"Linux/BsLinuxWindow.h"
 )
 )
 
 
 set(BS_BANSHEECORE_SRC_PLATFORM_UNIX
 set(BS_BANSHEECORE_SRC_PLATFORM_UNIX
-	"Unix/BsUnixPlatform.cpp"
-	"Unix/BsUnixWindow.cpp"
+	"Linux/BsLinuxPlatform.cpp"
+	"Linux/BsLinuxWindow.cpp"
 )
 )
 
 
 if(WIN32)
 if(WIN32)

+ 267 - 111
Source/BansheeCore/Unix/BsUnixPlatform.cpp → Source/BansheeCore/Linux/BsLinuxPlatform.cpp

@@ -1,12 +1,13 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include <Image/BsPixelData.h>
-#include <Image/BsPixelUtil.h>
-#include <X11/Xutil.h>
+#include "Image/BsPixelData.h"
+#include "Image/BsPixelUtil.h"
+#include "String/BsUnicode.h"
+#include "Linux/BsLinuxPlatform.h"
+#include "Linux/BsLinuxWindow.h"
+#include "RenderAPI/BsRenderWindow.h"
 #include <X11/Xatom.h>
 #include <X11/Xatom.h>
-#include <String/BsUnicode.h>
-#include "BsUnixPlatform.h"
-#include "BsUnixWindow.h"
+#include <X11/Xcursor/Xcursor.h>
 
 
 namespace bs
 namespace bs
 {
 {
@@ -19,7 +20,7 @@ namespace bs
 	Event<void(UINT32)> Platform::onCharInput;
 	Event<void(UINT32)> Platform::onCharInput;
 
 
 	Event<void(ct::RenderWindow*)> Platform::onMouseLeftWindow;
 	Event<void(ct::RenderWindow*)> Platform::onMouseLeftWindow;
-	Event<void()> Platform::onMouseCaptureChanged
+	Event<void()> Platform::onMouseCaptureChanged;
 
 
 	enum class X11CursorType
 	enum class X11CursorType
 	{
 	{
@@ -47,6 +48,7 @@ namespace bs
 		::Window mainXWindow = 0;
 		::Window mainXWindow = 0;
 		::Window fullscreenXWindow = 0;
 		::Window fullscreenXWindow = 0;
 		std::unordered_map<::Window, LinuxWindow*> windowMap;
 		std::unordered_map<::Window, LinuxWindow*> windowMap;
+		Mutex lock;
 
 
 		XIM IM;
 		XIM IM;
 		XIC IC;
 		XIC IC;
@@ -69,42 +71,39 @@ namespace bs
 
 
 	static const UINT32 DOUBLE_CLICK_MS = 500;
 	static const UINT32 DOUBLE_CLICK_MS = 500;
 
 
-	Platform::Pimpl* Platform::mData = bs_new<Platform::Pimpl>();
-
-	Platform::~Platform() { }
-
-	Vector2I Platform::getCursorPosition()
+	Vector2I _getCursorPosition(Platform::Pimpl* data)
 	{
 	{
-		UINT32 screenCount = XScreenCount(mData->xDisplay);
-
 		Vector2I pos;
 		Vector2I pos;
-		for(UINT32 i = 0; i < screenCount; ++i)
+		UINT32 screenCount = (UINT32)XScreenCount(data->xDisplay);
+
+		for (UINT32 i = 0; i < screenCount; ++i)
 		{
 		{
 			::Window outRoot, outChild;
 			::Window outRoot, outChild;
 			INT32 childX, childY;
 			INT32 childX, childY;
 			UINT32 mask;
 			UINT32 mask;
-			XQueryPointer(mData->xDisplay, XRootWindow(mData->xDisplay, i), &outRoot, &outChild, &pos.x,
-					&pos.y, &childX, &childY, &mask);
+			if(XQueryPointer(data->xDisplay, XRootWindow(data->xDisplay, i), &outRoot, &outChild, &pos.x,
+					&pos.y, &childX, &childY, &mask))
+				break;
 		}
 		}
 
 
 		return pos;
 		return pos;
 	}
 	}
 
 
-	void Platform::setCursorPosition(const Vector2I& screenPos)
+	void _setCursorPosition(Platform::Pimpl* data, const Vector2I& screenPos)
 	{
 	{
-		UINT32 screenCount = XScreenCount(mData->xDisplay);
+		UINT32 screenCount = (UINT32)XScreenCount(data->xDisplay);
 
 
 		// Note assuming screens are laid out horizontally left to right
 		// Note assuming screens are laid out horizontally left to right
 		INT32 screenX = 0;
 		INT32 screenX = 0;
 		for(UINT32 i = 0; i < screenCount; ++i)
 		for(UINT32 i = 0; i < screenCount; ++i)
 		{
 		{
-			::Window root = XRootWindow(mData->xDisplay, i);
-			INT32 screenXEnd = screenX + XDisplayWidth(mData->xDisplay, i);
+			::Window root = XRootWindow(data->xDisplay, i);
+			INT32 screenXEnd = screenX + XDisplayWidth(data->xDisplay, i);
 
 
 			if(screenPos.x >= screenX && screenPos.x < screenXEnd)
 			if(screenPos.x >= screenX && screenPos.x < screenXEnd)
 			{
 			{
-				XWarpPointer(mData->xDisplay, None, root, 0, 0, 0, 0, screenPos.x, screenPos.y);
-				XFlush(mData->xDisplay);
+				XWarpPointer(data->xDisplay, None, root, 0, 0, 0, 0, screenPos.x, screenPos.y);
+				XFlush(data->xDisplay);
 				return;
 				return;
 			}
 			}
 
 
@@ -112,22 +111,6 @@ namespace bs
 		}
 		}
 	}
 	}
 
 
-	void Platform::captureMouse(const RenderWindow& window)
-	{
-		// TODOPORT
-	}
-
-	void Platform::releaseMouseCapture()
-	{
-		// TODOPORT
-	}
-
-	bool Platform::isPointOverWindow(const RenderWindow& window, const Vector2I& screenPos)
-	{
-		// TODOPORT
-		return false;
-	}
-
 	void applyCurrentCursor(Platform::Pimpl* data, ::Window window)
 	void applyCurrentCursor(Platform::Pimpl* data, ::Window window)
 	{
 	{
 		if(data->isCursorHidden)
 		if(data->isCursorHidden)
@@ -185,8 +168,84 @@ namespace bs
 		return false;
 		return false;
 	}
 	}
 
 
+	void setCurrentCursor(Platform::Pimpl* data, ::Cursor cursor)
+	{
+		if(data->currentCursor)
+			XFreeCursor(data->xDisplay, data->currentCursor);
+
+		data->currentCursor = cursor;
+		for(auto& entry : data->windowMap)
+			applyCurrentCursor(data, entry.first);
+	}
+
+	Platform::Pimpl* Platform::mData = bs_new<Platform::Pimpl>();
+
+	Platform::~Platform()
+	{ }
+
+	Vector2I Platform::getCursorPosition()
+	{
+		Lock lock(mData->lock);
+		return _getCursorPosition(mData);
+	}
+
+	void Platform::setCursorPosition(const Vector2I& screenPos)
+	{
+		Lock lock(mData->lock);
+
+		_setCursorPosition(mData, screenPos);
+	}
+
+	void Platform::captureMouse(const RenderWindow& window)
+	{
+		Lock lock(mData->lock);
+
+		LinuxWindow* linuxWindow;
+		window.getCustomAttribute("WINDOW", &linuxWindow);
+
+		uint32_t mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask;
+		XGrabPointer(mData->xDisplay, linuxWindow->_getXWindow(), False, mask, GrabModeAsync,
+				GrabModeAsync, None, None, CurrentTime);
+		XSync(mData->xDisplay, False);
+	}
+
+	void Platform::releaseMouseCapture()
+	{
+		Lock lock(mData->lock);
+
+		XUngrabPointer(mData->xDisplay, CurrentTime);
+		XSync(mData->xDisplay, False);
+	}
+
+	bool Platform::isPointOverWindow(const RenderWindow& window, const Vector2I& screenPos)
+	{
+		Lock lock(mData->lock);
+
+		LinuxWindow* linuxWindow;
+		window.getCustomAttribute("WINDOW", &linuxWindow);
+		::Window xWindow = linuxWindow->_getXWindow();
+
+		Vector2I pos;
+		UINT32 screenCount = (UINT32)XScreenCount(mData->xDisplay);
+
+		for (UINT32 i = 0; i < screenCount; ++i)
+		{
+			::Window outRoot, outChild;
+			INT32 childX, childY;
+			UINT32 mask;
+			if(XQueryPointer(mData->xDisplay, XRootWindow(mData->xDisplay, i), &outRoot, &outChild, &pos.x,
+					&pos.y, &childX, &childY, &mask))
+			{
+				return outChild == xWindow;
+			}
+		}
+
+		return false;
+	}
+
 	void Platform::hideCursor()
 	void Platform::hideCursor()
 	{
 	{
+		Lock lock(mData->lock);
 		mData->isCursorHidden = true;
 		mData->isCursorHidden = true;
 
 
 		for(auto& entry : mData->windowMap)
 		for(auto& entry : mData->windowMap)
@@ -195,6 +254,7 @@ namespace bs
 
 
 	void Platform::showCursor()
 	void Platform::showCursor()
 	{
 	{
+		Lock lock(mData->lock);
 		mData->isCursorHidden = false;
 		mData->isCursorHidden = false;
 
 
 		for(auto& entry : mData->windowMap)
 		for(auto& entry : mData->windowMap)
@@ -203,43 +263,68 @@ namespace bs
 
 
 	bool Platform::isCursorHidden()
 	bool Platform::isCursorHidden()
 	{
 	{
+		Lock lock(mData->lock);
 		return mData->isCursorHidden;
 		return mData->isCursorHidden;
 	}
 	}
 
 
 	void Platform::clipCursorToWindow(const RenderWindow& window)
 	void Platform::clipCursorToWindow(const RenderWindow& window)
 	{
 	{
+		Lock lock(mData->lock);
+
+		LinuxWindow* linuxWindow;
+		window.getCustomAttribute("WINDOW", &linuxWindow);
+
 		mData->cursorClipEnabled = true;
 		mData->cursorClipEnabled = true;
-		mData->cursorClipWindow = window->_getInternal();
+		mData->cursorClipWindow = linuxWindow;
 
 
-		updateClipBounds(window);
+		updateClipBounds(mData, linuxWindow);
 
 
-		Vector2I pos = getCursorPosition();
+		Vector2I pos = _getCursorPosition(mData);
 
 
 		if(clipCursor(mData, pos))
 		if(clipCursor(mData, pos))
-			setCursorPosition(pos);
+			_setCursorPosition(mData, pos);
 	}
 	}
 
 
 	void Platform::clipCursorToRect(const Rect2I& screenRect)
 	void Platform::clipCursorToRect(const Rect2I& screenRect)
 	{
 	{
+		Lock lock(mData->lock);
+
 		mData->cursorClipEnabled = true;
 		mData->cursorClipEnabled = true;
 		mData->cursorClipRect = screenRect;
 		mData->cursorClipRect = screenRect;
 		mData->cursorClipWindow = nullptr;
 		mData->cursorClipWindow = nullptr;
 
 
-		Vector2I pos = getCursorPosition();
+		Vector2I pos = _getCursorPosition(mData);
 
 
 		if(clipCursor(mData, pos))
 		if(clipCursor(mData, pos))
-			setCursorPosition(pos);
+			_setCursorPosition(mData, pos);
 	}
 	}
 
 
 	void Platform::clipCursorDisable()
 	void Platform::clipCursorDisable()
 	{
 	{
+		Lock lock(mData->lock);
+
 		mData->cursorClipEnabled = false;
 		mData->cursorClipEnabled = false;
 		mData->cursorClipWindow = None;
 		mData->cursorClipWindow = None;
 	}
 	}
 
 
 	void Platform::setCursor(PixelData& pixelData, const Vector2I& hotSpot)
 	void Platform::setCursor(PixelData& pixelData, const Vector2I& hotSpot)
 	{
 	{
-		// TODOPORT
+		SPtr<PixelData> bgraData = PixelData::create(pixelData.getWidth(), pixelData.getHeight(), 1, PF_BGRA8);
+		PixelUtil::bulkPixelConversion(pixelData, *bgraData);
+
+		Lock lock(mData->lock);
+
+		XcursorImage* image = XcursorImageCreate((int)bgraData->getWidth(), (int)bgraData->getHeight());
+		image->xhot = hotSpot.x;
+		image->yhot = hotSpot.y;
+		image->delay = 0;
+
+		memcpy(image->pixels, bgraData->getData(), bgraData->getSize());
+
+		::Cursor cursor = XcursorImageLoadCursor(mData->xDisplay, image);
+		XcursorImageDestroy(image);
+
+		setCurrentCursor(mData, cursor);
 	}
 	}
 
 
 	void Platform::setIcon(const PixelData& pixelData)
 	void Platform::setIcon(const PixelData& pixelData)
@@ -247,12 +332,31 @@ namespace bs
 		SPtr<PixelData> resizedData = PixelData::create(32, 32, 1, PF_RGBA8);
 		SPtr<PixelData> resizedData = PixelData::create(32, 32, 1, PF_RGBA8);
 		PixelUtil::scale(pixelData, *resizedData);
 		PixelUtil::scale(pixelData, *resizedData);
 
 
-		// TODOPORT
+		if(!mData->mainXWindow)
+			return;
+
+		auto iterFind = mData->windowMap.find(mData->mainXWindow);
+		if(iterFind == mData->windowMap.end())
+			return;
+
+		LinuxWindow* mainLinuxWindow = iterFind->second;
+
+		Lock lock(mData->lock);
+		mainLinuxWindow->setIcon(pixelData);
 	}
 	}
 
 
 	void Platform::setCaptionNonClientAreas(const ct::RenderWindow& window, const Vector<Rect2I>& nonClientAreas)
 	void Platform::setCaptionNonClientAreas(const ct::RenderWindow& window, const Vector<Rect2I>& nonClientAreas)
 	{
 	{
-		// TODOPORT
+		if(nonClientAreas.size() == 0)
+			return;
+
+		Lock lock(mData->lock);
+
+		LinuxWindow* linuxWindow;
+		window.getCustomAttribute("WINDOW", &linuxWindow);
+
+		// Note: Only supporting a single area
+		linuxWindow->_setDragZone(nonClientAreas[0]);
 	}
 	}
 
 
 	void Platform::setResizeNonClientAreas(const ct::RenderWindow& window, const Vector<NonClientResizeArea>& nonClientAreas)
 	void Platform::setResizeNonClientAreas(const ct::RenderWindow& window, const Vector<NonClientResizeArea>& nonClientAreas)
@@ -272,16 +376,19 @@ namespace bs
 
 
 	OSDropTarget& Platform::createDropTarget(const RenderWindow* window, int x, int y, unsigned int width, unsigned int height)
 	OSDropTarget& Platform::createDropTarget(const RenderWindow* window, int x, int y, unsigned int width, unsigned int height)
 	{
 	{
+		Lock lock(mData->lock);
 		// TODOPORT
 		// TODOPORT
 	}
 	}
 
 
 	void Platform::destroyDropTarget(OSDropTarget& target)
 	void Platform::destroyDropTarget(OSDropTarget& target)
 	{
 	{
+		Lock lock(mData->lock);
 		// TODOPORT
 		// TODOPORT
 	}
 	}
 
 
 	void Platform::copyToClipboard(const WString& string)
 	void Platform::copyToClipboard(const WString& string)
 	{
 	{
+		Lock lock(mData->lock);
 		mData->clipboardData = string;
 		mData->clipboardData = string;
 
 
 		Atom clipboardAtom = XInternAtom(mData->xDisplay, "CLIPBOARD", 0);
 		Atom clipboardAtom = XInternAtom(mData->xDisplay, "CLIPBOARD", 0);
@@ -290,6 +397,7 @@ namespace bs
 
 
 	WString Platform::copyFromClipboard()
 	WString Platform::copyFromClipboard()
 	{
 	{
+		Lock lock(mData->lock);
 		Atom clipboardAtom = XInternAtom(mData->xDisplay, "CLIPBOARD", 0);
 		Atom clipboardAtom = XInternAtom(mData->xDisplay, "CLIPBOARD", 0);
 		::Window selOwner = XGetSelectionOwner(mData->xDisplay, clipboardAtom);
 		::Window selOwner = XGetSelectionOwner(mData->xDisplay, clipboardAtom);
 
 
@@ -340,14 +448,19 @@ namespace bs
 
 
 	WString Platform::keyCodeToUnicode(UINT32 keyCode)
 	WString Platform::keyCodeToUnicode(UINT32 keyCode)
 	{
 	{
+		Lock lock(mData->lock);
 		// TODOPORT
 		// TODOPORT
 		return L"";
 		return L"";
 	}
 	}
 
 
 	void Platform::_messagePump()
 	void Platform::_messagePump()
 	{
 	{
-		while(XPending(mData->xDisplay) > 0)
+		while(true)
 		{
 		{
+			Lock lock(mData->lock);
+			if(XPending(mData->xDisplay) <= 0)
+				break;
+
 			XEvent event;
 			XEvent event;
 			XNextEvent(mData->xDisplay, &event);
 			XNextEvent(mData->xDisplay, &event);
 
 
@@ -379,17 +492,18 @@ namespace bs
 				if(!XFilterEvent(&event, None))
 				if(!XFilterEvent(&event, None))
 				{
 				{
 					Status status;
 					Status status;
-					uint8_t buffer[16];
+					char buffer[16];
 
 
-					int32_t length = Xutf8LookupString(mData->IC, &event.xkey, (char*)buffer, sizeof(buffer), nullptr,
+					INT32 length = Xutf8LookupString(mData->IC, &event.xkey, buffer, sizeof(buffer), nullptr,
 							&status);
 							&status);
 
 
 					if(length > 0)
 					if(length > 0)
 					{
 					{
 						buffer[length] = '\0';
 						buffer[length] = '\0';
 
 
-						// TODOPORT - Buffer now contains the UTF8 value of length 'length' bytes. I can consider converting
-						// it to single UTF32 before returning
+						U32String utfStr = UTF8::toUTF32(String(buffer));
+						if(utfStr.length() > 0)
+							onCharInput((UINT32)utfStr[0]);
 					}
 					}
 				}
 				}
 
 
@@ -423,34 +537,57 @@ namespace bs
 				break;
 				break;
 			case ButtonPress:
 			case ButtonPress:
 			{
 			{
-				uint32_t button = event.xbutton.button;
+				UINT32 button = event.xbutton.button;
+
+				OSMouseButton mouseButton;
+				bool validPress = false;
 				switch(button)
 				switch(button)
 				{
 				{
-					// Button 4 & 5 is vertical wheel, 6 & 7 horizontal wheel, and we handle them elsewhere
-				case Button1: // Left
-				case Button2: // Middle
-				case Button3: // Right
-				case 8:
-				case 9:
-					int32_t x = event.xbutton.x_root;
-					int32_t y = event.xbutton.y_root;
-
-					bool alt = (event.xbutton.state & Mod1Mask) != 0;
-					bool control = (event.xbutton.state & ControlMask) != 0;
-					bool shift = (event.xbutton.state & ShiftMask) != 0;
-
-					// TODOPORT - Report button press
+				case Button1:
+					mouseButton = OSMouseButton::Left;
+					validPress = true;
+					break;
+				case Button2:
+					mouseButton = OSMouseButton::Middle;
+					validPress = true;
+					break;
+				case Button3:
+					mouseButton = OSMouseButton::Right;
+					validPress = true;
+					break;
+
+				default:
 					break;
 					break;
 				}
 				}
 
 
-				// Handle double-click
-				if(event.xbutton.time < (mData->lastButtonPressTime + DOUBLE_CLICK_MS))
+				if(validPress)
 				{
 				{
-					// TODOPORT - Trigger double-click
-					mData->lastButtonPressTime = 0;
+					// Send event
+					Vector2I pos;
+					pos.x = event.xbutton.x_root;
+					pos.y = event.xbutton.y_root;
+
+					OSPointerButtonStates btnStates;
+					btnStates.ctrl = (event.xbutton.state & ControlMask) != 0;
+					btnStates.shift = (event.xbutton.state & ShiftMask) != 0;
+					btnStates.mouseButtons[0] = (event.xbutton.state & Button1Mask) != 0;
+					btnStates.mouseButtons[1] = (event.xbutton.state & Button2Mask) != 0;
+					btnStates.mouseButtons[2] = (event.xbutton.state & Button3Mask) != 0;
+
+					onCursorButtonPressed(pos, mouseButton, btnStates);
+
+					// Handle double-click
+					if(button == Button1)
+					{
+						if (event.xbutton.time < (mData->lastButtonPressTime + DOUBLE_CLICK_MS))
+						{
+							onCursorDoubleClick(pos, btnStates);
+							mData->lastButtonPressTime = 0;
+						}
+						else
+							mData->lastButtonPressTime = event.xbutton.time;
+					}
 				}
 				}
-				else
-					mData->lastButtonPressTime = event.xbutton.time;
 
 
 				// Handle window dragging for windows without a title bar
 				// Handle window dragging for windows without a title bar
 				if(button == Button1)
 				if(button == Button1)
@@ -467,39 +604,38 @@ namespace bs
 			}
 			}
 			case ButtonRelease:
 			case ButtonRelease:
 			{
 			{
-				uint32_t button = event.xbutton.button;
-				bool alt = (event.xbutton.state & Mod1Mask) != 0;
-				bool control = (event.xbutton.state & ControlMask) != 0;
-				bool shift = (event.xbutton.state & ShiftMask) != 0;
+				UINT32 button = event.xbutton.button;
+
+				Vector2I pos;
+				pos.x = event.xbutton.x_root;
+				pos.y = event.xbutton.y_root;
+
+				OSPointerButtonStates btnStates;
+				btnStates.ctrl = (event.xbutton.state & ControlMask) != 0;
+				btnStates.shift = (event.xbutton.state & ShiftMask) != 0;
+				btnStates.mouseButtons[0] = (event.xbutton.state & Button1Mask) != 0;
+				btnStates.mouseButtons[1] = (event.xbutton.state & Button2Mask) != 0;
+				btnStates.mouseButtons[2] = (event.xbutton.state & Button3Mask) != 0;
 
 
 				switch(button)
 				switch(button)
 				{
 				{
-				case Button2: // Middle
-				case Button3: // Right
-				case 8:
-				case 9:
-				{
-					int32_t x = event.xbutton.x_root;
-					int32_t y = event.xbutton.y_root;
-
-					// TODOPORT - Report button release
+				case Button1:
+					onCursorButtonReleased(pos, OSMouseButton::Left, btnStates);
+					break;
+				case Button2:
+					onCursorButtonReleased(pos, OSMouseButton::Middle, btnStates);
+					break;
+				case Button3:
+					onCursorButtonReleased(pos, OSMouseButton::Right, btnStates);
 					break;
 					break;
-				}
 				case Button4: // Vertical mouse wheel
 				case Button4: // Vertical mouse wheel
 				case Button5:
 				case Button5:
 				{
 				{
-					int32_t delta = button == Button4 ? 1 : -1;
-
-					// TODOPORT - Send mouse wheel scroll
+					INT32 delta = button == Button4 ? 1 : -1;
+					onMouseWheelScrolled((float)delta);
 				}
 				}
 					break;
 					break;
-				case 6: // Horizontal mouse wheel
-				case 7:
-				{
-					int32_t delta = button == 6 ? 1 : -1;
-
-					// TODOPORT - Send mouse wheel scroll
-				}
+				default:
 					break;
 					break;
 				}
 				}
 
 
@@ -524,9 +660,17 @@ namespace bs
 
 
 				// Handle clipping if enabled
 				// Handle clipping if enabled
 				if(clipCursor(mData, pos))
 				if(clipCursor(mData, pos))
-					setCursorPosition(pos);
+					_setCursorPosition(mData, pos);
+
+				// Send event
+				OSPointerButtonStates btnStates;
+				btnStates.ctrl = (event.xmotion.state & ControlMask) != 0;
+				btnStates.shift = (event.xmotion.state & ShiftMask) != 0;
+				btnStates.mouseButtons[0] = (event.xmotion.state & Button1Mask) != 0;
+				btnStates.mouseButtons[1] = (event.xmotion.state & Button2Mask) != 0;
+				btnStates.mouseButtons[2] = (event.xmotion.state & Button3Mask) != 0;
 
 
-				// TODOPORT - Send mouse move event
+				onCursorMoved(pos, btnStates);
 
 
 				// Handle window dragging for windows without a title bar
 				// Handle window dragging for windows without a title bar
 				auto iterFind = mData->windowMap.find(event.xmotion.window);
 				auto iterFind = mData->windowMap.find(event.xmotion.window);
@@ -551,7 +695,7 @@ namespace bs
 					pos.y = event.xcrossing.y_root;
 					pos.y = event.xcrossing.y_root;
 
 
 					if(clipCursor(mData, pos))
 					if(clipCursor(mData, pos))
-						setCursorPosition(pos);
+						_setCursorPosition(mData, pos);
 
 
 					// TODOPORT - Send mouse leave event
 					// TODOPORT - Send mouse leave event
 				}
 				}
@@ -591,8 +735,8 @@ namespace bs
 				{
 				{
 					String utf8data = UTF8::fromWide(mData->clipboardData);
 					String utf8data = UTF8::fromWide(mData->clipboardData);
 
 
-					const uint8_t* data = (const uint8_t*)utf8data.c_str();
-					int32_t dataLength = utf8data.length();
+					const UINT8* data = (const UINT8*)utf8data.c_str();
+					INT32 dataLength = (INT32)utf8data.length();
 
 
 					XChangeProperty(mData->xDisplay, selReq.requestor, selReq.property,
 					XChangeProperty(mData->xDisplay, selReq.requestor, selReq.property,
 							selReq.target, 8, PropModeReplace, data, dataLength);
 							selReq.target, 8, PropModeReplace, data, dataLength);
@@ -633,7 +777,7 @@ namespace bs
 
 
 	void Platform::_startUp()
 	void Platform::_startUp()
 	{
 	{
-		// XInitThreads(); // TODO: Not sure if this will be necessary
+		Lock lock(mData->lock);
 		mData->xDisplay = XOpenDisplay(nullptr);
 		mData->xDisplay = XOpenDisplay(nullptr);
 
 
 		if(XSupportsLocale())
 		if(XSupportsLocale())
@@ -673,6 +817,8 @@ namespace bs
 
 
 	void Platform::_shutDown()
 	void Platform::_shutDown()
 	{
 	{
+		Lock lock(mData->lock);
+
 		// Free empty cursor
 		// Free empty cursor
 		XFreeCursor(mData->xDisplay, mData->emptyCursor);
 		XFreeCursor(mData->xDisplay, mData->emptyCursor);
 		mData->emptyCursor = None;
 		mData->emptyCursor = None;
@@ -706,6 +852,16 @@ namespace bs
 		return mData->mainXWindow;
 		return mData->mainXWindow;
 	}
 	}
 
 
+	void LinuxPlatform::lockX()
+	{
+		mData->lock.lock();
+	}
+
+	void LinuxPlatform::unlockX()
+	{
+		mData->lock.unlock();
+	}
+
 	void LinuxPlatform::_registerWindow(::Window xWindow, LinuxWindow* window)
 	void LinuxPlatform::_registerWindow(::Window xWindow, LinuxWindow* window)
 	{
 	{
 		// First window is assumed to be the main
 		// First window is assumed to be the main
@@ -740,21 +896,21 @@ namespace bs
 			mData->mainXWindow = 0;
 			mData->mainXWindow = 0;
 	}
 	}
 
 
-	Pixmap LinuxPlatform::createPixmap(const SPtr<PixelData>& data)
+	Pixmap LinuxPlatform::createPixmap(const PixelData& data)
 	{
 	{
-		SPtr<PixelData> bgraData = PixelData::create(data->getWidth(), data->getHeight(), 1, PF_BGRA8);
-		PixelUtil::bulkPixelConversion(*data, *bgraData);
+		SPtr<PixelData> bgraData = PixelData::create(data.getWidth(), data.getHeight(), 1, PF_BGRA8);
+		PixelUtil::bulkPixelConversion(data, *bgraData);
 
 
-		uint32_t depth = XDefaultDepth(mData->xDisplay, 0);
+		UINT32 depth = (UINT32)XDefaultDepth(mData->xDisplay, 0);
 		XImage* image = XCreateImage(mData->xDisplay, XDefaultVisual(mData->xDisplay, 0), depth, ZPixmap, 0,
 		XImage* image = XCreateImage(mData->xDisplay, XDefaultVisual(mData->xDisplay, 0), depth, ZPixmap, 0,
-				(char*)bgraData->getData(), data->getWidth(), data->getHeight(), 32, 0);
+				(char*)bgraData->getData(), data.getWidth(), data.getHeight(), 32, 0);
 
 
 		Pixmap pixmap = XCreatePixmap(mData->xDisplay, XDefaultRootWindow(mData->xDisplay),
 		Pixmap pixmap = XCreatePixmap(mData->xDisplay, XDefaultRootWindow(mData->xDisplay),
-				data->getWidth(), data->getHeight(), depth);
+				data.getWidth(), data.getHeight(), depth);
 
 
 		XGCValues gcValues;
 		XGCValues gcValues;
 		GC gc = XCreateGC(mData->xDisplay, pixmap, 0, &gcValues);
 		GC gc = XCreateGC(mData->xDisplay, pixmap, 0, &gcValues);
-		XPutImage(mData->xDisplay, pixmap, gc, image, 0, 0, 0, 0, data->getWidth(), data->getHeight());
+		XPutImage(mData->xDisplay, pixmap, gc, image, 0, 0, 0, 0, data.getWidth(), data.getHeight());
 		XFreeGC(mData->xDisplay, gc);
 		XFreeGC(mData->xDisplay, gc);
 
 
 		// Make sure XDestroyImage doesn't free the data pointed to by 'data.bytes'
 		// Make sure XDestroyImage doesn't free the data pointed to by 'data.bytes'

+ 7 - 1
Source/BansheeCore/Unix/BsUnixPlatform.h → Source/BansheeCore/Linux/BsLinuxPlatform.h

@@ -26,6 +26,12 @@ namespace bs
 		/** Returns the main X11 window. Caller must ensure the main window has been created. */
 		/** Returns the main X11 window. Caller must ensure the main window has been created. */
 		static ::Window getMainXWindow();
 		static ::Window getMainXWindow();
 
 
+		/** Locks access to the X11 system, not allowing any other thread to access it. Must be used for every X11 access. */
+		static void lockX();
+
+		/** Unlocks access to the X11 system. Must follow every call to lockX(). */
+		static void unlockX();
+
 		/** Notifies the system that a new window was created. */
 		/** Notifies the system that a new window was created. */
 		static void _registerWindow(::Window xWindow, LinuxWindow* window);
 		static void _registerWindow(::Window xWindow, LinuxWindow* window);
 
 
@@ -33,7 +39,7 @@ namespace bs
 		static void _unregisterWindow(::Window xWindow);
 		static void _unregisterWindow(::Window xWindow);
 
 
 		/** Generates a X11 Pixmap from the provided pixel data. */
 		/** Generates a X11 Pixmap from the provided pixel data. */
-		static Pixmap createPixmap(const SPtr<PixelData>& data);
+		static Pixmap createPixmap(const PixelData& data);
 	};
 	};
 
 
 	/** @} */
 	/** @} */

+ 16 - 11
Source/BansheeCore/Unix/BsUnixWindow.cpp → Source/BansheeCore/Linux/BsLinuxWindow.cpp

@@ -1,7 +1,7 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsUnixWindow.h"
-#include "BsUnixPlatform.h"
+#include "BsLinuxWindow.h"
+#include "BsLinuxPlatform.h"
 #include "Math/BsRect2I.h"
 #include "Math/BsRect2I.h"
 
 
 #include <X11/Xlib.h>
 #include <X11/Xlib.h>
@@ -37,36 +37,36 @@ namespace bs
 		WindowState state = WindowState::Normal;
 		WindowState state = WindowState::Normal;
 
 
 		Rect2I dragZone;
 		Rect2I dragZone;
-		int32_t dragStartX, dragStartY;
+		INT32 dragStartX, dragStartY;
 	};
 	};
 
 
 	LinuxWindow::LinuxWindow(const WINDOW_DESC &desc)
 	LinuxWindow::LinuxWindow(const WINDOW_DESC &desc)
 	{
 	{
 		::Display* display = LinuxPlatform::getXDisplay();
 		::Display* display = LinuxPlatform::getXDisplay();
 
 
-		int screen;
+		INT32 screen;
 		if(desc.screen == (UINT32)-1)
 		if(desc.screen == (UINT32)-1)
 			screen = XDefaultScreen(display);
 			screen = XDefaultScreen(display);
 		else
 		else
-			screen = std::min((int)desc.screen, XScreenCount(display));
+			screen = std::min((INT32)desc.screen, XScreenCount(display));
 
 
 		XSetWindowAttributes attributes;
 		XSetWindowAttributes attributes;
 		attributes.background_pixel = XWhitePixel(display, screen);
 		attributes.background_pixel = XWhitePixel(display, screen);
 		attributes.border_pixel = XBlackPixel(display, screen);
 		attributes.border_pixel = XBlackPixel(display, screen);
 
 
 		attributes.colormap = XCreateColormap(display,
 		attributes.colormap = XCreateColormap(display,
-				XRootWindow(display, desc.visualInfo.screen),
+				XRootWindow(display, screen),
 				desc.visualInfo.visual,
 				desc.visualInfo.visual,
 				AllocNone);
 				AllocNone);
 
 
-		uint32_t borderWidth = 0;
+		UINT32 borderWidth = 0;
 
 
 		m->x = desc.x;
 		m->x = desc.x;
 		m->y = desc.y;
 		m->y = desc.y;
 		m->width = desc.width;
 		m->width = desc.width;
 		m->height = desc.height;
 		m->height = desc.height;
 		m->xWindow = XCreateWindow(display,
 		m->xWindow = XCreateWindow(display,
-				XRootWindow(display, desc.visualInfo.screen),
+				XRootWindow(display, screen),
 				desc.x, desc.y,
 				desc.x, desc.y,
 				desc.width, desc.height,
 				desc.width, desc.height,
 				borderWidth, desc.visualInfo.depth,
 				borderWidth, desc.visualInfo.depth,
@@ -233,7 +233,7 @@ namespace bs
 		XWindowAttributes xwa;
 		XWindowAttributes xwa;
 		XGetWindowAttributes(LinuxPlatform::getXDisplay(), m->xWindow, &xwa);
 		XGetWindowAttributes(LinuxPlatform::getXDisplay(), m->xWindow, &xwa);
 
 
-		return xwa.width;
+		return (UINT32)xwa.width;
 	}
 	}
 
 
 	UINT32 LinuxWindow::getHeight() const
 	UINT32 LinuxWindow::getHeight() const
@@ -241,7 +241,7 @@ namespace bs
 		XWindowAttributes xwa;
 		XWindowAttributes xwa;
 		XGetWindowAttributes(LinuxPlatform::getXDisplay(), m->xWindow, &xwa);
 		XGetWindowAttributes(LinuxPlatform::getXDisplay(), m->xWindow, &xwa);
 
 
-		return xwa.height;
+		return (UINT32)xwa.height;
 	}
 	}
 
 
 	Vector2I LinuxWindow::windowToScreenPos(const Vector2I& windowPos) const
 	Vector2I LinuxWindow::windowToScreenPos(const Vector2I& windowPos) const
@@ -266,7 +266,7 @@ namespace bs
 		return windowPos;
 		return windowPos;
 	}
 	}
 
 
-	void LinuxWindow::setIcon(const SPtr<PixelData>& data)
+	void LinuxWindow::setIcon(const PixelData& data)
 	{
 	{
 		Pixmap iconPixmap = LinuxPlatform::createPixmap(data);
 		Pixmap iconPixmap = LinuxPlatform::createPixmap(data);
 
 
@@ -287,6 +287,11 @@ namespace bs
 		m->xWindow = 0;
 		m->xWindow = 0;
 	}
 	}
 
 
+	void LinuxWindow::_setDragZone(const Rect2I& rect)
+	{
+		m->dragZone = rect;
+	}
+
 	bool LinuxWindow::_dragStart(int32_t x, int32_t y)
 	bool LinuxWindow::_dragStart(int32_t x, int32_t y)
 	{
 	{
 		if(m->hasTitleBar)
 		if(m->hasTitleBar)

+ 5 - 2
Source/BansheeCore/Unix/BsUnixWindow.h → Source/BansheeCore/Linux/BsLinuxWindow.h

@@ -32,7 +32,10 @@ namespace bs
 		SPtr<PixelData> background;
 		SPtr<PixelData> background;
 	};
 	};
 
 
-	/**	Represents a X11 window. */
+	/**
+	 * Represents a X11 window. Note that all accesses (including creation and destruction) to objects of this class must
+	 * be locked by the main X11 lock accessible through LinuxPlatform.
+	 */
 	class BS_UTILITY_EXPORT LinuxWindow
 	class BS_UTILITY_EXPORT LinuxWindow
 	{
 	{
 	public:
 	public:
@@ -76,7 +79,7 @@ namespace bs
 		void move(INT32 left, INT32 top);
 		void move(INT32 left, INT32 top);
 
 
 		/** Sets the icon to display for the window. */
 		/** Sets the icon to display for the window. */
-		void setIcon(const SPtr<PixelData>& icon);
+		void setIcon(const PixelData& icon);
 
 
 		/**	Converts screen position into window local position. */
 		/**	Converts screen position into window local position. */
 		Vector2I screenToWindowPos(const Vector2I& screenPos) const;
 		Vector2I screenToWindowPos(const Vector2I& screenPos) const;

+ 1 - 1
Source/BansheeEngine/GUI/BsDragAndDropManager.cpp

@@ -52,7 +52,7 @@ namespace bs
 		// This generally happens when window loses focus and capture is lost (for example alt+tab)
 		// This generally happens when window loses focus and capture is lost (for example alt+tab)
 		int captureActive = mCaptureActive.load();
 		int captureActive = mCaptureActive.load();
 		if (!captureActive && mCaptureChanged.load() && 
 		if (!captureActive && mCaptureChanged.load() && 
-			(gTime().getFrameIdx() > mCaptureChangeFrame.load())) // Wait one frame to insure input (like mouse up) gets a chance to be processed
+			(gTime().getFrameIdx() > mCaptureChangeFrame.load())) // Wait one frame to ensure input (like mouse up) gets a chance to be processed
 		{
 		{
 			endDrag(false);
 			endDrag(false);
 			mCaptureChanged.store(false);
 			mCaptureChanged.store(false);

+ 1 - 1
Source/BansheeEngine/GUI/BsShortcutKey.cpp

@@ -85,7 +85,7 @@ namespace bs
 		}
 		}
 		else
 		else
 		{
 		{
-			charStr = PlatformUtility::keyCodeToUnicode((UINT32)button);
+			charStr = Platform::keyCodeToUnicode((UINT32)button);
 			StringUtil::toUpperCase(charStr);
 			StringUtil::toUpperCase(charStr);
 		}
 		}
 
 

+ 75 - 0
Source/BansheeEngine/Platform/BsSplashScreen.cpp

@@ -1,5 +1,6 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include <X11/Xutil.h>
 #include "Platform/BsSplashScreen.h"
 #include "Platform/BsSplashScreen.h"
 #include "Resources/BsBuiltinResources.h"
 #include "Resources/BsBuiltinResources.h"
 #include "Utility/BsTimer.h"
 #include "Utility/BsTimer.h"
@@ -74,6 +75,80 @@ namespace bs
 	}
 	}
 }
 }
 
 
+#elif BS_PLATFORM == BS_PLATFORM_LINUX
+#include "Linux/BsLinuxPlatform.h"
+#include "Linux/BsLinuxWindow.h"
+
+namespace bs
+{
+	struct SplashScreen::Pimpl
+	{
+		LinuxWindow* window = nullptr;
+		Timer timer;
+	};
+
+	// Note: Never freed, but that's fine
+	SplashScreen::Pimpl* SplashScreen::m = bs_new<Pimpl>();
+	const UINT32 SplashScreen::SPLASH_SCREEN_DURATION_MS = 1000;
+
+	void SplashScreen::show()
+	{
+		gCoreThread().queueCommand(&SplashScreen::create, CTQF_InternalQueue);
+	}
+
+	void SplashScreen::hide()
+	{
+		gCoreThread().queueCommand(&SplashScreen::destroy, CTQF_InternalQueue);
+	}
+
+	void SplashScreen::create()
+	{
+		if (m->window != nullptr)
+			return;
+
+		WINDOW_DESC windowDesc;
+		windowDesc.width = 543;
+		windowDesc.height = 680;
+		windowDesc.x = -1;
+		windowDesc.y = -1;
+		windowDesc.title = "Banshee Splash";
+		windowDesc.showDecorations = false;
+		windowDesc.allowResize = false;
+
+		SPtr<PixelData> splashPixelData = BuiltinResources::getSplashScreen();
+		if (splashPixelData == nullptr)
+			return;
+
+		windowDesc.background = splashPixelData;
+
+		LinuxPlatform::lockX();
+		windowDesc.screen = XDefaultScreen(LinuxPlatform::getXDisplay());
+		windowDesc.visualInfo.depth = XDefaultDepth(LinuxPlatform::getXDisplay(), windowDesc.screen);
+		windowDesc.visualInfo.visual = XDefaultVisual(LinuxPlatform::getXDisplay(), windowDesc.screen);
+
+		m->window = bs_new<LinuxWindow>(windowDesc);
+		LinuxPlatform::unlockX();
+
+		m->timer.reset();
+	}
+
+	void SplashScreen::destroy()
+	{
+		if (m->window == nullptr)
+			return;
+
+		UINT64 currentTime = m->timer.getMilliseconds();
+		if (currentTime < SPLASH_SCREEN_DURATION_MS)
+			BS_THREAD_SLEEP(SPLASH_SCREEN_DURATION_MS - currentTime);
+
+		LinuxPlatform::lockX();
+		bs_delete(m->window);
+		LinuxPlatform::unlockX();
+
+		m->window = nullptr;
+	}
+}
+
 #else
 #else
 
 
 static_assert("Missing SplashScreen implementation.");
 static_assert("Missing SplashScreen implementation.");

+ 13 - 1
Source/BansheeGLRenderAPI/Linux/BsLinuxContext.cpp

@@ -1,7 +1,7 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "Linux/BsLinuxContext.h"
 #include "Linux/BsLinuxContext.h"
-#include "Unix/BsUnixPlatform.h"
+#include "Linux/BsLinuxPlatform.h"
 #include "Linux/BsLinuxGLSupport.h"
 #include "Linux/BsLinuxGLSupport.h"
 
 
 namespace bs { namespace ct
 namespace bs { namespace ct
@@ -15,6 +15,8 @@ namespace bs { namespace ct
 		LinuxContext::LinuxContext(::Display* display, XVisualInfo& visualInfo)
 		LinuxContext::LinuxContext(::Display* display, XVisualInfo& visualInfo)
 		: mDisplay(display), mContext(None)
 		: mDisplay(display), mContext(None)
 	{
 	{
+		LinuxPlatform::lockX();
+
 		INT32 dummy;
 		INT32 dummy;
 		XVisualInfo* windowVisualInfo = XGetVisualInfo(display, VisualIDMask | VisualScreenMask, &visualInfo, &dummy);
 		XVisualInfo* windowVisualInfo = XGetVisualInfo(display, VisualIDMask | VisualScreenMask, &visualInfo, &dummy);
 
 
@@ -85,6 +87,8 @@ namespace bs { namespace ct
 		XFree(windowVisualInfo);
 		XFree(windowVisualInfo);
 
 
 		mContext = context;
 		mContext = context;
+
+		LinuxPlatform::unlockX();
 	}
 	}
 
 
 	LinuxContext::~LinuxContext()
 	LinuxContext::~LinuxContext()
@@ -94,20 +98,28 @@ namespace bs { namespace ct
 
 
 	void LinuxContext::setCurrent()
 	void LinuxContext::setCurrent()
 	{
 	{
+		LinuxPlatform::lockX();
 		glXMakeCurrent(mDisplay, LinuxPlatform::getMainXWindow(), mContext);
 		glXMakeCurrent(mDisplay, LinuxPlatform::getMainXWindow(), mContext);
+		LinuxPlatform::unlockX();
 	}
 	}
 
 
 	void LinuxContext::endCurrent()
 	void LinuxContext::endCurrent()
 	{
 	{
+		LinuxPlatform::lockX();
 		glXMakeCurrent(mDisplay, None, None);
 		glXMakeCurrent(mDisplay, None, None);
+		LinuxPlatform::unlockX();
 	}
 	}
 
 
 	void LinuxContext::releaseContext()
 	void LinuxContext::releaseContext()
 	{
 	{
 		if (mContext)
 		if (mContext)
 		{
 		{
+			LinuxPlatform::lockX();
+
 			glXDestroyContext(mDisplay, mContext);
 			glXDestroyContext(mDisplay, mContext);
 			mContext = None;
 			mContext = None;
+
+			LinuxPlatform::unlockX();
 		}
 		}
 	}
 	}
 }}
 }}

+ 35 - 11
Source/BansheeGLRenderAPI/Linux/BsLinuxGLSupport.cpp

@@ -1,6 +1,6 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "Unix/BsUnixPlatform.h"
+#include "Linux/BsLinuxPlatform.h"
 #include "Linux/BsLinuxGLSupport.h"
 #include "Linux/BsLinuxGLSupport.h"
 #include "Linux/BsLinuxContext.h"
 #include "Linux/BsLinuxContext.h"
 #include "Linux/BsLinuxRenderWindow.h"
 #include "Linux/BsLinuxRenderWindow.h"
@@ -106,6 +106,8 @@ namespace bs { namespace ct
 	void LinuxGLSupport::start()
 	void LinuxGLSupport::start()
 	{
 	{
 		// Retrieve all essential extensions
 		// Retrieve all essential extensions
+		LinuxPlatform::lockX();
+
 		::Display* display = LinuxPlatform::getXDisplay();
 		::Display* display = LinuxPlatform::getXDisplay();
 		const char* glExtensions = glXQueryExtensionsString(display, DefaultScreen(display));
 		const char* glExtensions = glXQueryExtensionsString(display, DefaultScreen(display));
 
 
@@ -133,6 +135,8 @@ namespace bs { namespace ct
 			}
 			}
 
 
 		} while(*iter++);
 		} while(*iter++);
+
+		LinuxPlatform::unlockX();
 	}
 	}
 
 
 	void LinuxGLSupport::stop()
 	void LinuxGLSupport::stop()
@@ -161,7 +165,7 @@ namespace bs { namespace ct
 		return (void*)glXGetProcAddressARB((const GLubyte*)procname.c_str());
 		return (void*)glXGetProcAddressARB((const GLubyte*)procname.c_str());
 	}
 	}
 
 
-	XVisualInfo LinuxGLSupport::findBestVisual(::Display* display, bool depthStencil, UINT32 multisample, bool srgb) const
+	GLVisualConfig LinuxGLSupport::findBestVisual(::Display* display, bool depthStencil, UINT32 multisample, bool srgb) const
 	{
 	{
 		static constexpr INT32 VISUAL_ATTRIBS[] =
 		static constexpr INT32 VISUAL_ATTRIBS[] =
 		{
 		{
@@ -177,29 +181,37 @@ namespace bs { namespace ct
 
 
 		INT32 numConfigs;
 		INT32 numConfigs;
 		GLXFBConfig* configs = glXChooseFBConfig(display, DefaultScreen(display), VISUAL_ATTRIBS, &numConfigs);
 		GLXFBConfig* configs = glXChooseFBConfig(display, DefaultScreen(display), VISUAL_ATTRIBS, &numConfigs);
+		GLVisualCapabilities* caps = bs_stack_new<GLVisualCapabilities>(numConfigs);
 
 
 		// Find a config that best matches the requested properties
 		// Find a config that best matches the requested properties
 		INT32 bestScore = 0;
 		INT32 bestScore = 0;
 		INT32 bestConfig = -1;
 		INT32 bestConfig = -1;
 		for (int i = 0; i < numConfigs; ++i)
 		for (int i = 0; i < numConfigs; ++i)
 		{
 		{
-			uint32_t configScore = 0;
+			UINT32 configScore = 0;
 
 
 			// Depth buffer contributes the most to score
 			// Depth buffer contributes the most to score
 			if(depthStencil)
 			if(depthStencil)
 			{
 			{
-				int32_t depth, stencil;
+				INT32 depth, stencil;
 				glXGetFBConfigAttrib(display, configs[i], GLX_DEPTH_SIZE, &depth);
 				glXGetFBConfigAttrib(display, configs[i], GLX_DEPTH_SIZE, &depth);
 				glXGetFBConfigAttrib(display, configs[i], GLX_STENCIL_SIZE, &stencil);
 				glXGetFBConfigAttrib(display, configs[i], GLX_STENCIL_SIZE, &stencil);
 
 
+				UINT32 score = 0;
 				if(depth == 24 && stencil == 8)
 				if(depth == 24 && stencil == 8)
-					configScore += 10000;
+					score = 10000;
 				else if(depth == 32 && stencil == 8)
 				else if(depth == 32 && stencil == 8)
-					configScore += 9000;
+					score = 9000;
 				else if(depth == 32)
 				else if(depth == 32)
-					configScore += 8000;
+					score = 8000;
 				else if(depth == 16)
 				else if(depth == 16)
-					configScore += 7000;
+					score = 7000;
+
+				if(score > 0)
+				{
+					configScore += score;
+					caps[i].depthStencil = true;
+				}
 			}
 			}
 
 
 			// sRGB contributes second most
 			// sRGB contributes second most
@@ -214,8 +226,10 @@ namespace bs { namespace ct
 					glXGetFBConfigAttrib(display, configs[i], GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, &hasSRGB);
 					glXGetFBConfigAttrib(display, configs[i], GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, &hasSRGB);
 
 
 				if(hasSRGB)
 				if(hasSRGB)
+				{
 					configScore += 500;
 					configScore += 500;
-
+					caps[i].srgb = true;
+				}
 			}
 			}
 
 
 			if((multisample >= 1) && extGLX_ARB_multisample)
 			if((multisample >= 1) && extGLX_ARB_multisample)
@@ -225,7 +239,10 @@ namespace bs { namespace ct
 				glXGetFBConfigAttrib(display, configs[i], GLX_SAMPLES, &numSamples);
 				glXGetFBConfigAttrib(display, configs[i], GLX_SAMPLES, &numSamples);
 
 
 				if(hasMultisample && (numSamples <= multisample))
 				if(hasMultisample && (numSamples <= multisample))
+				{
 					configScore += (32 - (multisample - numSamples)) * 10;
 					configScore += (32 - (multisample - numSamples)) * 10;
+					caps[i].numSamples = numSamples;
+				}
 			}
 			}
 
 
 			if(configScore > bestScore)
 			if(configScore > bestScore)
@@ -235,18 +252,25 @@ namespace bs { namespace ct
 			}
 			}
 		}
 		}
 
 
+		GLVisualConfig output;
+
 		if(bestConfig == -1)
 		if(bestConfig == -1)
 		{
 		{
 			// Something went wrong
 			// Something went wrong
 			XFree(configs);
 			XFree(configs);
-			return XVisualInfo();
+			bs_stack_delete(caps, numConfigs);
+
+			return output;
 		}
 		}
 
 
 		XVisualInfo* visualInfo = glXGetVisualFromFBConfig(display, configs[bestConfig]);
 		XVisualInfo* visualInfo = glXGetVisualFromFBConfig(display, configs[bestConfig]);
-		XVisualInfo output = *visualInfo;
+
+		output.visualInfo = *visualInfo;
+		output.caps = caps[bestConfig];
 
 
 		XFree(configs);
 		XFree(configs);
 		XFree(visualInfo);
 		XFree(visualInfo);
+		bs_stack_delete(caps, numConfigs);
 
 
 		return output;
 		return output;
 	}
 	}

+ 16 - 1
Source/BansheeGLRenderAPI/Linux/BsLinuxGLSupport.h

@@ -36,6 +36,21 @@ namespace bs { namespace ct
 	extern glXSwapIntervalMESAProc glXSwapIntervalMESA;
 	extern glXSwapIntervalMESAProc glXSwapIntervalMESA;
 	extern glXSwapIntervalSGIProc glXSwapIntervalSGI;
 	extern glXSwapIntervalSGIProc glXSwapIntervalSGI;
 
 
+	/** Determines which features are supported by a particular framebuffer configuration. */
+	struct GLVisualCapabilities
+	{
+		bool depthStencil = false;
+		UINT32 numSamples = 1;
+		bool srgb = false;
+	};
+
+	/** Contains information about a framebuffer configuration that can be used to initialize a window and GL context. */
+	struct GLVisualConfig
+	{
+		GLVisualCapabilities caps;
+		XVisualInfo visualInfo;
+	};
+
 	/**	Handles OpenGL initialization, window creation and extensions on Linux. */
 	/**	Handles OpenGL initialization, window creation and extensions on Linux. */
 	class LinuxGLSupport : public GLSupport
 	class LinuxGLSupport : public GLSupport
 	{
 	{
@@ -72,7 +87,7 @@ namespace bs { namespace ct
 		 * 							will automatically be encoded into gamma space on write.
 		 * 							will automatically be encoded into gamma space on write.
 		 * @return					X11 visual info structure you may use to initialize a window.
 		 * @return					X11 visual info structure you may use to initialize a window.
 		 */
 		 */
-		XVisualInfo findBestVisual(::Display* display, bool depthStencil, UINT32 multisample, bool srgb) const;
+		GLVisualConfig findBestVisual(::Display* display, bool depthStencil, UINT32 multisample, bool srgb) const;
 
 
 		/** @copydoc GLSupport::getVideoModeInfo */
 		/** @copydoc GLSupport::getVideoModeInfo */
 		SPtr<VideoModeInfo> getVideoModeInfo() const override;
 		SPtr<VideoModeInfo> getVideoModeInfo() const override;

+ 209 - 97
Source/BansheeGLRenderAPI/Linux/BsLinuxRenderWindow.cpp

@@ -1,24 +1,16 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "CoreThread/BsCoreThread.h"
 #include "CoreThread/BsCoreThread.h"
-#include "BsGLPixelFormat.h"
-#include "Unix/BsUnixPlatform.h"
+#include "Linux/BsLinuxPlatform.h"
 #include "Linux/BsLinuxRenderWindow.h"
 #include "Linux/BsLinuxRenderWindow.h"
-#include "Unix/BsUnixWindow.h"
+#include "Linux/BsLinuxWindow.h"
 #include "Linux/BsLinuxVideoModeInfo.h"
 #include "Linux/BsLinuxVideoModeInfo.h"
 #include "Linux/BsLinuxGLSupport.h"
 #include "Linux/BsLinuxGLSupport.h"
-#include "Math/BsMath.h"
-#include "RenderAPI/BsRenderAPI.h"
+#include "BsGLPixelFormat.h"
 #include "BsGLRenderWindowManager.h"
 #include "BsGLRenderWindowManager.h"
-#include <GL/glxew.h>
-#include "Math/BsVector2I.h"
 
 
 namespace bs
 namespace bs
 {
 {
-	LinuxRenderWindowProperties::LinuxRenderWindowProperties(const RENDER_WINDOW_DESC& desc)
-		:RenderWindowProperties(desc)
-	{ }
-
 	LinuxRenderWindow::LinuxRenderWindow(const RENDER_WINDOW_DESC& desc, UINT32 windowId, ct::LinuxGLSupport& glSupport)
 	LinuxRenderWindow::LinuxRenderWindow(const RENDER_WINDOW_DESC& desc, UINT32 windowId, ct::LinuxGLSupport& glSupport)
 		:RenderWindow(desc, windowId), mGLSupport(glSupport), mProperties(desc)
 		:RenderWindow(desc, windowId), mGLSupport(glSupport), mProperties(desc)
 	{ }
 	{ }
@@ -27,21 +19,32 @@ namespace bs
 	{
 	{
 		if (name == "WINDOW")
 		if (name == "WINDOW")
 		{
 		{
-			::Window *x11Window = (::Window*)data;
-			// TODOPORT
-			//*x11Window = (UINT64)getHWnd();
+			blockUntilCoreInitialized();
+			getCore()->getCustomAttribute(name, data);
 			return;
 			return;
 		}
 		}
 	}
 	}
 
 
 	Vector2I LinuxRenderWindow::screenToWindowPos(const Vector2I& screenPos) const
 	Vector2I LinuxRenderWindow::screenToWindowPos(const Vector2I& screenPos) const
 	{
 	{
-		// TODOPORT
+		blockUntilCoreInitialized();
+
+		LinuxPlatform::lockX();
+		Vector2I pos = getCore()->_getInternal()->screenToWindowPos(screenPos);
+		LinuxPlatform::unlockX();
+
+		return pos;
 	}
 	}
 
 
 	Vector2I LinuxRenderWindow::windowToScreenPos(const Vector2I& windowPos) const
 	Vector2I LinuxRenderWindow::windowToScreenPos(const Vector2I& windowPos) const
 	{
 	{
-		// TODOPORT
+		blockUntilCoreInitialized();
+
+		LinuxPlatform::lockX();
+		Vector2I pos = getCore()->_getInternal()->windowToScreenPos(windowPos);
+		LinuxPlatform::unlockX();
+
+		return pos;
 	}
 	}
 
 
 	SPtr<ct::LinuxRenderWindow> LinuxRenderWindow::getCore() const
 	SPtr<ct::LinuxRenderWindow> LinuxRenderWindow::getCore() const
@@ -51,7 +54,7 @@ namespace bs
 
 
 	void LinuxRenderWindow::syncProperties()
 	void LinuxRenderWindow::syncProperties()
 	{
 	{
-		ScopedSpinLock lock(getCore()->mLock);
+		ScopedSpinLock lock(getCore()->_getPropertiesLock());
 		mProperties = getCore()->mSyncedProperties;
 		mProperties = getCore()->mSyncedProperties;
 	}
 	}
 
 
@@ -59,90 +62,85 @@ namespace bs
 	{
 	{
 	LinuxRenderWindow::LinuxRenderWindow(const RENDER_WINDOW_DESC& desc, UINT32 windowId, LinuxGLSupport& glsupport)
 	LinuxRenderWindow::LinuxRenderWindow(const RENDER_WINDOW_DESC& desc, UINT32 windowId, LinuxGLSupport& glsupport)
 			: RenderWindow(desc, windowId), mWindow(nullptr), mGLSupport(glsupport), mContext(nullptr), mProperties(desc)
 			: RenderWindow(desc, windowId), mWindow(nullptr), mGLSupport(glsupport), mContext(nullptr), mProperties(desc)
-			, mSyncedProperties(desc), mIsChild(false), mShowOnSwap(false)
+			, mSyncedProperties(desc), mIsChild(false), mShowOnSwap(false), mOldScreenConfig(nullptr)
 	{ }
 	{ }
 
 
 	LinuxRenderWindow::~LinuxRenderWindow()
 	LinuxRenderWindow::~LinuxRenderWindow()
 	{
 	{
-		LinuxRenderWindowProperties& props = mProperties;
+		RenderWindowProperties& props = mProperties;
 
 
 		if (mWindow != nullptr)
 		if (mWindow != nullptr)
 		{
 		{
+			LinuxPlatform::lockX();
+
+			if(mOldScreenConfig)
+			{
+				XRRFreeScreenConfigInfo(mOldScreenConfig);
+				mOldScreenConfig = nullptr;
+			}
+
 			mWindow->close();
 			mWindow->close();
 
 
 			bs_delete(mWindow);
 			bs_delete(mWindow);
 			mWindow = nullptr;
 			mWindow = nullptr;
-		}
 
 
-		props.mActive = false;
+			LinuxPlatform::unlockX();
+		}
 	}
 	}
 
 
 	void LinuxRenderWindow::initialize()
 	void LinuxRenderWindow::initialize()
 	{
 	{
-		LinuxRenderWindowProperties& props = mProperties;
+		LinuxPlatform::lockX();
+
+		RenderWindowProperties& props = mProperties;
 
 
-		props.mIsFullScreen = mDesc.fullscreen;
+		props.isFullScreen = mDesc.fullscreen;
 		mIsChild = false;
 		mIsChild = false;
-		mDisplayFrequency = Math::roundToInt(mDesc.videoMode.getRefreshRate());
-		props.mColorDepth = 32;
+
+		GLVisualConfig visualConfig = mGLSupport.findBestVisual(LinuxPlatform::getXDisplay(), mDesc.depthBuffer,
+				mDesc.multisampleCount, mDesc.gamma);
 
 
 		WINDOW_DESC windowDesc;
 		WINDOW_DESC windowDesc;
-		windowDesc.fullscreen = mDesc.fullscreen;
 		windowDesc.x = mDesc.left;
 		windowDesc.x = mDesc.left;
 		windowDesc.y = mDesc.top;
 		windowDesc.y = mDesc.top;
 		windowDesc.width = mDesc.videoMode.getWidth();
 		windowDesc.width = mDesc.videoMode.getWidth();
 		windowDesc.height = mDesc.videoMode.getHeight();
 		windowDesc.height = mDesc.videoMode.getHeight();
-		windowDesc.hidden = mDesc.hidden || mDesc.hideUntilSwap;
 		windowDesc.title = mDesc.title;
 		windowDesc.title = mDesc.title;
 		windowDesc.showDecorations = !mDesc.toolWindow;
 		windowDesc.showDecorations = !mDesc.toolWindow;
 		windowDesc.modal = mDesc.modal;
 		windowDesc.modal = mDesc.modal;
-		windowDesc.visualInfo = mGLSupport.findBestVisual(LinuxPlatform::getXDisplay(), mDesc.depthBuffer,
-				mDesc.multisampleCount, mDesc.gamma);
+		windowDesc.visualInfo = visualConfig.visualInfo;
+		windowDesc.screen = mDesc.videoMode.getOutputIdx();
 
 
 		NameValuePairList::const_iterator opt;
 		NameValuePairList::const_iterator opt;
 		opt = mDesc.platformSpecific.find("parentWindowHandle");
 		opt = mDesc.platformSpecific.find("parentWindowHandle");
 		if (opt != mDesc.platformSpecific.end())
 		if (opt != mDesc.platformSpecific.end())
 			windowDesc.parent = (::Window)parseUINT64(opt->second);
 			windowDesc.parent = (::Window)parseUINT64(opt->second);
 
 
-		const LinuxVideoModeInfo& videoModeInfo = static_cast<const LinuxVideoModeInfo&>(RenderAPI::instance()
-				.getVideoModeInfo());
-		UINT32 numOutputs = videoModeInfo.getNumOutputs();
-		if (numOutputs > 0)
-		{
-			UINT32 actualMonitorIdx = std::min(mDesc.videoMode.getOutputIdx(), numOutputs - 1);
-			const LinuxVideoOutputInfo& outputInfo = static_cast<const LinuxVideoOutputInfo&>(videoModeInfo
-					.getOutputInfo(actualMonitorIdx));
-			windowDesc.monitor = outputInfo.getMonitorHandle();
-		}
-
 		mIsChild = windowDesc.parent != nullptr;
 		mIsChild = windowDesc.parent != nullptr;
-		props.mIsFullScreen = mDesc.fullscreen && !mIsChild;
-		props.mColorDepth = 32;
-		props.mActive = true;
+		props.isFullScreen = mDesc.fullscreen && !mIsChild;
 
 
-		if (!windowDesc.external)
-		{
-			mShowOnSwap = mDesc.hideUntilSwap;
-			props.mHidden = mDesc.hideUntilSwap || mDesc.hidden;
-		}
+		mShowOnSwap = mDesc.hideUntilSwap;
+		props.isHidden = mDesc.hideUntilSwap || mDesc.hidden;
 
 
 		mWindow = bs_new<LinuxWindow>(windowDesc);
 		mWindow = bs_new<LinuxWindow>(windowDesc);
 
 
-		props.mWidth = mWindow->getWidth();
-		props.mHeight = mWindow->getHeight();
-		props.mTop = mWindow->getTop();
-		props.mLeft = mWindow->getLeft();
+		props.width = mWindow->getWidth();
+		props.height = mWindow->getHeight();
+		props.top = mWindow->getTop();
+		props.left = mWindow->getLeft();
 
 
-		props.mHwGamma = testHwGamma;
-		props.mMultisampleCount = testMultisample;
+		props.hwGamma = visualConfig.caps.srgb;
+		props.multisampleCount = visualConfig.caps.numSamples > 1;
 
 
 		XWindowAttributes windowAttributes;
 		XWindowAttributes windowAttributes;
 		XGetWindowAttributes(LinuxPlatform::getXDisplay(), mWindow->_getXWindow(), &windowAttributes);
 		XGetWindowAttributes(LinuxPlatform::getXDisplay(), mWindow->_getXWindow(), &windowAttributes);
 
 
 		XVisualInfo requestVI;
 		XVisualInfo requestVI;
-		requestVI.screen = DefaultScreen(LinuxPlatform::getXDisplay());
+		requestVI.screen = windowDesc.screen;
 		requestVI.visualid = XVisualIDFromVisual(windowAttributes.visual);
 		requestVI.visualid = XVisualIDFromVisual(windowAttributes.visual);
 
 
+		LinuxPlatform::unlockX(); // Calls below have their own locking mechanisms
+
 		mContext = mGLSupport.createContext(LinuxPlatform::getXDisplay(), requestVI);
 		mContext = mGLSupport.createContext(LinuxPlatform::getXDisplay(), requestVI);
 
 
 		if(mDesc.fullscreen && !mIsChild)
 		if(mDesc.fullscreen && !mIsChild)
@@ -172,23 +170,110 @@ namespace bs
 		if (numOutputs == 0)
 		if (numOutputs == 0)
 			return;
 			return;
 
 
-		LinuxRenderWindowProperties& props = mProperties;
+		RenderWindowProperties& props = mProperties;
 
 
 		UINT32 actualMonitorIdx = std::min(monitorIdx, numOutputs - 1);
 		UINT32 actualMonitorIdx = std::min(monitorIdx, numOutputs - 1);
 		const LinuxVideoOutputInfo& outputInfo = static_cast<const LinuxVideoOutputInfo&>(videoModeInfo.getOutputInfo (actualMonitorIdx));
 		const LinuxVideoOutputInfo& outputInfo = static_cast<const LinuxVideoOutputInfo&>(videoModeInfo.getOutputInfo (actualMonitorIdx));
 
 
-		// TODO - Change screen mode
+		LinuxPlatform::lockX();
+		::Display* display = LinuxPlatform::getXDisplay();
 
 
+		// Change video mode if required
+		bool changeVideoMode = false;
 
 
+		XRRScreenConfiguration* screenConfig = XRRGetScreenInfo(display, XRootWindow(display, DefaultScreen (display)));
+		Rotation currentRotation;
+		SizeID currentSizeID = XRRConfigCurrentConfiguration(screenConfig, &currentRotation);
+		short currentRate = XRRConfigCurrentRate(screenConfig);
+
+		int numSizes;
+		XRRScreenSize* screenSizes = XRRConfigSizes(screenConfig, &numSizes);
+
+		if(width != screenSizes[currentSizeID].width || height != screenSizes[currentSizeID].height ||
+			currentRate != (short)refreshRate)
+			changeVideoMode = true;
+
+		// If provided mode matches current mode, avoid making the video mode change
+		if(changeVideoMode)
+		{
+			// Remember the old config so we can restore it when exiting fullscreen
+			if(mOldScreenConfig)
+			{
+				XRRFreeScreenConfigInfo(mOldScreenConfig);
+				mOldScreenConfig = nullptr;
+			}
+
+			mOldScreenConfig = screenConfig;
+			mOldConfigSizeID = currentSizeID;
+			mOldConfigRate = currentRate;
+
+			// Look for size that best matches our requested video mode
+			bool foundSize = false;
+			SizeID foundSizeID = 0;
+			for(int i = 0; i < numSizes; i++)
+			{
+				UINT32 curWidth, curHeight;
+				if(currentRotation == RR_Rotate_90 || currentRotation == RR_Rotate_270)
+				{
+					curWidth = screenSizes[i].height;
+					curHeight = screenSizes[i].width;
+				}
+				else
+				{
+					curWidth = screenSizes[i].width;
+					curHeight = screenSizes[i].height;
+				}
+
+				if(curWidth == width && curHeight == height)
+				{
+					foundSizeID = i;
+					foundSize = true;
+					break;
+				}
+			}
+
+			if(!foundSize)
+				LOGERR("Cannot change video mode, requested resolution not supported.");
+
+			// Find refresh rate closest to the requested one, or fall back to 60
+			if(foundSize)
+			{
+				int numRates;
+				short* rates = XRRConfigRates(screenConfig, foundSizeID, &numRates);
+
+				short bestRate = 60;
+				for(int i = 0; i < numRates; i++)
+				{
+					if(rates[i] == (short)refreshRate)
+					{
+						bestRate = rates[i];
+						break;
+					}
+					else
+					{
+						short diffNew = abs((short)refreshRate - rates[i]);
+						short diffOld = abs((short)refreshRate - bestRate);
+
+						if(diffNew < diffOld)
+							bestRate = rates[i];
+					}
+				}
+
+				XRRSetScreenConfigAndRate(display, screenConfig, XRootWindow(display, DefaultScreen(display)),
+						foundSizeID, currentRotation, bestRate, CurrentTime);
+			}
+		}
 
 
 		mWindow->_setFullscreen(true);
 		mWindow->_setFullscreen(true);
 
 
-		props.mIsFullScreen = true;
+		LinuxPlatform::unlockX();
+
+		props.isFullScreen = true;
 
 
-		props.mTop = monitorInfo.rcMonitor.top;
-		props.mLeft = monitorInfo.rcMonitor.left;
-		props.mWidth = width;
-		props.mHeight = height;
+		props.top = 0;
+		props.left = 0;
+		props.width = width;
+		props.height = height;
 
 
 		_windowMovedOrResized();
 		_windowMovedOrResized();
 	}
 	}
@@ -204,23 +289,33 @@ namespace bs
 	{
 	{
 		THROW_IF_NOT_CORE_THREAD;
 		THROW_IF_NOT_CORE_THREAD;
 
 
-		LinuxRenderWindowProperties& props = mProperties;
+		RenderWindowProperties& props = mProperties;
 
 
-		if (!props.mIsFullScreen)
+		if (!props.isFullScreen)
 			return;
 			return;
 
 
-		props.mIsFullScreen = false;
-		props.mWidth = width;
-		props.mHeight = height;
+		props.isFullScreen = false;
+		props.width = width;
+		props.height = height;
 
 
-		// TODO - Restore old screen config
+		LinuxPlatform::lockX();
 
 
+		// Restore old screen config
+		if(mOldScreenConfig)
+		{
+			::Display* display = LinuxPlatform::getXDisplay();
+			XRRSetScreenConfigAndRate(display, mOldScreenConfig, XRootWindow(display, DefaultScreen(display)),
+					mOldConfigSizeID, mOldConfigRotation, mOldConfigRate, CurrentTime);
+			XRRFreeScreenConfigInfo(mOldScreenConfig);
+		}
 		mWindow->_setFullscreen(false);
 		mWindow->_setFullscreen(false);
 
 
+		LinuxPlatform::unlockX();
+
 		{
 		{
 			ScopedSpinLock lock(mLock);
 			ScopedSpinLock lock(mLock);
-			mSyncedProperties.mWidth = props.mWidth;
-			mSyncedProperties.mHeight = props.mHeight;
+			mSyncedProperties.width = props.width;
+			mSyncedProperties.height = props.height;
 		}
 		}
 
 
 		bs::RenderWindowManager::instance().notifySyncDataDirty(this);
 		bs::RenderWindowManager::instance().notifySyncDataDirty(this);
@@ -231,18 +326,20 @@ namespace bs
 	{
 	{
 		THROW_IF_NOT_CORE_THREAD;
 		THROW_IF_NOT_CORE_THREAD;
 
 
-		LinuxRenderWindowProperties& props = mProperties;
-		if (!props.mIsFullScreen)
+		RenderWindowProperties& props = mProperties;
+		if (!props.isFullScreen)
 		{
 		{
+			LinuxPlatform::lockX();
 			mWindow->move(left, top);
 			mWindow->move(left, top);
+			LinuxPlatform::unlockX();
 
 
-			props.mTop = mWindow->getTop();
-			props.mLeft = mWindow->getLeft();
+			props.top = mWindow->getTop();
+			props.left = mWindow->getLeft();
 
 
 			{
 			{
 				ScopedSpinLock lock(mLock);
 				ScopedSpinLock lock(mLock);
-				mSyncedProperties.mTop = props.mTop;
-				mSyncedProperties.mLeft = props.mLeft;
+				mSyncedProperties.top = props.top;
+				mSyncedProperties.left = props.left;
 			}
 			}
 
 
 			bs::RenderWindowManager::instance().notifySyncDataDirty(this);
 			bs::RenderWindowManager::instance().notifySyncDataDirty(this);
@@ -253,18 +350,20 @@ namespace bs
 	{
 	{
 		THROW_IF_NOT_CORE_THREAD;
 		THROW_IF_NOT_CORE_THREAD;
 
 
-		LinuxRenderWindowProperties& props = mProperties;
-		if (!props.mIsFullScreen)
+		RenderWindowProperties& props = mProperties;
+		if (!props.isFullScreen)
 		{
 		{
+			LinuxPlatform::lockX();
 			mWindow->resize(width, height);
 			mWindow->resize(width, height);
+			LinuxPlatform::unlockX();
 
 
-			props.mWidth = mWindow->getWidth();
-			props.mHeight = mWindow->getHeight();
+			props.width = mWindow->getWidth();
+			props.height = mWindow->getHeight();
 
 
 			{
 			{
 				ScopedSpinLock lock(mLock);
 				ScopedSpinLock lock(mLock);
-				mSyncedProperties.mWidth = props.mWidth;
-				mSyncedProperties.mHeight = props.mHeight;
+				mSyncedProperties.width = props.width;
+				mSyncedProperties.height = props.height;
 			}
 			}
 
 
 			bs::RenderWindowManager::instance().notifySyncDataDirty(this);
 			bs::RenderWindowManager::instance().notifySyncDataDirty(this);
@@ -275,21 +374,27 @@ namespace bs
 	{
 	{
 		THROW_IF_NOT_CORE_THREAD;
 		THROW_IF_NOT_CORE_THREAD;
 
 
+		LinuxPlatform::lockX();
 		mWindow->minimize();
 		mWindow->minimize();
+		LinuxPlatform::unlockX();
 	}
 	}
 
 
 	void LinuxRenderWindow::maximize()
 	void LinuxRenderWindow::maximize()
 	{
 	{
 		THROW_IF_NOT_CORE_THREAD;
 		THROW_IF_NOT_CORE_THREAD;
 
 
+		LinuxPlatform::lockX();
 		mWindow->maximize();
 		mWindow->maximize();
+		LinuxPlatform::unlockX();
 	}
 	}
 
 
 	void LinuxRenderWindow::restore()
 	void LinuxRenderWindow::restore()
 	{
 	{
 		THROW_IF_NOT_CORE_THREAD;
 		THROW_IF_NOT_CORE_THREAD;
 
 
+		LinuxPlatform::lockX();
 		mWindow->restore();
 		mWindow->restore();
+		LinuxPlatform::unlockX();
 	}
 	}
 
 
 	void LinuxRenderWindow::swapBuffers(UINT32 syncMask)
 	void LinuxRenderWindow::swapBuffers(UINT32 syncMask)
@@ -299,15 +404,17 @@ namespace bs
 		if (mShowOnSwap)
 		if (mShowOnSwap)
 			setHidden(false);
 			setHidden(false);
 
 
+		LinuxPlatform::lockX();
 		glXSwapBuffers(LinuxPlatform::getXDisplay(), mWindow->_getXWindow());
 		glXSwapBuffers(LinuxPlatform::getXDisplay(), mWindow->_getXWindow());
+		LinuxPlatform::unlockX();
 	}
 	}
 
 
 	void LinuxRenderWindow::copyToMemory(PixelData &dst, FrameBuffer buffer)
 	void LinuxRenderWindow::copyToMemory(PixelData &dst, FrameBuffer buffer)
 	{
 	{
 		THROW_IF_NOT_CORE_THREAD;
 		THROW_IF_NOT_CORE_THREAD;
 
 
-		if ((dst.getRight() > getProperties().getWidth()) ||
-			(dst.getBottom() > getProperties().getHeight()) ||
+		if ((dst.getRight() > getProperties().width) ||
+			(dst.getBottom() > getProperties().height) ||
 			(dst.getFront() != 0) || (dst.getBack() != 1))
 			(dst.getFront() != 0) || (dst.getBack() != 1))
 		{
 		{
 			BS_EXCEPT(InvalidParametersException, "Invalid box.");
 			BS_EXCEPT(InvalidParametersException, "Invalid box.");
@@ -315,7 +422,7 @@ namespace bs
 
 
 		if (buffer == FB_AUTO)
 		if (buffer == FB_AUTO)
 		{
 		{
-			buffer = mProperties.isFullScreen() ? FB_FRONT : FB_BACK;
+			buffer = mProperties.isFullScreen ? FB_FRONT : FB_BACK;
 		}
 		}
 
 
 		GLenum format = GLPixelUtil::getGLOriginFormat(dst.getFormat());
 		GLenum format = GLPixelUtil::getGLOriginFormat(dst.getFormat());
@@ -366,8 +473,8 @@ namespace bs
 		}
 		}
 		else if(name == "WINDOW")
 		else if(name == "WINDOW")
 		{
 		{
-			::Window* window = (::Window*)data;
-			*window = mWindow->_getXWindow();
+			LinuxWindow** window = (LinuxWindow**)data;
+			*window = mWindow;
 			return;
 			return;
 		}
 		}
 	}
 	}
@@ -376,11 +483,15 @@ namespace bs
 	{
 	{
 		THROW_IF_NOT_CORE_THREAD;
 		THROW_IF_NOT_CORE_THREAD;
 
 
+		LinuxPlatform::lockX();
+
 		if(state)
 		if(state)
 			mWindow->restore();
 			mWindow->restore();
 		else
 		else
 			mWindow->minimize();
 			mWindow->minimize();
 
 
+		LinuxPlatform::unlockX();
+
 		RenderWindow::setActive(state);
 		RenderWindow::setActive(state);
 	}
 	}
 
 
@@ -390,11 +501,15 @@ namespace bs
 
 
 		mShowOnSwap = false;
 		mShowOnSwap = false;
 
 
+		LinuxPlatform::lockX();
+
 		if(hidden)
 		if(hidden)
 			mWindow->hide();
 			mWindow->hide();
 		else
 		else
 			mWindow->show();
 			mWindow->show();
 
 
+		LinuxPlatform::unlockX();
+
 		RenderWindow::setHidden(hidden);
 		RenderWindow::setHidden(hidden);
 	}
 	}
 
 
@@ -403,15 +518,13 @@ namespace bs
 		if (!mWindow)
 		if (!mWindow)
 			return;
 			return;
 
 
-		mWindow->_windowMovedOrResized();
-
-		LinuxRenderWindowProperties& props = mProperties;
-		if (!props.mIsFullScreen) // Fullscreen is handled directly by this object
+		RenderWindowProperties& props = mProperties;
+		if (!props.isFullScreen) // Fullscreen is handled directly by this object
 		{
 		{
-			props.mTop = mWindow->getTop();
-			props.mLeft = mWindow->getLeft();
-			props.mWidth = mWindow->getWidth();
-			props.mHeight = mWindow->getHeight();
+			props.top = mWindow->getTop();
+			props.left = mWindow->getLeft();
+			props.width = mWindow->getWidth();
+			props.height = mWindow->getHeight();
 		}
 		}
 
 
 		RenderWindow::_windowMovedOrResized();
 		RenderWindow::_windowMovedOrResized();
@@ -422,6 +535,5 @@ namespace bs
 		ScopedSpinLock lock(mLock);
 		ScopedSpinLock lock(mLock);
 		mProperties = mSyncedProperties;
 		mProperties = mSyncedProperties;
 	}
 	}
-	}
-}
+}}
 
 

+ 16 - 15
Source/BansheeGLRenderAPI/Linux/BsLinuxRenderWindow.h

@@ -3,6 +3,7 @@
 #pragma once
 #pragma once
 
 
 #include "RenderAPI/BsRenderWindow.h"
 #include "RenderAPI/BsRenderWindow.h"
+#include <X11/extensions/Xrandr.h>
 
 
 namespace bs
 namespace bs
 {
 {
@@ -20,18 +21,6 @@ namespace bs
 	 *  @{
 	 *  @{
 	 */
 	 */
 
 
-	/**	Contains various properties that describe a render window. */
-	class LinuxRenderWindowProperties : public RenderWindowProperties
-	{
-	public:
-		LinuxRenderWindowProperties(const RENDER_WINDOW_DESC& desc);
-		virtual ~LinuxRenderWindowProperties() { }
-
-	private:
-		friend class ct::LinuxRenderWindow;
-		friend class LinuxRenderWindow;
-	};
-
 	/**
 	/**
 	 * Render window implementation for Linux.
 	 * Render window implementation for Linux.
 	 *
 	 *
@@ -69,7 +58,7 @@ namespace bs
 
 
 	private:
 	private:
 		ct::LinuxGLSupport& mGLSupport;
 		ct::LinuxGLSupport& mGLSupport;
-		LinuxRenderWindowProperties mProperties;
+		RenderWindowProperties mProperties;
 	};
 	};
 
 
 	namespace ct
 	namespace ct
@@ -132,6 +121,12 @@ namespace bs
 			/** @copydoc RenderWindow::_windowMovedOrResized */
 			/** @copydoc RenderWindow::_windowMovedOrResized */
 			void _windowMovedOrResized() override;
 			void _windowMovedOrResized() override;
 
 
+			/** Returns a lock that can be used for accessing synced properties. */
+			SpinLock& _getPropertiesLock() { return mLock;}
+
+			/** Returns the internal X11 window that this object wraps. */
+			LinuxWindow* _getInternal() const { return mWindow; }
+
 		protected:
 		protected:
 			friend class LinuxGLSupport;
 			friend class LinuxGLSupport;
 
 
@@ -153,10 +148,16 @@ namespace bs
 			LinuxWindow* mWindow;
 			LinuxWindow* mWindow;
 			LinuxGLSupport& mGLSupport;
 			LinuxGLSupport& mGLSupport;
 			SPtr<LinuxContext> mContext;
 			SPtr<LinuxContext> mContext;
-			LinuxRenderWindowProperties mProperties;
-			LinuxRenderWindowProperties mSyncedProperties;
+			RenderWindowProperties mProperties;
+			RenderWindowProperties mSyncedProperties;
 			bool mIsChild;
 			bool mIsChild;
 			bool mShowOnSwap;
 			bool mShowOnSwap;
+
+			// Config before entering fullscreen
+			XRRScreenConfiguration* mOldScreenConfig;
+			SizeID mOldConfigSizeID;
+			short mOldConfigRate;
+			Rotation mOldConfigRotation;
 		};
 		};
 	}
 	}
 
 

+ 5 - 1
Source/BansheeGLRenderAPI/Linux/BsLinuxVideoModeInfo.cpp

@@ -1,7 +1,7 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "Linux/BsLinuxVideoModeInfo.h"
 #include "Linux/BsLinuxVideoModeInfo.h"
-#include "Unix/BsUnixPlatform.h"
+#include "Linux/BsLinuxPlatform.h"
 #include <X11/extensions/Xrandr.h>
 #include <X11/extensions/Xrandr.h>
 
 
 #define XRANDR_ROTATION_LEFT    (1 << 1)
 #define XRANDR_ROTATION_LEFT    (1 << 1)
@@ -11,6 +11,8 @@ namespace bs { namespace ct
 {
 {
 	LinuxVideoModeInfo::LinuxVideoModeInfo()
 	LinuxVideoModeInfo::LinuxVideoModeInfo()
 	{
 	{
+		LinuxPlatform::lockX();
+
 		::Display* display = LinuxPlatform::getXDisplay();
 		::Display* display = LinuxPlatform::getXDisplay();
 
 
 		INT32 minor, major;
 		INT32 minor, major;
@@ -53,6 +55,8 @@ namespace bs { namespace ct
 
 
 			XRRFreeScreenResources(screenRes);
 			XRRFreeScreenResources(screenRes);
 		}
 		}
+
+		LinuxPlatform::unlockX();
 	}
 	}
 
 
 	LinuxVideoOutputInfo::LinuxVideoOutputInfo(::Display* x11Display, XRROutputInfo* outputInfo, XRRCrtcInfo* crtcInfo,
 	LinuxVideoOutputInfo::LinuxVideoOutputInfo(::Display* x11Display, XRROutputInfo* outputInfo, XRRCrtcInfo* crtcInfo,

+ 3 - 3
Source/BansheeUtility/CMakeSources.cmake

@@ -29,9 +29,9 @@ set(BS_BANSHEEUTILITY_SRC_WIN32
 )
 )
 
 
 set(BS_BANSHEEUTILITY_SRC_UNIX
 set(BS_BANSHEEUTILITY_SRC_UNIX
-	"Unix/BsUnixCrashHandler.cpp"
-	"Unix/BsUnixFileSystem.cpp"
-	"Unix/BsUnixPlatformUtility.cpp"
+	"Linux/BsUnixCrashHandler.cpp"
+	"Linux/BsUnixFileSystem.cpp"
+	"Linux/BsUnixPlatformUtility.cpp"
 )
 )
 
 
 set(BS_BANSHEEUTILITY_INC_IMAGE
 set(BS_BANSHEEUTILITY_INC_IMAGE

+ 0 - 0
Source/BansheeUtility/Unix/BsUnixCrashHandler.cpp → Source/BansheeUtility/Linux/BsUnixCrashHandler.cpp


+ 0 - 0
Source/BansheeUtility/Unix/BsUnixFileSystem.cpp → Source/BansheeUtility/Linux/BsUnixFileSystem.cpp


+ 0 - 0
Source/BansheeUtility/Unix/BsUnixPlatformUtility.cpp → Source/BansheeUtility/Linux/BsUnixPlatformUtility.cpp