Переглянути джерело

WIP Linux port:
- Linux input handling code

Marko Pintera 8 роки тому
батько
коміт
995485a010

+ 5 - 0
Source/BansheeCore/CMakeSources.cmake

@@ -604,6 +604,7 @@ set(BS_BANSHEECORE_INC_PLATFORM_UNIX
 	"Linux/BsLinuxPlatform.h"
 	"Linux/BsLinuxPlatform.h"
 	"Linux/BsLinuxWindow.h"
 	"Linux/BsLinuxWindow.h"
 	"Linux/BsLinuxDropTarget.h"
 	"Linux/BsLinuxDropTarget.h"
+	"Linux/BsLinuxInput.h"
 )
 )
 
 
 set(BS_BANSHEECORE_SRC_PLATFORM_UNIX
 set(BS_BANSHEECORE_SRC_PLATFORM_UNIX
@@ -611,6 +612,10 @@ set(BS_BANSHEECORE_SRC_PLATFORM_UNIX
 	"Linux/BsLinuxWindow.cpp"
 	"Linux/BsLinuxWindow.cpp"
 	"Linux/BsLinuxDropTarget.cpp"
 	"Linux/BsLinuxDropTarget.cpp"
 	"Linux/BsLinuxFolderMonitor.cpp"
 	"Linux/BsLinuxFolderMonitor.cpp"
+	"Linux/BsLinuxInput.cpp"
+	"Linux/BsLinuxGamepad.cpp"
+	"Linux/BsLinuxMouse.cpp"
+	"Linux/BsLinuxKeyboard.cpp"
 )
 )
 
 
 if(WIN32)
 if(WIN32)

+ 156 - 0
Source/BansheeCore/Linux/BsLinuxGamepad.cpp

@@ -0,0 +1,156 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "Input/BsGamepad.h"
+#include "Input/BsInput.h"
+#include "Linux/BsLinuxInput.h"
+#include <fcntl.h>
+#include <linux/input.h>
+
+namespace bs
+{
+	/** Contains private data for the Linux Gamepad implementation. */
+	struct Gamepad::Pimpl
+	{
+		GamepadInfo info;
+		INT32 fileHandle;
+		ButtonCode povState;
+	};
+
+	Gamepad::Gamepad(const String& name, const GamepadInfo& gamepadInfo, Input* owner)
+			: mName(name), mOwner(owner)
+	{
+		m = bs_new<Pimpl>();
+		m->info = gamepadInfo;
+		m->povState = BC_UNASSIGNED;
+
+		String eventPath = "/dev/input/event" + toString(gamepadInfo.eventHandlerIdx);
+		m->fileHandle = open(eventPath.c_str(), O_RDWR | O_NONBLOCK);
+
+		if(m->fileHandle == -1)
+			LOGERR("Failed to open input event file handle for device: " + gamepadInfo.name);
+	}
+
+	Gamepad::~Gamepad()
+	{
+		if(m->fileHandle != -1)
+			close(m->fileHandle);
+
+		bs_delete(m);
+	}
+
+	void Gamepad::capture()
+	{
+		if(m->fileHandle == -1)
+			return;
+
+		struct AxisState
+		{
+			bool moved;
+			INT32 value;
+		};
+
+		AxisState axisState[24];
+		bs_zero_out(axisState);
+
+		input_event events[BUFFER_SIZE_GAMEPAD];
+		while(true)
+		{
+			ssize_t numReadBytes = read(m->fileHandle, &events, sizeof(events));
+			if(numReadBytes < 0)
+				break;
+
+			UINT32 numEvents = numReadBytes / sizeof(input_event);
+			for(UINT32 i = 0; i < numEvents; ++i)
+			{
+				switch(events[i].type)
+				{
+				case EV_KEY:
+				{
+					auto findIter = m->info.buttonMap.find(events[i].code);
+					if(findIter == m->info.buttonMap.end())
+						continue;
+
+					if(events[i].value)
+						mOwner->_notifyButtonPressed(m->info.id, findIter->second, (UINT64)events[i].time.tv_usec);
+					else
+						mOwner->_notifyButtonReleased(m->info.id, findIter->second, (UINT64)events[i].time.tv_usec);
+				}
+					break;
+				case EV_ABS:
+				{
+					// Stick or trigger
+					if(events[i].code <= ABS_BRAKE)
+					{
+						const AxisInfo& axisInfo = m->info.axisMap[events[i].code];
+
+						if(axisInfo.axisIdx >= 24)
+							break;
+
+						axisState[axisInfo.axisIdx].moved = true;
+
+						// Scale range if needed
+						if(axisInfo.min == Gamepad::MIN_AXIS && axisInfo.max != Gamepad::MAX_AXIS )
+							axisState[axisInfo.axisIdx].value = events[i].value;
+						else
+						{
+							float range = (float)(axisInfo.max - axisInfo.min);
+							float normalizedValue = (axisInfo.max - events[i].value) / range;
+
+							range = (float)(Gamepad::MAX_AXIS - Gamepad::MIN_AXIS);
+							axisState[axisInfo.axisIdx].value = Gamepad::MIN_AXIS + (INT32)(normalizedValue * range);
+						}
+					}
+					else if(events[i].code <= ABS_HAT3Y) // POV
+					{
+						// Note: We only support a single POV and report events from all POVs as if they were from the
+						// same source
+						INT32 povIdx = events[i].code - ABS_HAT0X;
+
+						ButtonCode povButton = BC_UNASSIGNED;
+						if((povIdx & 0x1) == 0) // Even, x axis
+						{
+							if(events[i].value == -1)
+								povButton = BC_GAMEPAD_DPAD_LEFT;
+							else if(events[i].value == 1)
+								povButton = BC_GAMEPAD_DPAD_RIGHT;
+						}
+						else // Odd, y axis
+						{
+							if(events[i].value == -1)
+								povButton = BC_GAMEPAD_DPAD_UP;
+							else if(events[i].value == 1)
+								povButton = BC_GAMEPAD_DPAD_DOWN;
+						}
+
+						if(m->povState != povButton)
+						{
+							if(m->povState != BC_UNASSIGNED)
+								mOwner->_notifyButtonReleased(m->info.id, m->povState, (UINT64)events[i].time.tv_usec);
+
+							if(povButton != BC_UNASSIGNED)
+								mOwner->_notifyButtonPressed(m->info.id, povButton, (UINT64)events[i].time.tv_usec);
+
+
+							m->povState = povButton;
+						}
+					}
+					break;
+				}
+				default: break;
+				}
+			}
+		}
+
+		for(UINT32 i = 0; i < 24; i++)
+		{
+			if(axisState[i].moved)
+				mOwner->_notifyAxisMoved(m->info.id, i, axisState[i].value);
+		}
+	}
+
+	void Gamepad::changeCaptureContext(UINT64 windowHandle)
+	{
+		// Do nothing
+	}
+}
+

+ 344 - 0
Source/BansheeCore/Linux/BsLinuxInput.cpp

@@ -0,0 +1,344 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "Input/BsInput.h"
+#include "Linux/BsLinuxInput.h"
+#include "Input/BsMouse.h"
+#include "Input/BsKeyboard.h"
+#include "Input/BsGamepad.h"
+#include <fcntl.h>
+#include <linux/input.h>
+
+namespace bs
+{
+	/** Information about events reported from a specific input event device. */
+	struct EventInfo
+	{
+		Vector<INT32> buttons;
+		Vector<INT32> relAxes;
+		Vector<INT32> absAxes;
+		Vector<INT32> hats;
+	};
+
+	/** Checks is the bit at the specified location in a byte array is set. */
+	bool isBitSet(UINT8 bits[], UINT32 bit)
+	{
+		return ((bits[bit/8] >> (bit%8)) & 1) != 0;
+	}
+
+	/** Returns information about an input event device attached to he provided file handle. */
+	bool getEventInfo(int fileHandle, EventInfo& eventInfo)
+	{
+		UINT8 eventBits[1 + EV_MAX/8];
+		bs_zero_out(eventBits);
+
+		if (ioctl(fileHandle, EVIOCGBIT(0, sizeof(eventBits)), eventBits) == -1)
+			return false;
+
+		for (UINT32 i = 0; i < EV_MAX; i++)
+		{
+			if(isBitSet(eventBits, i))
+			{
+				if(i == EV_ABS)
+				{
+					UINT8 absAxisBits[1 + ABS_MAX/8];
+					bs_zero_out(absAxisBits);
+
+					if (ioctl(fileHandle, EVIOCGBIT(i, sizeof(absAxisBits)), absAxisBits) == -1)
+					{
+						LOGERR("Could not read device absolute axis features.");
+						continue;
+					}
+
+					for (UINT32 j = 0; j < ABS_MAX; j++)
+					{
+						if(isBitSet(absAxisBits, j))
+						{
+							if(j >= ABS_HAT0X && j <= ABS_HAT3Y)
+								eventInfo.hats.push_back(j);
+							else
+								eventInfo.absAxes.push_back(j);
+						}
+					}
+				}
+				else if(i == EV_REL)
+				{
+					UINT8 relAxisBits[1 + REL_MAX/8];
+					bs_zero_out(relAxisBits);
+
+					if (ioctl(fileHandle, EVIOCGBIT(i, sizeof(relAxisBits)), relAxisBits) == -1)
+					{
+						LOGERR("Could not read device relative axis features.");
+						continue;
+					}
+
+					for (UINT32 j = 0; j < REL_MAX; j++)
+					{
+						if(isBitSet(relAxisBits, j))
+							eventInfo.relAxes.push_back(j);
+					}
+				}
+				else if(i == EV_KEY)
+				{
+					UINT8 keyBits[1 + KEY_MAX/8];
+					bs_zero_out(keyBits);
+
+					if (ioctl(fileHandle, EVIOCGBIT(i, sizeof(keyBits)), keyBits) == -1)
+					{
+						LOGERR("Could not read device key features.");
+						continue;
+					}
+
+					for (UINT32 j = 0; j < KEY_MAX; j++)
+					{
+						if(isBitSet(keyBits, j))
+							eventInfo.buttons.push_back(j);
+					}
+				}
+			}
+		}
+
+		return true;
+	}
+
+	/** Converts a Linux button code to Banshee ButtonCode. */
+	ButtonCode gamepadMapCommonButton(INT32 code)
+	{
+		// Note: Assuming XBox controller layout here
+		switch (code)
+		{
+		case BTN_TRIGGER_HAPPY1:
+			return BC_GAMEPAD_DPAD_LEFT;
+		case BTN_TRIGGER_HAPPY2:
+			return BC_GAMEPAD_DPAD_RIGHT;
+		case BTN_TRIGGER_HAPPY3:
+			return BC_GAMEPAD_DPAD_UP;
+		case BTN_TRIGGER_HAPPY4:
+			return BC_GAMEPAD_DPAD_DOWN;
+		case BTN_START:
+			return BC_GAMEPAD_START;
+		case BTN_SELECT:
+			return BC_GAMEPAD_BACK;
+		case BTN_THUMBL:
+			return BC_GAMEPAD_LS;
+		case BTN_THUMBR:
+			return BC_GAMEPAD_RS;
+		case BTN_TL:
+			return BC_GAMEPAD_LB;
+		case BTN_TR:
+			return BC_GAMEPAD_RB;
+		case BTN_A:
+			return BC_GAMEPAD_A;
+		case BTN_B:
+			return BC_GAMEPAD_B;
+		case BTN_X:
+			return BC_GAMEPAD_X;
+		case BTN_Y:
+			return BC_GAMEPAD_Y;
+		}
+
+		return BC_UNASSIGNED;
+	}
+
+	/**
+	 * Maps an absolute axis as reported by the Linux system, to a Banshee axis. This will be one of the InputAxis enum
+	 * members, or -1 if it cannot be mapped.
+	 */
+	INT32 gamepadMapCommonAxis(INT32 axis)
+	{
+		switch(axis)
+		{
+		case ABS_X: return (INT32)InputAxis::LeftStickX;
+		case ABS_Y: return (INT32)InputAxis::LeftStickY;
+		case ABS_RX: return (INT32)InputAxis::RightStickX;
+		case ABS_RY: return (INT32)InputAxis::RightStickY;
+		case ABS_Z: return (INT32)InputAxis::LeftTrigger;
+		case ABS_RZ: return (INT32)InputAxis::RightTrigger;
+		}
+
+		return -1;
+	}
+
+	/**
+	 * Returns true if the input event attached to the specified file handle is a gamepad,
+	 * and populates the gamepad info structure. Returns false otherwise.
+	 */
+	bool parseGamepadInfo(int fileHandle, int eventHandlerIdx, GamepadInfo& info)
+	{
+		EventInfo eventInfo;
+		if(!getEventInfo(fileHandle, eventInfo))
+			return false;
+
+		bool isGamepad = false;
+
+		// Check for gamepad buttons
+		UINT32 unknownButtonIdx = 0;
+		for(auto& entry : eventInfo.buttons)
+		{
+			if((entry >= BTN_JOYSTICK && entry < BTN_GAMEPAD)
+				|| (entry >= BTN_GAMEPAD && entry < BTN_DIGI)
+				|| (entry >= BTN_WHEEL && entry < KEY_OK))
+			{
+				ButtonCode bc = gamepadMapCommonButton(entry);
+				if(bc == BC_UNASSIGNED)
+				{
+					// Map to unnamed buttons
+					if(unknownButtonIdx < 20)
+					{
+						bc = (ButtonCode)((INT32)BC_GAMEPAD_BTN1 + unknownButtonIdx);
+						info.buttonMap[entry] = bc;
+
+						unknownButtonIdx++;
+					}
+				}
+				else
+					info.buttonMap[entry] = bc;
+
+				isGamepad = true;
+			}
+		}
+
+		if(isGamepad)
+		{
+			info.eventHandlerIdx = eventHandlerIdx;
+
+			// Get device name
+			char name[128];
+			if (ioctl(fileHandle, EVIOCGNAME(sizeof(name)), name) != -1)
+				info.name = String(name);
+			else
+				LOGERR("Could not read device name.");
+
+			// Get axis ranges
+			UINT32 unknownAxisIdx = 0;
+			for(auto& entry : eventInfo.absAxes)
+			{
+				AxisInfo& axisInfo = info.axisMap[entry];
+				axisInfo.min = Gamepad::MIN_AXIS;
+				axisInfo.max = Gamepad::MAX_AXIS;
+
+				input_absinfo absinfo;
+				if (ioctl(fileHandle, EVIOCGABS(entry), &absinfo) == -1)
+				{
+					LOGERR("Could not read absolute axis device features.");
+					continue;
+				}
+
+				axisInfo.min = absinfo.minimum;
+				axisInfo.max = absinfo.maximum;
+
+				axisInfo.axisIdx = gamepadMapCommonAxis(entry);
+				if(axisInfo.axisIdx == -1)
+				{
+					axisInfo.axisIdx = (INT32)InputAxis::Count + unknownAxisIdx;
+					unknownAxisIdx++;
+				}
+			}
+		}
+
+		return isGamepad;
+	}
+
+	/** Checks is the event handler at the specified input file handle is a mouse. */
+	bool isMouse(int fileHandle)
+	{
+		EventInfo eventInfo;
+		if(!getEventInfo(fileHandle, eventInfo))
+			return false;
+
+		// Check for mouse buttons
+		for(auto& entry : eventInfo.buttons)
+		{
+			if((entry >= BTN_MOUSE && entry < BTN_JOYSTICK))
+				return true;
+		}
+
+		return false;
+	}
+
+	/** Checks is the event handler at the specified input file handle is a keyboard. */
+	bool isKeyboard(int fileHandle)
+	{
+		EventInfo eventInfo;
+		if(!getEventInfo(fileHandle, eventInfo))
+			return false;
+
+		// Check for keyboard keys
+		for(auto& entry : eventInfo.buttons)
+		{
+			if(entry >= KEY_ESC && entry < KEY_KPDOT)
+				return true;
+		}
+
+		return false;
+	}
+
+	void Input::initRawInput()
+	{
+		mPlatformData = bs_new<InputPrivateData>();
+
+		// Scan for valid input devices
+		for(int i = 0; i < 64; ++i )
+		{
+			String eventPath = "/dev/input/event" + toString(i);
+			int file = open(eventPath.c_str(), O_RDONLY |O_NONBLOCK);
+			if(file == -1)
+			{
+				if(errno == EACCES)
+					LOGERR("Cannot open input device " + eventPath + ". Make sure your user has valid permissions.");
+
+				continue;
+			}
+
+			GamepadInfo info;
+			if(parseGamepadInfo(file, i, info))
+			{
+				info.id = (UINT32)mPlatformData->gamepadInfos.size();
+				mPlatformData->gamepadInfos.push_back(info);
+			}
+			else if(isKeyboard(file))
+				mPlatformData->keyboards.push_back(i);
+			else if(isMouse(file))
+				mPlatformData->mice.push_back(i);
+
+			close(file);
+		}
+
+		if (getDeviceCount(InputDevice::Keyboard) > 0)
+			mKeyboard = bs_new<Keyboard>("Keyboard", this);
+
+		if (getDeviceCount(InputDevice::Mouse) > 0)
+			mMouse = bs_new<Mouse>("Mouse", this);
+
+		UINT32 numGamepads = getDeviceCount(InputDevice::Gamepad);
+		for (UINT32 i = 0; i < numGamepads; i++)
+			mGamepads.push_back(bs_new<Gamepad>(mPlatformData->gamepadInfos[i].name, mPlatformData->gamepadInfos[i], this));
+	}
+
+	void Input::cleanUpRawInput()
+	{
+		if (mMouse != nullptr)
+			bs_delete(mMouse);
+
+		if (mKeyboard != nullptr)
+			bs_delete(mKeyboard);
+
+		for (auto& gamepad : mGamepads)
+			bs_delete(gamepad);
+
+		bs_delete(mPlatformData);
+	}
+
+	UINT32 Input::getDeviceCount(InputDevice device) const
+	{
+		switch(device)
+		{
+		case InputDevice::Keyboard: return 1;
+		case InputDevice::Mouse: return 1;
+		case InputDevice::Gamepad: return (UINT32)mPlatformData->gamepadInfos.size();
+		case InputDevice::Count: return 0;
+		}
+
+		return 0;
+	}
+}
+

+ 43 - 0
Source/BansheeCore/Linux/BsLinuxInput.h

@@ -0,0 +1,43 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsCorePrerequisites.h"
+
+namespace bs
+{
+	/** Infomation about an analog axis that's part of a gamepad. */
+	struct AxisInfo
+	{
+		INT32 axisIdx;
+		INT32 min;
+		INT32 max;
+	};
+
+	/** Information about a gamepad. */
+	struct GamepadInfo
+	{
+		UINT32 id;
+		UINT32 eventHandlerIdx;
+		String name;
+
+		UnorderedMap<INT32, ButtonCode> buttonMap;
+		UnorderedMap<INT32, AxisInfo> axisMap;
+	};
+
+	/**
+	 * Data specific to Linux implementation of the input system. Can be passed to platform specific implementations of
+	 * the individual device types.
+	 */
+	struct InputPrivateData
+	{
+		Vector<GamepadInfo> gamepadInfos;
+		Vector<INT32> mice;
+		Vector<INT32> keyboards;
+	};
+
+#define BUFFER_SIZE_GAMEPAD 64
+#define BUFFER_SIZE_KEYBOARD 128
+#define BUFFER_SIZE_MOUSE 16
+}
+

+ 99 - 0
Source/BansheeCore/Linux/BsLinuxKeyboard.cpp

@@ -0,0 +1,99 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "Input/BsKeyboard.h"
+#include "Input/BsInput.h"
+#include "Linux/BsLinuxInput.h"
+#include <fcntl.h>
+#include <linux/input.h>
+
+namespace bs
+{
+	constexpr UINT32 MAX_DEVICES = 8;
+
+	/** Contains private data for the Linux Keyboard implementation. */
+	struct Keyboard::Pimpl
+	{
+		INT32 fileHandles[MAX_DEVICES];
+	};
+
+	Keyboard::Keyboard(const String& name, Input* owner)
+		: mName(name), mOwner(owner)
+	{
+		InputPrivateData* pvtData = owner->_getPrivateData();
+
+		m = bs_new<Pimpl>();
+
+		for(UINT32 i = 0; i < MAX_DEVICES; i++)
+			m->fileHandles[i] = -1;
+
+		UINT32 idx = 0;
+		for(auto& entry : pvtData->keyboards)
+		{
+			String eventPath = "/dev/input/event" + toString(entry);
+			m->fileHandles[idx] = open(eventPath.c_str(), O_RDWR | O_NONBLOCK);
+
+			if(m->fileHandles[idx] == -1)
+				LOGERR("Failed to open input event file handle for device: " + mName);
+
+			idx++;
+
+			if(idx >= MAX_DEVICES)
+				break;
+		}
+	}
+
+	Keyboard::~Keyboard()
+	{
+		for(UINT32 i = 0; i < MAX_DEVICES; i++)
+		{
+			if(m->fileHandles[i] != -1)
+				close(m->fileHandles[i]);
+		}
+
+		bs_delete(m);
+	}
+
+	void Keyboard::capture()
+	{
+		input_event events[BUFFER_SIZE_KEYBOARD];
+		for(UINT32 i = 0; i < MAX_DEVICES; i++)
+		{
+			if(m->fileHandles[i] == -1)
+				continue;
+
+			while(true)
+			{
+				ssize_t numReadBytes = read(m->fileHandles[i], &events, sizeof(events));
+				if(numReadBytes < 0)
+					break;
+
+				UINT32 numEvents = numReadBytes / sizeof(input_event);
+				for(UINT32 j = 0; j < numEvents; ++j)
+				{
+					switch(events[j].type)
+					{
+					case EV_KEY:
+					{
+						ButtonCode bc = (ButtonCode)events[j].code;
+						if(bc < BC_MOUSE_LEFT)
+						{
+							if(events[j].value)
+								mOwner->_notifyButtonPressed(0, bc, (UINT64)events[j].time.tv_usec);
+							else
+								mOwner->_notifyButtonReleased(0, bc, (UINT64)events[j].time.tv_usec);
+						}
+					}
+						break;
+					default: break;
+					}
+				}
+			}
+		}
+	}
+
+	void Keyboard::changeCaptureContext(UINT64 windowHandle)
+	{
+		// Do nothing
+	}
+}
+

+ 162 - 0
Source/BansheeCore/Linux/BsLinuxMouse.cpp

@@ -0,0 +1,162 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "Input/BsMouse.h"
+#include "Input/BsInput.h"
+#include "Linux/BsLinuxInput.h"
+#include <fcntl.h>
+#include <linux/input.h>
+
+namespace bs
+{
+	constexpr UINT32 MAX_DEVICES = 8;
+
+	/** Contains private data for the Linux Mouse implementation. */
+	struct Mouse::Pimpl
+	{
+		INT32 fileHandles[MAX_DEVICES];
+	};
+
+	Mouse::Mouse(const String& name, Input* owner)
+		: mName(name), mOwner(owner)
+	{
+		InputPrivateData* pvtData = owner->_getPrivateData();
+
+		m = bs_new<Pimpl>();
+
+		for(UINT32 i = 0; i < MAX_DEVICES; i++)
+			m->fileHandles[i] = -1;
+
+		UINT32 idx = 0;
+		for(auto& entry : pvtData->mice)
+		{
+			String eventPath = "/dev/input/event" + toString(entry);
+			m->fileHandles[idx] = open(eventPath.c_str(), O_RDWR | O_NONBLOCK);
+
+			if(m->fileHandles[idx] == -1)
+				LOGERR("Failed to open input event file handle for device: " + mName);
+
+			idx++;
+
+			if(idx >= MAX_DEVICES)
+				break;
+		}
+	}
+
+	Mouse::~Mouse()
+	{
+		for(UINT32 i = 0; i < MAX_DEVICES; i++)
+		{
+			if(m->fileHandles[i] != -1)
+				close(m->fileHandles[i]);
+		}
+
+		bs_delete(m);
+	}
+
+	void Mouse::capture()
+	{
+		INT32 relX = 0;
+		INT32 relY = 0;
+		INT32 relZ = 0;
+
+		input_event events[BUFFER_SIZE_MOUSE];
+		for(UINT32 i = 0; i < MAX_DEVICES; i++)
+		{
+			if(m->fileHandles[i] == -1)
+				continue;
+
+			while(true)
+			{
+				ssize_t numReadBytes = read(m->fileHandles[i], &events, sizeof(events));
+				if(numReadBytes < 0)
+					break;
+
+				UINT32 numEvents = numReadBytes / sizeof(input_event);
+				for(UINT32 j = 0; j < numEvents; ++j)
+				{
+					switch(events[j].type)
+					{
+					case EV_KEY:
+					{
+						ButtonCode bc = BC_UNASSIGNED;
+
+						// Unnamed mouse buttons
+						if(events[j].code >= BTN_MISC && events[j].code < BTN_MOUSE)
+							bc = (ButtonCode)((INT32)BC_MOUSE_BTN4 + (events[j].code - BTN_MISC));
+						// Named mouse buttons
+						else if(events[j].code >= BTN_MOUSE && events[j].code < BTN_JOYSTICK)
+						{
+							switch (events[j].code)
+							{
+							case BTN_LEFT:
+								bc = BC_MOUSE_LEFT;
+								break;
+							case BTN_MIDDLE:
+								bc = BC_MOUSE_MIDDLE;
+								break;
+							case BTN_RIGHT:
+								bc = BC_MOUSE_RIGHT;
+								break;
+							case BTN_SIDE:
+								bc = BC_MOUSE_BTN15;
+								break;
+							case BTN_EXTRA:
+								bc = BC_MOUSE_BTN16;
+								break;
+							case BTN_FORWARD:
+								bc = BC_MOUSE_BTN17;
+								break;
+							case BTN_BACK:
+								bc = BC_MOUSE_BTN18;
+								break;
+							case BTN_TASK:
+								bc = BC_MOUSE_BTN19;
+								break;
+							default:
+								break;
+							}
+						}
+
+						if(bc != BC_UNASSIGNED)
+						{
+							if (events[j].value)
+								mOwner->_notifyButtonPressed(0, bc, (UINT64) events[j].time.tv_usec);
+							else
+								mOwner->_notifyButtonReleased(0, bc, (UINT64) events[j].time.tv_usec);
+						}
+					}
+						break;
+					case EV_REL:
+					{
+						switch(events[j].code)
+						{
+						case REL_X:
+							relX = events[j].value;
+							break;
+						case REL_Y:
+							relY = events[i].value;
+							break;
+						case REL_WHEEL:
+							relZ = events[i].value;
+							break;
+						default:
+							break;
+						}
+						break;
+					}
+					default: break;
+					}
+				}
+			}
+		}
+
+		if(relX != 0 || relY != 0 || relZ != 0)
+			mOwner->_notifyMouseMoved(relX, relY, relZ);
+	}
+
+	void Mouse::changeCaptureContext(UINT64 windowHandle)
+	{
+		// Do nothing
+	}
+}
+

+ 1 - 1
Source/BansheeCore/RenderAPI/BsViewport.h

@@ -19,7 +19,7 @@ namespace bs
 	/** Flags that determine which portion of the viewport to clear. */
 	/** Flags that determine which portion of the viewport to clear. */
 	enum BS_SCRIPT_EXPORT(n:ClearFlags) class ClearFlagBits
 	enum BS_SCRIPT_EXPORT(n:ClearFlags) class ClearFlagBits
 	{
 	{
-		None,
+		Empty,
 		Color = 1 << 0,
 		Color = 1 << 0,
 		Depth = 1 << 1,
 		Depth = 1 << 1,
 		Stencil = 1 << 2
 		Stencil = 1 << 2

+ 14 - 5
Source/BansheeCore/Win32/BsWin32Gamepad.cpp

@@ -238,13 +238,14 @@ namespace bs
 			AxisState axisState[6];
 			AxisState axisState[6];
 			bs_zero_out(axisState);
 			bs_zero_out(axisState);
 
 
+			// Note: Order of axes must match InputAxis enum
 			// Left stick
 			// Left stick
-			axisState[0].value = -(int)inputState.Gamepad.sThumbLY;
-			axisState[1].value = (int)inputState.Gamepad.sThumbLX;
+			axisState[0].value = (int)inputState.Gamepad.sThumbLX;
+			axisState[1].value = -(int)inputState.Gamepad.sThumbLY;
 
 
 			// Right stick 
 			// Right stick 
-			axisState[2].value = -(int)inputState.Gamepad.sThumbRY;
-			axisState[3].value = (int)inputState.Gamepad.sThumbRX;
+			axisState[2].value = (int)inputState.Gamepad.sThumbRX;
+			axisState[3].value = -(int)inputState.Gamepad.sThumbRY;
 
 
 			// Left trigger
 			// Left trigger
 			axisState[4].value = std::min((int)inputState.Gamepad.bLeftTrigger * 129, MAX_AXIS);
 			axisState[4].value = std::min((int)inputState.Gamepad.bLeftTrigger * 129, MAX_AXIS);
@@ -314,6 +315,14 @@ namespace bs
 					m->buttonState[i] = buttonState;
 					m->buttonState[i] = buttonState;
 				}
 				}
 			}
 			}
+
+			for (int i = 0; i < 6; ++i)
+			{
+				if (!axisState[i].moved)
+					continue;
+
+				mOwner->_notifyAxisMoved(m->info.id, i + (int)InputAxis::MouseZ, axisState[i].value);
+			}
 		}
 		}
 		else // DirectInput
 		else // DirectInput
 		{
 		{
@@ -393,7 +402,7 @@ namespace bs
 				{
 				{
 					if (!axisState[i].moved)
 					if (!axisState[i].moved)
 						continue;
 						continue;
-					
+
 					mOwner->_notifyAxisMoved(m->info.id, i + (int)InputAxis::MouseZ, axisState[i].value);
 					mOwner->_notifyAxisMoved(m->info.id, i + (int)InputAxis::MouseZ, axisState[i].value);
 				}
 				}
 			}
 			}

+ 1 - 1
Source/Examples/ExampleGettingStarted/Main.cpp

@@ -399,7 +399,7 @@ namespace bs
 		guiCamera->setLayers(0);
 		guiCamera->setLayers(0);
 
 
 		// Don't clear this camera as that would clear anything the main camera has rendered.
 		// Don't clear this camera as that would clear anything the main camera has rendered.
-		guiCamera->getViewport()->setClearFlags(ClearFlagBits::None);
+		guiCamera->getViewport()->setClearFlags(ClearFlagBits::Empty);
 
 
 		// Add a GUIWidget, the top-level GUI component, parent to all GUI elements. GUI widgets
 		// Add a GUIWidget, the top-level GUI component, parent to all GUI elements. GUI widgets
 		// require you to specify a viewport that they will output rendered GUI elements to.
 		// require you to specify a viewport that they will output rendered GUI elements to.