mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
2495 lines
90 KiB
C++
2495 lines
90 KiB
C++
//
|
|
// UserInputService.cpp
|
|
// App
|
|
//
|
|
// Created by Ben Tkacheff on 8/28/12.
|
|
//
|
|
//
|
|
#include "stdafx.h"
|
|
|
|
#include "V8DataModel/UserInputService.h"
|
|
#include "V8DataModel/Bindable.h"
|
|
#include "V8DataModel/Workspace.h"
|
|
#include "V8DataModel/Filters.h"
|
|
#include "V8DataModel/ClickDetector.h"
|
|
#include "V8DataModel/MouseCommand.h"
|
|
#include "V8DataModel/TextBox.h"
|
|
#include "V8DataModel/PlayerGui.h"
|
|
#include "v8datamodel/UserController.h"
|
|
#include "v8datamodel/Frame.h"
|
|
#include "v8datamodel/GamepadService.h"
|
|
#include "v8datamodel/TouchInputService.h"
|
|
#include "V8World/World.h"
|
|
#include "V8World/ContactManager.h"
|
|
#include "v8datamodel/ScreenGui.h"
|
|
#include "reflection/object.h"
|
|
#include "Humanoid/Humanoid.h"
|
|
#include "Util/UserInputBase.h"
|
|
#include "Util/Quaternion.h"
|
|
#include "Util/NavKeys.h"
|
|
|
|
#include "rbx/Profiler.h"
|
|
|
|
#include <boost/algorithm/string.hpp>
|
|
|
|
#ifdef __APPLE__ // needed for pasting ability on OS X
|
|
#if !RBX_PLATFORM_IOS
|
|
#import <Cocoa/Cocoa.h>
|
|
#endif
|
|
#endif
|
|
|
|
LOGGROUP(UserInputProfile)
|
|
|
|
FASTFLAGVARIABLE(UseInGameTopBar, false)
|
|
FASTFLAGVARIABLE(MobileToggleChatVisibleIcon, false)
|
|
DYNAMIC_FASTINTVARIABLE(MoveInGameChatToTopPlaceId, 0)
|
|
FASTFLAGVARIABLE(LuaChatPhoneFontSize, false)
|
|
|
|
DYNAMIC_FASTINTVARIABLE(LuaChatFloodCheckMessages, 7)
|
|
DYNAMIC_FASTINTVARIABLE(LuaChatFloodCheckInterval, 15)
|
|
FASTFLAGVARIABLE(LuaChatFiltering, false)
|
|
|
|
DYNAMIC_FASTFLAGVARIABLE(GetFocusedTextBoxEnabled, false)
|
|
|
|
FASTFLAGVARIABLE(FlyCamOnRenderStep, false)
|
|
|
|
FASTFLAGVARIABLE(PlayerDropDownEnabled, false)
|
|
|
|
DYNAMIC_FASTFLAGVARIABLE(EnableShowStatsLua, false)
|
|
|
|
FASTFLAGVARIABLE(UserUseNewControlScript, false)
|
|
|
|
FASTFLAG(UserAllCamerasInLua)
|
|
|
|
namespace RBX {
|
|
|
|
namespace Reflection {
|
|
template<>
|
|
EnumDesc<UserInputService::SwipeDirection>::EnumDesc()
|
|
:EnumDescriptor("SwipeDirection")
|
|
{
|
|
addPair(UserInputService::DIRECTION_RIGHT, "Right");
|
|
addPair(UserInputService::DIRECTION_LEFT, "Left");
|
|
addPair(UserInputService::DIRECTION_UP, "Up");
|
|
addPair(UserInputService::DIRECTION_DOWN, "Down");
|
|
addPair(UserInputService::DIRECTION_NONE, "None");
|
|
}
|
|
|
|
template<>
|
|
EnumDesc<UserInputService::Platform>::EnumDesc()
|
|
:EnumDescriptor("Platform")
|
|
{
|
|
addPair(UserInputService::PLATFORM_WINDOWS, "Windows");
|
|
addPair(UserInputService::PLATFORM_OSX, "OSX");
|
|
addPair(UserInputService::PLATFORM_IOS, "IOS");
|
|
addPair(UserInputService::PLATFORM_ANDROID, "Android");
|
|
addPair(UserInputService::PLATFORM_XBOXONE, "XBoxOne");
|
|
addPair(UserInputService::PLATFORM_PS4, "PS4");
|
|
addPair(UserInputService::PLATFORM_PS3, "PS3");
|
|
addPair(UserInputService::PLATFORM_XBOX360, "XBox360");
|
|
addPair(UserInputService::PLATFORM_WIIU, "WiiU");
|
|
addPair(UserInputService::PLATFORM_NX, "NX");
|
|
addPair(UserInputService::PLATFORM_OUYA, "Ouya");
|
|
addPair(UserInputService::PLATFORM_ANDROIDTV, "AndroidTV");
|
|
addPair(UserInputService::PLATFORM_CHROMECAST, "Chromecast");
|
|
addPair(UserInputService::PLATFORM_LINUX, "Linux");
|
|
addPair(UserInputService::PLATFORM_STEAMOS, "SteamOS");
|
|
addPair(UserInputService::PLATFORM_WEBOS, "WebOS");
|
|
addPair(UserInputService::PLATFORM_DOS, "DOS");
|
|
addPair(UserInputService::PLATFORM_BEOS, "BeOS");
|
|
addPair(UserInputService::PLATFORM_UWP, "UWP");
|
|
addPair(UserInputService::PLATFORM_NONE, "None");
|
|
}
|
|
|
|
template<>
|
|
EnumDesc<UserInputService::MouseType>::EnumDesc()
|
|
:EnumDescriptor("MouseBehavior")
|
|
{
|
|
addPair(UserInputService::MOUSETYPE_DEFAULT, "Default");
|
|
addPair(UserInputService::MOUSETYPE_LOCKCENTER, "LockCenter");
|
|
addPair(UserInputService::MOUSETYPE_LOCKCURRENT, "LockCurrentPosition");
|
|
}
|
|
|
|
template<>
|
|
EnumDesc<UserInputService::OverrideMouseIconBehavior>::EnumDesc()
|
|
:EnumDescriptor("OverrideMouseIconBehavior")
|
|
{
|
|
addPair(UserInputService::OVERRIDE_BEHAVIOR_NONE, "None");
|
|
addPair(UserInputService::OVERRIDE_BEHAVIOR_FORCESHOW, "ForceShow");
|
|
addPair(UserInputService::OVERRIDE_BEHAVIOR_FORCEHIDE, "ForceHide");
|
|
}
|
|
|
|
template<>
|
|
EnumDesc<UserInputService::UserCFrame>::EnumDesc()
|
|
:EnumDescriptor("UserCFrame")
|
|
{
|
|
addPair(UserInputService::USERCFRAME_HEAD, "Head");
|
|
addPair(UserInputService::USERCFRAME_LEFTHAND, "LeftHand");
|
|
addPair(UserInputService::USERCFRAME_RIGHTHAND, "RightHand");
|
|
}
|
|
|
|
template<>
|
|
UserInputService::UserCFrame& Variant::convert<UserInputService::UserCFrame>(void)
|
|
{
|
|
return genericConvert<UserInputService::UserCFrame>();
|
|
}
|
|
} // namespace Reflection
|
|
|
|
template<>
|
|
bool StringConverter<UserInputService::UserCFrame>::convertToValue(const std::string& text, UserInputService::UserCFrame& value)
|
|
{
|
|
return Reflection::EnumDesc<UserInputService::UserCFrame>::singleton().convertToValue(text.c_str(), value);
|
|
}
|
|
|
|
const char* const sUserInputService = "UserInputService";
|
|
|
|
boost::mutex UserInputService::InputEventsMutex;
|
|
|
|
// turn this on to debug touch events
|
|
// will draw a square on the screen for each touch event
|
|
bool UserInputService::DrawTouchEvents = false;
|
|
RBX::Color3 UserInputService::DrawTouchColor = RBX::Color3(1,0,0);
|
|
RBX::Color3 UserInputService::DrawMoveColor = RBX::Color3(0,1,0);
|
|
|
|
REFLECTION_BEGIN();
|
|
// ROBLOX Only functions/events/props
|
|
static Reflection::BoundFuncDesc<UserInputService, UserInputService::Platform(void)> func_getPlatform(&UserInputService::getPlatformLua, "GetPlatform", Security::RobloxScript);
|
|
|
|
static Reflection::BoundFuncDesc<UserInputService, InputObject::UserInputType()> func_getLastInputType(&UserInputService::getLastInputType, "GetLastInputType", Security::None);
|
|
static Reflection::EventDesc<UserInputService, void(InputObject::UserInputType)> event_lastInputTypeChanged(&UserInputService::lastInputTypeChangedSignal, "LastInputTypeChanged", "lastInputType", Security::None);
|
|
|
|
static const Reflection::PropDescriptor<UserInputService, bool> prop_TouchEnabled("TouchEnabled", category_Data, &UserInputService::getTouchEnabled, NULL);
|
|
static const Reflection::PropDescriptor<UserInputService, bool> prop_KeyboardEnabled("KeyboardEnabled", category_Data, &UserInputService::getKeyboardEnabled, NULL);
|
|
static const Reflection::PropDescriptor<UserInputService, bool> prop_MouseEnabled("MouseEnabled", category_Data, &UserInputService::getMouseEnabled, NULL);
|
|
static const Reflection::PropDescriptor<UserInputService, bool> prop_GamepadEnabled("GamepadEnabled", category_Data, &UserInputService::getGamepadEnabled, NULL);
|
|
|
|
//todo: Remove these events/functions
|
|
static Reflection::EventDesc<UserInputService, void()> event_JumpRequest(&UserInputService::jumpRequestEvent, "JumpRequest", Security::None);
|
|
Reflection::PropDescriptor<UserInputService, bool> UserInputService::prop_ModalEnabled("ModalEnabled", category_Behavior, &UserInputService::getModalEnabled, &UserInputService::setModalEnabled, Reflection::PropertyDescriptor::SCRIPTING, Security::None);
|
|
|
|
// High Level touch events
|
|
static Reflection::EventDesc<UserInputService, void(shared_ptr<const Reflection::ValueArray>, bool)> event_TapGesture(&UserInputService::tapGestureEvent, "TouchTap", "touchPositions", "gameProcessedEvent", Security::None);
|
|
static Reflection::EventDesc<UserInputService, void(shared_ptr<const Reflection::ValueArray>,float,float,InputObject::UserInputState, bool)> event_PinchGesture(&UserInputService::pinchGestureEvent, "TouchPinch","touchPositions", "scale", "velocity", "state","gameProcessedEvent", Security::None);
|
|
static Reflection::EventDesc<UserInputService, void(UserInputService::SwipeDirection, int, bool)> event_SwipeGesture(&UserInputService::swipeGestureEvent, "TouchSwipe", "swipeDirection", "numberOfTouches", "gameProcessedEvent", Security::None);
|
|
static Reflection::EventDesc<UserInputService, void(shared_ptr<const Reflection::ValueArray>, InputObject::UserInputState, bool)> event_LongPressGesture(&UserInputService::longPressGestureEvent, "TouchLongPress", "touchPositions", "state", "gameProcessedEvent", Security::None);
|
|
static Reflection::EventDesc<UserInputService, void(shared_ptr<const Reflection::ValueArray>, float,float,InputObject::UserInputState, bool)> event_RotateGesture(&UserInputService::rotateGestureEvent, "TouchRotate", "touchPositions", "rotation", "velocity", "state", "gameProcessedEvent", Security::None);
|
|
static Reflection::EventDesc<UserInputService, void(shared_ptr<const Reflection::ValueArray>, RBX::Vector2,RBX::Vector2,InputObject::UserInputState, bool)> event_PanGesture(&UserInputService::panGestureEvent, "TouchPan", "touchPositions", "totalTranslation", "velocity", "state", "gameProcessedEvent", Security::None);
|
|
|
|
// Low Level touch events
|
|
static Reflection::EventDesc<UserInputService,
|
|
void(shared_ptr<Instance>, bool),
|
|
rbx::signal<void(shared_ptr<Instance>, bool)>,
|
|
rbx::signal<void(shared_ptr<Instance>, bool)>* (UserInputService::*)(bool)> event_TouchStarted(&UserInputService::getTouchBeganEvent, "TouchStarted", "touch", "gameProcessedEvent", Security::None);
|
|
static Reflection::EventDesc<UserInputService,
|
|
void(shared_ptr<Instance>, bool),
|
|
rbx::signal<void(shared_ptr<Instance>, bool)>,
|
|
rbx::signal<void(shared_ptr<Instance>, bool)>* (UserInputService::*)(bool)> event_TouchMoved(&UserInputService::getTouchChangedEvent, "TouchMoved", "touch", "gameProcessedEvent", Security::None);
|
|
static Reflection::EventDesc<UserInputService,
|
|
void(shared_ptr<Instance>, bool),
|
|
rbx::signal<void(shared_ptr<Instance>, bool)>,
|
|
rbx::signal<void(shared_ptr<Instance>, bool)>* (UserInputService::*)(bool)> event_TouchEnded(&UserInputService::getTouchEndedEvent, "TouchEnded", "touch", "gameProcessedEvent", Security::None);
|
|
|
|
// Low Level Generic input events
|
|
Reflection::EventDesc<UserInputService,
|
|
void(shared_ptr<Instance>, bool),
|
|
rbx::signal<void(shared_ptr<Instance>, bool)>,
|
|
rbx::signal<void(shared_ptr<Instance>, bool)>* (UserInputService::*)(bool)> UserInputService::event_InputBegin(&UserInputService::getInputBeganEvent, "InputBegan", "input", "gameProcessedEvent", Security::None);
|
|
Reflection::EventDesc<UserInputService,
|
|
void(shared_ptr<Instance>, bool),
|
|
rbx::signal<void(shared_ptr<Instance>, bool)>,
|
|
rbx::signal<void(shared_ptr<Instance>, bool)>* (UserInputService::*)(bool)> UserInputService::event_InputUpdate(&UserInputService::getInputChangedEvent, "InputChanged", "input", "gameProcessedEvent", Security::None);
|
|
Reflection::EventDesc<UserInputService,
|
|
void(shared_ptr<Instance>, bool),
|
|
rbx::signal<void(shared_ptr<Instance>, bool)>,
|
|
rbx::signal<void(shared_ptr<Instance>, bool)>* (UserInputService::*)(bool)> UserInputService::event_InputEnd(&UserInputService::getInputEndedEvent, "InputEnded", "input", "gameProcessedEvent", Security::None);
|
|
|
|
// Textbox Stuff
|
|
static Reflection::EventDesc<UserInputService, void(shared_ptr<Instance>)> event_TextboxFocus(&UserInputService::textBoxGainFocus, "TextBoxFocused", "textboxFocused", Security::None);
|
|
static Reflection::EventDesc<UserInputService, void(shared_ptr<Instance>)> event_TextboxRelease(&UserInputService::textBoxReleaseFocus, "TextBoxFocusReleased", "textboxReleased", Security::None);
|
|
static Reflection::BoundFuncDesc<UserInputService, shared_ptr<Instance>()> func_getFocusedTextBox(&UserInputService::getFocusedTextBox, "GetFocusedTextBox", Security::None);
|
|
|
|
// Window Stuff
|
|
static Reflection::EventDesc<UserInputService, void()> event_windowFocused(&UserInputService::windowFocused, "WindowFocused", Security::None);
|
|
static Reflection::EventDesc<UserInputService, void()> event_windowFocusReleased(&UserInputService::windowFocusReleased, "WindowFocusReleased", Security::None);
|
|
|
|
// Mouse Stuff
|
|
static const Reflection::PropDescriptor<UserInputService, bool> prop_mouseIconEnabled("MouseIconEnabled", category_Data, &UserInputService::getMouseIconEnabled, &UserInputService::setMouseIconEnabled);
|
|
static const Reflection::EnumPropDescriptor<UserInputService, UserInputService::OverrideMouseIconBehavior> prop_overrideMouseIconBehavior("OverrideMouseIconBehavior", category_Data, &UserInputService::getOverrideMouseIconBehavior, &UserInputService::setOverrideMouseIconBehavior, Reflection::PropertyDescriptor::SCRIPTING, Security::RobloxScript);
|
|
static const Reflection::EnumPropDescriptor<UserInputService, UserInputService::MouseType> prop_mouseType("MouseBehavior", category_Data, &UserInputService::getMouseType, &UserInputService::setMouseType);
|
|
|
|
// Keyboard Stuff
|
|
static Reflection::BoundFuncDesc<UserInputService, shared_ptr<const Reflection::ValueArray>()> func_getKeyboardState(&UserInputService::getKeyboardState, "GetKeysPressed", Security::None);
|
|
static Reflection::BoundFuncDesc<UserInputService, bool(RBX::KeyCode)> func_isKeyCodeDown(&UserInputService::isKeyDown, "IsKeyDown", "keyCode", Security::None);
|
|
|
|
// Motion Stuff
|
|
static Reflection::EventDesc<UserInputService,
|
|
void(shared_ptr<Instance>),
|
|
rbx::signal<void(shared_ptr<Instance>)>,
|
|
rbx::signal<void(shared_ptr<Instance>)>* (UserInputService::*)(bool)> event_AccelerometerChanged(&UserInputService::getOrCreateScriptAccelerometerEventSignal, "DeviceAccelerationChanged","acceleration", Security::None);
|
|
static Reflection::EventDesc<UserInputService,
|
|
void(shared_ptr<Instance>),
|
|
rbx::signal<void(shared_ptr<Instance>)>,
|
|
rbx::signal<void(shared_ptr<Instance>)>* (UserInputService::*)(bool)> event_gravityChanged(&UserInputService::getOrCreateScriptGravityEventSignal, "DeviceGravityChanged","gravity", Security::None);
|
|
static Reflection::EventDesc<UserInputService,
|
|
void(shared_ptr<Instance>, CoordinateFrame),
|
|
rbx::signal<void(shared_ptr<Instance>, CoordinateFrame)>,
|
|
rbx::signal<void(shared_ptr<Instance>, CoordinateFrame)>* (UserInputService::*)(bool)> event_GyroChanged(&UserInputService::getOrCreateScriptGyroEventSignal, "DeviceRotationChanged","rotation","cframe", Security::None);
|
|
|
|
static Reflection::BoundFuncDesc<UserInputService, shared_ptr<Instance>(void)> func_getCurrentAcceleration(&UserInputService::getAcceleration, "GetDeviceAcceleration", Security::None);
|
|
static Reflection::BoundFuncDesc<UserInputService, shared_ptr<Instance>(void)> func_getCurrentGravity(&UserInputService::getGravity, "GetDeviceGravity", Security::None);
|
|
static Reflection::BoundFuncDesc<UserInputService, shared_ptr<const Reflection::Tuple>(void)> func_getCurrentRotation(&UserInputService::getRotation, "GetDeviceRotation", Security::None);
|
|
|
|
static const Reflection::PropDescriptor<UserInputService, bool> prop_accelerometerEnabled("AccelerometerEnabled", category_Data, &UserInputService::getAccelerometerEnabled, NULL);
|
|
static const Reflection::PropDescriptor<UserInputService, bool> prop_gyroscopeEnabled("GyroscopeEnabled", category_Data, &UserInputService::getGyroscopeEnabled, NULL);
|
|
|
|
// Gamepad Stuff
|
|
static Reflection::BoundFuncDesc<UserInputService, shared_ptr<const Reflection::ValueArray>()> func_getConnectedGamepads(&UserInputService::getConnectedGamepads, "GetConnectedGamepads", Security::None);
|
|
static Reflection::BoundFuncDesc<UserInputService, bool(InputObject::UserInputType)> func_getGamepadConnected(&UserInputService::getGamepadConnected, "GetGamepadConnected", "gamepadNum", Security::None);
|
|
static Reflection::EventDesc<UserInputService, void(InputObject::UserInputType)> event_gamepadConnected(&UserInputService::gamepadDisconnectedSignal, "GamepadDisconnected", "gamepadNum", Security::None);
|
|
static Reflection::EventDesc<UserInputService, void(InputObject::UserInputType)> event_gamepadDisconnected(&UserInputService::gamepadConnectedSignal, "GamepadConnected", "gamepadNum", Security::None);
|
|
|
|
static Reflection::BoundFuncDesc<UserInputService, shared_ptr<const Reflection::ValueArray>(InputObject::UserInputType)> func_getGamepadState(&UserInputService::getGamepadState, "GetGamepadState", "gamepadNum", Security::None);
|
|
static Reflection::BoundFuncDesc<UserInputService, bool(InputObject::UserInputType, RBX::KeyCode)> func_getgamepadSupportsKeyCode(&UserInputService::gamepadSupports, "GamepadSupports", "gamepadNum", "gamepadKeyCode", Security::None);
|
|
static Reflection::BoundFuncDesc<UserInputService, shared_ptr<const Reflection::ValueArray>(InputObject::UserInputType)> func_getSupportedGamepadKeyCodes(&UserInputService::getSupportedGamepadKeyCodes, "GetSupportedGamepadKeyCodes", "gamepadNum", Security::None);
|
|
|
|
static Reflection::BoundFuncDesc<UserInputService, shared_ptr<const Reflection::ValueArray>()> func_getNavigationGamepads(&UserInputService::getNavigationGamepads, "GetNavigationGamepads", Security::None);
|
|
static Reflection::BoundFuncDesc<UserInputService, bool(InputObject::UserInputType)> func_isNavigationGamepad(&UserInputService::isNavigationGamepad, "IsNavigationGamepad", "gamepadEnum", Security::None);
|
|
static Reflection::BoundFuncDesc<UserInputService, void(InputObject::UserInputType, bool)> func_setNavigationGamepad(&UserInputService::setNavigationGamepad, "SetNavigationGamepad", "gamepadEnum", "enabled", Security::None);
|
|
|
|
// VR Stuff
|
|
static const Reflection::PropDescriptor<UserInputService, bool> prop_IsVREnabled("IsVREnabled", category_Data, &UserInputService::getVREnabled, NULL, Reflection::PropertyDescriptor::Attributes::deprecated());
|
|
static const Reflection::PropDescriptor<UserInputService, CoordinateFrame> prop_UserHeadCFrame("UserHeadCFrame", category_Data, &UserInputService::getUserHeadCFrame, NULL, Reflection::PropertyDescriptor::Attributes::deprecated());
|
|
|
|
static const Reflection::PropDescriptor<UserInputService, bool> prop_VREnabled("VREnabled", category_Data, &UserInputService::getVREnabled, NULL);
|
|
static const Reflection::BoundFuncDesc<UserInputService, void()> func_RecenterUserHeadCFrame(&UserInputService::recenterUserHeadCFrame, "RecenterUserHeadCFrame", Security::None);
|
|
static const Reflection::BoundFuncDesc<UserInputService, CoordinateFrame(UserInputService::UserCFrame)> func_GetUserCFrame(&UserInputService::getUserCFrameLua, "GetUserCFrame", "type", Security::None);
|
|
static const Reflection::EventDesc<UserInputService, void(UserInputService::UserCFrame, CoordinateFrame value)> event_UserCFrameChanged(&UserInputService::userCFrameChanged, "UserCFrameChanged", "type", "value", Security::None);
|
|
|
|
REFLECTION_END();
|
|
|
|
bool UserInputService::isStudioEmulatingMobile = false;
|
|
|
|
UserInputService::UserInputService() :
|
|
isJump(false),
|
|
walkDirection(Vector2::zero()),
|
|
lastWalkDirection(Vector2::zero()),
|
|
maxWalkDelta(0.0f),
|
|
touchEnabled(false),
|
|
keyboardEnabled(false),
|
|
mouseEnabled(false),
|
|
gamepadEnabled(false),
|
|
accelerometerEnabled(false),
|
|
gyroscopeEnabled(false),
|
|
localCharacterJumpEnabled(true),
|
|
wrapMode(WRAP_AUTO),
|
|
modalEnabled(false),
|
|
cameraZoom(0),
|
|
cameraPanDelta(Vector2::zero()),
|
|
cameraMouseTrack(Vector2::zero()),
|
|
cameraMouseWrap(Vector2::zero()),
|
|
mouseIconEnabled(true),
|
|
mouseType(MOUSETYPE_DEFAULT),
|
|
shouldFireAccelerationEvent(false),
|
|
shouldFireGravityEvent(false),
|
|
shouldFireRotationEvent(false),
|
|
capsLocked(false),
|
|
overrideMouseIconBehavior(OVERRIDE_BEHAVIOR_NONE),
|
|
studioCamFlySteps(0),
|
|
vrEnabled(false),
|
|
recenterUserHeadCFrameRequested(false)
|
|
{
|
|
switch (getPlatform())
|
|
{
|
|
// desktop
|
|
case UserInputService::PLATFORM_WINDOWS:
|
|
case UserInputService::PLATFORM_OSX:
|
|
{
|
|
lastInputType = InputObject::TYPE_MOUSEMOVEMENT;
|
|
setMouseEnabled(true);
|
|
setKeyboardEnabled(true);
|
|
break;
|
|
}
|
|
// mobile
|
|
case UserInputService::PLATFORM_IOS:
|
|
case UserInputService::PLATFORM_ANDROID:
|
|
{
|
|
lastInputType = InputObject::TYPE_TOUCH;
|
|
setTouchEnabled(true);
|
|
break;
|
|
}
|
|
// console
|
|
case UserInputService::PLATFORM_XBOXONE:
|
|
{
|
|
lastInputType = InputObject::TYPE_GAMEPAD1;
|
|
setGamepadEnabled(true);
|
|
break;
|
|
}
|
|
case UserInputService::PLATFORM_NONE:
|
|
default:
|
|
{
|
|
lastInputType = InputObject::TYPE_NONE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
modPairs.push_back(std::make_pair(SDLK_LCTRL, KMOD_LCTRL));
|
|
modPairs.push_back(std::make_pair(SDLK_RCTRL, KMOD_RCTRL));
|
|
#elif __APPLE__
|
|
modPairs.push_back(std::make_pair(SDLK_LMETA, KMOD_LMETA));
|
|
modPairs.push_back(std::make_pair(SDLK_RMETA, KMOD_RMETA));
|
|
#endif
|
|
modPairs.push_back(std::make_pair(SDLK_LSHIFT, KMOD_LSHIFT));
|
|
modPairs.push_back(std::make_pair(SDLK_RSHIFT, KMOD_RSHIFT));
|
|
modPairs.push_back(std::make_pair(SDLK_CAPSLOCK, KMOD_CAPS));
|
|
|
|
#ifdef RBX_TEST_BUILD
|
|
keyboardEnabled = true; // used for VirtualUser
|
|
#endif
|
|
}
|
|
|
|
bool UserInputService::IsUsingNewKeyboardEvents()
|
|
{
|
|
#ifdef RBX_PLATFORM_UWP
|
|
return true;
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
void UserInputService::onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
|
|
{
|
|
if (DFFlag::GetFocusedTextBoxEnabled)
|
|
{
|
|
if(oldProvider)
|
|
{
|
|
textboxFocusBeganConnection.disconnect();
|
|
textboxFocusEndedConnection.disconnect();
|
|
}
|
|
}
|
|
Super::onServiceProvider(oldProvider, newProvider);
|
|
|
|
if (newProvider)
|
|
{
|
|
if(DrawTouchEvents)
|
|
{
|
|
drawTouchFrame = Creatable<Instance>::create<RBX::Frame>();
|
|
drawTouchFrame->setBorderSizePixel(0);
|
|
drawTouchFrame->setSize(RBX::UDim2(0,4,0,4));
|
|
}
|
|
|
|
if (DFFlag::GetFocusedTextBoxEnabled)
|
|
{
|
|
textboxFocusBeganConnection = textBoxGainFocus.connect(boost::bind(&UserInputService::textboxFocused, this, _1));
|
|
textboxFocusEndedConnection = textBoxReleaseFocus.connect(boost::bind(&UserInputService::textboxFocusReleased, this, _1));
|
|
}
|
|
|
|
rotationInputObject = RBX::Creatable<Instance>::create<InputObject>(InputObject::TYPE_GYRO, InputObject::INPUT_STATE_CHANGE, Vector3(0,0,0), SDLK_UNKNOWN, RBX::DataModel::get(newProvider));
|
|
accelerationInputObject = RBX::Creatable<Instance>::create<InputObject>(InputObject::TYPE_ACCELEROMETER, InputObject::INPUT_STATE_CHANGE, Vector3(0,0,0), SDLK_UNKNOWN, RBX::DataModel::get(newProvider));
|
|
gravityInputObject = RBX::Creatable<Instance>::create<InputObject>(InputObject::TYPE_ACCELEROMETER, InputObject::INPUT_STATE_CHANGE, Vector3(0,0,0), SDLK_UNKNOWN, RBX::DataModel::get(newProvider));
|
|
rotationCFrame = CoordinateFrame(Vector3(0,0,0));
|
|
|
|
for (int i = InputObject::TYPE_GAMEPAD1; i <= InputObject::TYPE_GAMEPAD8; i++)
|
|
{
|
|
connectedGamepadsMap[(InputObject::UserInputType) i] = false;
|
|
}
|
|
|
|
fakeMouseEventsMap[InputObject::TYPE_MOUSEMOVEMENT] = RBX::Creatable<RBX::Instance>::create<RBX::InputObject>(RBX::InputObject::TYPE_MOUSEMOVEMENT,
|
|
RBX::InputObject::INPUT_STATE_CHANGE,
|
|
RBX::Vector3(-1,-1,0),
|
|
RBX::Vector3(0,0,0),
|
|
RBX::DataModel::get(newProvider));
|
|
fakeMouseEventsMap[InputObject::TYPE_MOUSEMOVEMENT]->setSourceUserInputType(RBX::InputObject::TYPE_TOUCH);
|
|
|
|
fakeMouseEventsMap[InputObject::TYPE_MOUSEBUTTON1] = RBX::Creatable<RBX::Instance>::create<RBX::InputObject>(RBX::InputObject::TYPE_MOUSEBUTTON1,
|
|
RBX::InputObject::INPUT_STATE_BEGIN,
|
|
RBX::Vector3(-1,-1,0),
|
|
RBX::Vector3(0,0,0),
|
|
RBX::DataModel::get(newProvider));
|
|
fakeMouseEventsMap[InputObject::TYPE_MOUSEBUTTON1]->setSourceUserInputType(RBX::InputObject::TYPE_TOUCH);
|
|
|
|
currentMousePosition = RBX::Creatable<Instance>::create<InputObject>(InputObject::TYPE_MOUSEMOVEMENT, InputObject::INPUT_STATE_CANCEL, Vector3(-10000,-10000,0), SDLK_UNKNOWN, RBX::DataModel::get(newProvider));
|
|
}
|
|
}
|
|
bool UserInputService::isTenFootInterface()
|
|
{
|
|
UserInputService::Platform platform = getPlatform();
|
|
|
|
return (platform == PLATFORM_XBOXONE) || (platform == PLATFORM_PS4) || (platform == PLATFORM_PS3) || (platform == PLATFORM_XBOX360)
|
|
|| (platform == PLATFORM_WIIU) || (platform == PLATFORM_OUYA) || (platform == PLATFORM_ANDROIDTV) || (platform == PLATFORM_STEAMOS);
|
|
}
|
|
rbx::signal<void(shared_ptr<Instance>, bool)>* UserInputService::getInputBeganEvent(bool whatever)
|
|
{
|
|
return RBX::Security::Context::current().hasPermission(RBX::Security::RobloxScript) ? &coreInputBeganEvent : &inputBeganEvent;
|
|
}
|
|
rbx::signal<void(shared_ptr<Instance>, bool)>* UserInputService::getInputChangedEvent(bool whatever)
|
|
{
|
|
return RBX::Security::Context::current().hasPermission(RBX::Security::RobloxScript) ? &coreInputUpdatedEvent : &inputUpdatedEvent;
|
|
}
|
|
rbx::signal<void(shared_ptr<Instance>, bool)>* UserInputService::getInputEndedEvent(bool whatever)
|
|
{
|
|
return RBX::Security::Context::current().hasPermission(RBX::Security::RobloxScript) ? &coreInputEndedEvent : &inputEndedEvent;
|
|
}
|
|
|
|
rbx::signal<void(shared_ptr<Instance>, bool)>* UserInputService::getTouchBeganEvent(bool whatever)
|
|
{
|
|
return RBX::Security::Context::current().hasPermission(RBX::Security::RobloxScript) ? &coreTouchStartedEvent : &touchStartedEvent;
|
|
}
|
|
rbx::signal<void(shared_ptr<Instance>, bool)>* UserInputService::getTouchChangedEvent(bool whatever)
|
|
{
|
|
return RBX::Security::Context::current().hasPermission(RBX::Security::RobloxScript) ? &coreTouchMovedEvent : &touchMovedEvent;
|
|
}
|
|
rbx::signal<void(shared_ptr<Instance>, bool)>* UserInputService::getTouchEndedEvent(bool whatever)
|
|
{
|
|
return RBX::Security::Context::current().hasPermission(RBX::Security::RobloxScript) ? &coreTouchEndedEvent : &touchEndedEvent;
|
|
}
|
|
|
|
UserInputService::Platform UserInputService::getPlatform()
|
|
{
|
|
#if defined(RBX_PLATFORM_IOS)
|
|
return PLATFORM_IOS;
|
|
#elif defined(__APPLE__) && !defined(RBX_PLATFORM_IOS)
|
|
return PLATFORM_OSX;
|
|
#elif defined(RBX_PLATFORM_DURANGO)
|
|
return PLATFORM_XBOXONE;
|
|
#elif defined(RBX_PLATFORM_UWP)
|
|
return PLATFORM_UWP;
|
|
#elif defined(RBX_PLATFORM_DURANGO)
|
|
return PLATFORM_XBOXONE;
|
|
#elif defined(_WIN32) && !defined(RBX_PLATFORM_DURANGO) && !defined(RBX_PLATFORM_UWP)
|
|
return PLATFORM_WINDOWS;
|
|
#elif defined(__ANDROID__)
|
|
return PLATFORM_ANDROID;
|
|
#else
|
|
return PLATFORM_NONE;
|
|
#endif
|
|
|
|
}
|
|
|
|
void UserInputService::signalInputEventOnService(const shared_ptr<InputObject>& inputObject, const InputObject::UserInputState& inputState, bool processedEvent, bool menuIsOpen)
|
|
{
|
|
if (inputObject && inputObject.get())
|
|
{
|
|
if (!inputObject->isPublicEvent())
|
|
{
|
|
return;
|
|
}
|
|
|
|
boost::mutex::scoped_lock lock(InputEventsMutex);
|
|
|
|
InputObject::UserInputState switchState = InputObject::INPUT_STATE_NONE;
|
|
|
|
switchState = inputObject->getUserInputState();
|
|
|
|
switch (switchState)
|
|
{
|
|
case InputObject::INPUT_STATE_BEGIN:
|
|
{
|
|
coreInputBeganEvent(inputObject, processedEvent);
|
|
if (!menuIsOpen)
|
|
{
|
|
inputBeganEvent(inputObject, processedEvent);
|
|
}
|
|
|
|
if (inputObject->isTouchEvent())
|
|
{
|
|
coreTouchMovedEvent(inputObject, processedEvent);
|
|
if (!menuIsOpen)
|
|
{
|
|
touchStartedEvent(inputObject, processedEvent);
|
|
}
|
|
}
|
|
else if(inputObject->getUserInputType() == InputObject::TYPE_FOCUS)
|
|
{
|
|
windowFocused();
|
|
}
|
|
break;
|
|
}
|
|
case InputObject::INPUT_STATE_CHANGE:
|
|
{
|
|
coreInputUpdatedEvent(inputObject, processedEvent);
|
|
if (!menuIsOpen)
|
|
{
|
|
inputUpdatedEvent(inputObject, processedEvent);
|
|
}
|
|
|
|
if (inputObject->isTouchEvent())
|
|
{
|
|
coreTouchMovedEvent(inputObject, processedEvent);
|
|
if (!menuIsOpen)
|
|
{
|
|
touchMovedEvent(inputObject, processedEvent);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case InputObject::INPUT_STATE_END:
|
|
{
|
|
coreInputEndedEvent(inputObject, processedEvent);
|
|
if (!menuIsOpen)
|
|
{
|
|
inputEndedEvent(inputObject, processedEvent);
|
|
}
|
|
|
|
if (inputObject->isTouchEvent())
|
|
{
|
|
coreTouchEndedEvent(inputObject, processedEvent);
|
|
if (!menuIsOpen)
|
|
{
|
|
touchEndedEvent(inputObject, processedEvent);
|
|
}
|
|
}
|
|
else if(inputObject->getUserInputType() == InputObject::TYPE_FOCUS)
|
|
{
|
|
windowFocusReleased();
|
|
}
|
|
break;
|
|
}
|
|
case InputObject::INPUT_STATE_NONE:
|
|
default: // intentional fall-thru
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void UserInputService::doDataModelProcessInput(DataModel* dataModel, const shared_ptr<InputObject>& inputObject, void* nativeInputObject, const InputObject::UserInputState& inputState)
|
|
{
|
|
bool processedEvent = dataModel->processInputObject(inputObject);
|
|
if ( inputObject->isMouseEvent() )
|
|
{
|
|
processedMouseEvent(processedEvent, nativeInputObject, inputObject);
|
|
}
|
|
processedEventSignal(inputObject, processedEvent);
|
|
|
|
bool menuIsOpen = false;
|
|
if (GuiService* guiService = RBX::ServiceProvider::find<GuiService>(dataModel))
|
|
{
|
|
menuIsOpen = guiService->getMenuOpen();
|
|
}
|
|
|
|
signalInputEventOnService(inputObject, inputState, processedEvent, menuIsOpen);
|
|
}
|
|
|
|
void UserInputService::dataModelProcessInput(const shared_ptr<InputObject>& inputObject, void* nativeInputObject, const InputObject::UserInputState& inputState)
|
|
{
|
|
if (inputObject)
|
|
{
|
|
if (inputObject->isPublicEvent())
|
|
{
|
|
updateLastInputType(inputObject);
|
|
}
|
|
updateCurrentMousePosition(inputObject);
|
|
|
|
if (DataModel* dataModel = DataModel::get(this))
|
|
{
|
|
doDataModelProcessInput(dataModel, inputObject, nativeInputObject, inputState);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UserInputService::updateLastInputType(const shared_ptr<InputObject>& inputObject)
|
|
{
|
|
if (lastInputType != inputObject->getUserInputType())
|
|
{
|
|
if ( (inputObject->getKeyCode() == SDLK_GAMEPAD_THUMBSTICK1 || inputObject->getKeyCode() == SDLK_GAMEPAD_THUMBSTICK2 ||
|
|
inputObject->getKeyCode() == SDLK_GAMEPAD_BUTTONR2 || inputObject->getKeyCode() == SDLK_GAMEPAD_BUTTONL2) &&
|
|
inputObject->getRawPosition().magnitude() < 0.2 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
lastInputType = inputObject->getUserInputType();
|
|
lastInputTypeChangedSignal(lastInputType);
|
|
}
|
|
}
|
|
|
|
void UserInputService::updateCurrentMousePosition(const shared_ptr<InputObject>& inputObject)
|
|
{
|
|
if (lastInputType == InputObject::TYPE_MOUSEMOVEMENT && inputObject->getUserInputType() == InputObject::TYPE_MOUSEMOVEMENT)
|
|
{
|
|
currentMousePosition = inputObject;
|
|
}
|
|
else if (GamepadService::getGamepadIntForEnum(lastInputType) != -1 && GamepadService::getGamepadIntForEnum(inputObject->getUserInputType()) != -1)
|
|
{
|
|
if (Workspace* workspace = RBX::ServiceProvider::find<Workspace>(this))
|
|
{
|
|
Vector2 viewport = workspace->getCamera()->getViewport();
|
|
Vector3 mousePos(viewport.x/2.0f,viewport.y/2.0f,0);
|
|
|
|
if (RBX::Network::Players::findLocalPlayer(this))
|
|
{
|
|
DataModel* dataModel = DataModel::get(this);
|
|
if (RBX::Workspace* workspace = dataModel->getWorkspace())
|
|
{
|
|
if (Camera* camera = workspace->getCamera())
|
|
{
|
|
float distance = (camera->getCameraFocus().translation - camera->getCameraCoordinateFrame().translation).length();
|
|
if (distance > 0.55f)
|
|
{
|
|
mousePos = Vector3(viewport.x/2.0f,viewport.y/3.0f,0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
currentMousePosition = RBX::Creatable<RBX::Instance>::create<RBX::InputObject>(InputObject::TYPE_MOUSEMOVEMENT, InputObject::INPUT_STATE_CHANGE,
|
|
mousePos, Vector3::zero(), RBX::DataModel::get(this));
|
|
}
|
|
}
|
|
}
|
|
|
|
void UserInputService::processInputVector(EventsVector& eventVector, const InputObject::UserInputState& inputState)
|
|
{
|
|
EventsVector tmpEventsVector;
|
|
{
|
|
boost::mutex::scoped_lock lock(InputEventsMutex);
|
|
if (eventVector == endEventsToProcess)
|
|
{
|
|
tempEndEvents = eventVector;
|
|
}
|
|
eventVector.swap(tmpEventsVector);
|
|
}
|
|
|
|
// process important events (i.e. key press, touch start/end, etc.)
|
|
if (tmpEventsVector.size() > 0)
|
|
{
|
|
EventsVector::iterator end = tmpEventsVector.end();
|
|
EventsVector::iterator begin = tmpEventsVector.begin();
|
|
for (EventsVector::iterator iter = begin; iter != end; ++iter)
|
|
{
|
|
if(DrawTouchEvents)
|
|
{
|
|
shared_ptr<Instance> newObj = drawTouchFrame->clone(RBX::ScriptingCreator);
|
|
Frame* newFrame = Instance::fastDynamicCast<Frame>(newObj.get());
|
|
Vector2 framePos = (*iter).first->get2DPosition();
|
|
newFrame->setPosition(UDim2(0,framePos.x,0,framePos.y));
|
|
newFrame->setBackgroundColor3(DrawTouchColor);
|
|
|
|
shared_ptr<DataModel> dataModel = shared_from(DataModel::get(this));
|
|
if (CoreGuiService* cgs = dataModel->find<CoreGuiService>())
|
|
{
|
|
if (Instance* parent = cgs->findFirstChildByName("RobloxGui"))
|
|
{
|
|
newFrame->setParent(parent);
|
|
}
|
|
}
|
|
}
|
|
|
|
dataModelProcessInput((*iter).first, (*iter).second, inputState);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void UserInputService::processInputObjects()
|
|
{
|
|
DrawTouchColor = RBX::Color3(1,0,0);
|
|
|
|
// process important start events (i.e. key down, touch start, etc.)
|
|
processInputVector(beginEventsToProcess, InputObject::INPUT_STATE_BEGIN);
|
|
|
|
if (DrawTouchEvents)
|
|
{
|
|
if (DrawMoveColor == Color3(0,1,0))
|
|
{
|
|
DrawMoveColor = Color3(0,1,1);
|
|
}
|
|
else if(DrawMoveColor == Color3(0,1,1))
|
|
{
|
|
DrawMoveColor = Color3(1,1,0);
|
|
}
|
|
else
|
|
{
|
|
DrawMoveColor = Color3(0,1,0);
|
|
}
|
|
}
|
|
DrawTouchColor = DrawMoveColor;
|
|
|
|
// process all events that only matter for a frame, usually changed events (mouse move, touch move, etc.)
|
|
processInputVector(changeEventsToProcess, InputObject::INPUT_STATE_CHANGE);
|
|
|
|
DrawTouchColor = RBX::Color3(0,0,1);
|
|
|
|
// process important end events (i.e. key up, touch end, etc.)
|
|
processInputVector(endEventsToProcess, InputObject::INPUT_STATE_END);
|
|
}
|
|
|
|
void UserInputService::processCameraInternal()
|
|
{
|
|
|
|
if (FFlag::UserAllCamerasInLua)
|
|
{
|
|
if (const DataModel* dataModel = DataModel::get(this))
|
|
{
|
|
if (const RBX::Workspace* workspace = dataModel->getWorkspace())
|
|
{
|
|
if (const Camera* camera = workspace->getConstCamera())
|
|
{
|
|
if (camera->hasClientPlayer())
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!FFlag::FlyCamOnRenderStep)
|
|
{
|
|
if (cameraZoom == 0 &&
|
|
cameraPanDelta == Vector2::zero() &&
|
|
cameraMouseWrap == Vector2::zero() &&
|
|
cameraMouseTrack == Vector2::zero())
|
|
return;
|
|
}
|
|
|
|
if (DataModel* dataModel = DataModel::get(this))
|
|
{
|
|
RBX::Workspace* workspace = dataModel->getWorkspace();
|
|
if(!workspace)
|
|
return;
|
|
|
|
Camera* camera = workspace->getCamera();
|
|
if(!camera)
|
|
return;
|
|
|
|
if (FFlag::FlyCamOnRenderStep)
|
|
{
|
|
if (!RBX::Network::Players::findLocalCharacter(this) || RBX::Network::Players::isCloudEdit(this))
|
|
{
|
|
if (ControllerService* service = ServiceProvider::find<ControllerService>(this))
|
|
{
|
|
if (const UserInputBase* hardwareDevice = service->getHardwareDevice())
|
|
{
|
|
NavKeys navKeys;
|
|
if(DataModel* dm = DataModel::get(this))
|
|
hardwareDevice->getNavKeys(navKeys,dm->getSharedSuppressNavKeys());
|
|
|
|
if (navKeys.navKeyDown())
|
|
{
|
|
if (workspace->getCurrentMouseCommand())
|
|
workspace->getCamera()->doFly(navKeys, studioCamFlySteps++);
|
|
}
|
|
else
|
|
{
|
|
if(studioCamFlySteps > 0)
|
|
workspace->getCamera()->pushCameraHistoryStack();
|
|
studioCamFlySteps = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( camera->getCameraType() == Camera::CUSTOM_CAMERA &&
|
|
RBX::Network::Players::findLocalPlayer(dataModel) && !RBX::Network::Players::isCloudEdit(this))
|
|
{
|
|
cameraZoom = 0;
|
|
cameraPanDelta = Vector2::zero();
|
|
cameraMouseWrap = Vector2::zero();
|
|
cameraMouseTrack = Vector2::zero();
|
|
return;
|
|
}
|
|
|
|
if (camera->getCameraType() != Camera::LOCKED_CAMERA)
|
|
{
|
|
if (camera->getCameraPanMode() == Camera::CAMERAPANMODE_CLASSIC)
|
|
{
|
|
if (cameraPanDelta.x != 0)
|
|
camera->panRadians(cameraPanDelta.x);
|
|
if (cameraPanDelta.y != 0)
|
|
camera->tiltRadians(cameraPanDelta.y);
|
|
cameraPanDelta = Vector2::zero();
|
|
}
|
|
|
|
if (cameraZoom != 0)
|
|
{
|
|
camera->zoom(cameraZoom);
|
|
cameraZoom = 0;
|
|
}
|
|
}
|
|
|
|
if (cameraMouseTrack != Vector2::zero())
|
|
{
|
|
camera->onMouseTrack(cameraMouseTrack);
|
|
cameraMouseTrack = Vector2::zero();
|
|
}
|
|
|
|
if (cameraMouseWrap != Vector2::zero())
|
|
{
|
|
camera->onMousePan(cameraMouseWrap);
|
|
cameraMouseWrap = Vector2::zero();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void UserInputService::processToolEvents()
|
|
{
|
|
InputObjectVector toolEventsVectorTmp;
|
|
{
|
|
boost::mutex::scoped_lock lock(ToolEventsMutex);
|
|
toolEventsVectorTmp.swap(toolEventsVector);
|
|
}
|
|
|
|
if (toolEventsVectorTmp.size() > 0)
|
|
{
|
|
if(RBX::DataModel* dataModel = RBX::DataModel::get(this))
|
|
{
|
|
for(InputObjectVector::iterator iter = toolEventsVectorTmp.begin(); iter != toolEventsVectorTmp.end(); ++iter)
|
|
{
|
|
dataModel->processWorkspaceEvent(*iter);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void UserInputService::processTextboxInternal()
|
|
{
|
|
TextBoxEventsVector tmpTextboxEventsVector;
|
|
{
|
|
boost::mutex::scoped_lock lock(TextboxEventsMutex);
|
|
tmpTextboxEventsVector.swap(textboxFinishedVector);
|
|
}
|
|
|
|
if (tmpTextboxEventsVector.size() > 0)
|
|
{
|
|
for(TextBoxEventsVector::iterator iter = tmpTextboxEventsVector.begin(); iter != tmpTextboxEventsVector.end(); ++iter)
|
|
{
|
|
shared_ptr<InputObject> dummyReturnInput = RBX::Creatable<Instance>::create<InputObject>(InputObject::TYPE_KEYBOARD, InputObject::INPUT_STATE_BEGIN, Vector3::zero(), SDLK_RETURN, RBX::DataModel::get(this));
|
|
textBoxFinishedEditing((*iter).first->c_str(), (*iter).second, (*iter).second ? dummyReturnInput : shared_ptr<InputObject>());
|
|
}
|
|
}
|
|
}
|
|
|
|
void UserInputService::textboxFocused(shared_ptr<Instance> textBox)
|
|
{
|
|
currentTextBox = textBox;
|
|
}
|
|
|
|
void UserInputService::textboxFocusReleased(shared_ptr<Instance> textBox)
|
|
{
|
|
currentTextBox = shared_ptr<Instance>();
|
|
}
|
|
|
|
shared_ptr<Instance> UserInputService::getFocusedTextBox()
|
|
{
|
|
if (!DFFlag::GetFocusedTextBoxEnabled)
|
|
{
|
|
RBX::StandardOut::singleton()->printf(MESSAGE_ERROR, "UserInputService:GetFocusedTextBox() is not yet enabled");
|
|
return shared_ptr<Instance>();
|
|
}
|
|
return currentTextBox;
|
|
}
|
|
|
|
void UserInputService::cleanupCurrentTouches()
|
|
{
|
|
EventsVector::iterator end = tempEndEvents.end();
|
|
EventsVector::iterator begin = tempEndEvents.begin();
|
|
for (EventsVector::iterator iter = begin; iter != end; ++iter)
|
|
{
|
|
shared_ptr<InputObject> inputObject = (*iter).first;
|
|
if (inputObject->isTouchEvent() && inputObject->getUserInputState() == InputObject::INPUT_STATE_END)
|
|
{
|
|
for(InputObjectVector::iterator iter = currentTouches.begin(); iter != currentTouches.end(); ++iter)
|
|
{
|
|
if ((*iter) == inputObject)
|
|
{
|
|
currentTouches.erase(iter);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
tempEndEvents.clear();
|
|
}
|
|
|
|
void UserInputService::onRenderStep()
|
|
{
|
|
// allow any buffers to unload into InputObjects
|
|
updateInputSignal();
|
|
|
|
processInputObjects();
|
|
processKeyboardEvents();
|
|
processGestures();
|
|
processMotionEvents();
|
|
processToolEvents();
|
|
processCameraInternal();
|
|
processTextboxInternal();
|
|
|
|
if(fireJumpRequestEvent)
|
|
{
|
|
jumpRequestEvent();
|
|
fireJumpRequestEvent = false;
|
|
}
|
|
|
|
cleanupCurrentTouches();
|
|
|
|
if (mouseType == MOUSETYPE_LOCKCENTER)
|
|
{
|
|
if (canUseMouseLockCenter())
|
|
{
|
|
if ( ControllerService* service = ServiceProvider::find<ControllerService>(this) )
|
|
{
|
|
if ( UserInputBase* userInput = service->getHardwareDevice() )
|
|
{
|
|
userInput->centerCursor();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UserInputService::setModalEnabled(bool value)
|
|
{
|
|
if(modalEnabled != value)
|
|
{
|
|
modalEnabled = value;
|
|
raisePropertyChanged(prop_ModalEnabled);
|
|
}
|
|
}
|
|
bool UserInputService::getModalEnabled() const
|
|
{
|
|
return modalEnabled;
|
|
}
|
|
|
|
bool UserInputService::getAccelerometerEnabled() const
|
|
{
|
|
return accelerometerEnabled;
|
|
}
|
|
bool UserInputService::getGyroscopeEnabled() const
|
|
{
|
|
return gyroscopeEnabled;
|
|
}
|
|
bool UserInputService::getTouchEnabled() const
|
|
{
|
|
return touchEnabled || isStudioEmulatingMobile;
|
|
}
|
|
bool UserInputService::getKeyboardEnabled() const
|
|
{
|
|
return keyboardEnabled;
|
|
}
|
|
bool UserInputService::getMouseEnabled() const
|
|
{
|
|
return mouseEnabled;
|
|
}
|
|
bool UserInputService::getGamepadEnabled() const
|
|
{
|
|
return gamepadEnabled;
|
|
}
|
|
void UserInputService::setGyroscopeEnabled(bool value)
|
|
{
|
|
if (value != gyroscopeEnabled)
|
|
{
|
|
gyroscopeEnabled = value;
|
|
raisePropertyChanged(prop_gyroscopeEnabled);
|
|
}
|
|
}
|
|
void UserInputService::setAccelerometerEnabled(bool value)
|
|
{
|
|
if (value != accelerometerEnabled)
|
|
{
|
|
accelerometerEnabled = value;
|
|
raisePropertyChanged(prop_accelerometerEnabled);
|
|
}
|
|
}
|
|
void UserInputService::setTouchEnabled(bool value)
|
|
{
|
|
if (value != touchEnabled)
|
|
{
|
|
touchEnabled = value;
|
|
raisePropertyChanged(prop_TouchEnabled);
|
|
}
|
|
}
|
|
void UserInputService::setKeyboardEnabled(bool value)
|
|
{
|
|
if (value != keyboardEnabled)
|
|
{
|
|
keyboardEnabled = value;
|
|
raisePropertyChanged(prop_KeyboardEnabled);
|
|
}
|
|
}
|
|
void UserInputService::setMouseEnabled(bool value)
|
|
{
|
|
if (value != mouseEnabled)
|
|
{
|
|
mouseEnabled = value;
|
|
raisePropertyChanged(prop_MouseEnabled);
|
|
}
|
|
}
|
|
void UserInputService::setGamepadEnabled(bool value)
|
|
{
|
|
if (value != gamepadEnabled)
|
|
{
|
|
gamepadEnabled = value;
|
|
raisePropertyChanged(prop_GamepadEnabled);
|
|
}
|
|
}
|
|
|
|
bool UserInputService::getStudioEmulatingMobileEnabled()
|
|
{
|
|
return isStudioEmulatingMobile;
|
|
}
|
|
void UserInputService::setStudioEmulatingMobileEnabled(bool value)
|
|
{
|
|
isStudioEmulatingMobile = value;
|
|
}
|
|
|
|
bool UserInputService::getLocalCharacterJumpEnabled() const
|
|
{
|
|
return localCharacterJumpEnabled;
|
|
}
|
|
void UserInputService::setLocalCharacterJumpEnabled(bool value)
|
|
{
|
|
if(RBX::Network::Players::frontendProcessing(this))
|
|
{
|
|
if(value != localCharacterJumpEnabled)
|
|
{
|
|
localCharacterJumpEnabled = value;
|
|
// raisePropertyChanged(prop_LocalCharacterJumpEnabled);
|
|
}
|
|
}
|
|
else
|
|
throw std::runtime_error("UserInputService.LocalCharacterJumpEnabled should only be accessed from a local script");
|
|
}
|
|
|
|
void UserInputService::setLocalHumanoidJump(weak_ptr<DataModel> weakDataModel, bool isJump)
|
|
{
|
|
if(shared_ptr<DataModel> dataModel = weakDataModel.lock())
|
|
{
|
|
RBX::Workspace* workspace = dataModel->getWorkspace();
|
|
if(!workspace)
|
|
return;
|
|
|
|
RBX::ModelInstance* localCharacter = RBX::Network::Players::findLocalCharacter(workspace);
|
|
if(RBX::Humanoid* localHumanoid = RBX::Humanoid::modelIsCharacter(localCharacter))
|
|
localHumanoid->setJump(isJump);
|
|
}
|
|
}
|
|
|
|
void UserInputService::jumpLocalCharacterLua()
|
|
{
|
|
setLocalHumanoidJump(shared_from(DataModel::get(this)), true);
|
|
}
|
|
|
|
void UserInputService::moveLocalCharacterInternal(weak_ptr<DataModel> weakDataModel, Vector2 walkDir, float walkDelta)
|
|
{
|
|
if(shared_ptr<DataModel> dataModel = weakDataModel.lock())
|
|
{
|
|
RBX::Workspace* workspace = dataModel->getWorkspace();
|
|
if(!workspace)
|
|
return;
|
|
|
|
Camera* camera = workspace->getCamera();
|
|
if(!camera)
|
|
return;
|
|
|
|
RBX::ModelInstance* localCharacter = RBX::Network::Players::findLocalCharacter(workspace);
|
|
if(RBX::Humanoid* localHumanoid = RBX::Humanoid::modelIsCharacter(localCharacter))
|
|
{
|
|
if(walkDir == Vector2::zero())
|
|
{
|
|
if (isStudioEmulatingMobile)
|
|
localHumanoid->setWalkingFromStudioTouchEmulation(false);
|
|
localHumanoid->setWalkDirection(Vector3::zero());
|
|
localHumanoid->setPercentWalkSpeed(1.0f);
|
|
return;
|
|
}
|
|
if (isStudioEmulatingMobile)
|
|
localHumanoid->setWalkingFromStudioTouchEmulation(true);
|
|
|
|
// set the percent walk speed for analog style controls (like a thumbstick)
|
|
localHumanoid->setPercentWalkSpeed(walkDir.length()/walkDelta);
|
|
Vector2 movementVector = walkDir.direction();
|
|
|
|
// get the angle between the standard "forward" direction and our current desired direction
|
|
RBX::Vector2 northDirection(0,-1);
|
|
float crossMoveWithNorth = (movementVector.x * northDirection.y) - (movementVector.y * northDirection.x);
|
|
float angle = -atan2(crossMoveWithNorth, movementVector.dot(northDirection));
|
|
|
|
// get camera lookVector, projected into the xz plane
|
|
RBX::Vector3 camLookVector = (camera->getCameraFocus().translation - camera->getCameraCoordinateFrame().translation).unit();
|
|
RBX::Vector3 camLookProjected = G3D::Plane(Vector3(0,1,0),Vector3(0,0,0)).closestPoint(camLookVector);
|
|
RBX::Vector2 camLook2d = RBX::Vector2(camLookProjected.x,camLookProjected.z);
|
|
|
|
// rotate our desired move direction with the camera xz plane projection
|
|
float cosAngle = cosf(angle);
|
|
float sinAngle = sinf(angle);
|
|
float xRotated = camLook2d.x * cosAngle - camLook2d.y * sinAngle;
|
|
float zRotated = camLook2d.x * sinAngle + camLook2d.y * cosAngle;
|
|
|
|
localHumanoid->setWalkDirection(RBX::Vector3(xRotated,0,zRotated));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void UserInputService::moveLocalCharacterLua(Vector2 walkDir, float maxWalkDelta)
|
|
{
|
|
walkDirection = walkDir;
|
|
this->maxWalkDelta = fabs(maxWalkDelta);
|
|
|
|
moveLocalCharacterInternal(shared_from(RBX::DataModel::get(this)), walkDir, maxWalkDelta);
|
|
}
|
|
|
|
void UserInputService::moveLocalCharacter(RBX::Vector2 movementVector, float maxMovementDelta)
|
|
{
|
|
walkDirection = movementVector;
|
|
maxWalkDelta = fabs(maxMovementDelta);
|
|
}
|
|
|
|
void UserInputService::jumpLocalCharacter(bool jumpValue)
|
|
{
|
|
if( getLocalCharacterJumpEnabled() )
|
|
isJump = jumpValue;
|
|
|
|
if (jumpValue)
|
|
sendJumpRequestEvent();
|
|
}
|
|
|
|
void UserInputService::jumpOnceLocalCharacter(bool jumpValue)
|
|
{
|
|
if (jumpValue)
|
|
sendJumpRequestEvent();
|
|
}
|
|
|
|
void UserInputService::processToolEvent(const shared_ptr<RBX::InputObject>& event)
|
|
{
|
|
boost::mutex::scoped_lock lock(ToolEventsMutex);
|
|
toolEventsVector.push_back(event);
|
|
}
|
|
|
|
static void doCameraRotate(float panAmount, float tiltAmount, weak_ptr<DataModel> weakDataModel)
|
|
{
|
|
if(shared_ptr<DataModel> dataModel = weakDataModel.lock())
|
|
{
|
|
RBX::Workspace* workspace = dataModel->getWorkspace();
|
|
if(!workspace)
|
|
return;
|
|
|
|
Camera* camera = workspace->getCamera();
|
|
if(!camera)
|
|
return;
|
|
|
|
if (camera->getCameraType() != Camera::LOCKED_CAMERA && camera->getCameraPanMode() == Camera::CAMERAPANMODE_CLASSIC)
|
|
{
|
|
camera->panRadians(panAmount);
|
|
camera->tiltRadians(tiltAmount);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UserInputService::wrapCamera(RBX::Vector2 mouseWrap)
|
|
{
|
|
cameraMouseWrap += mouseWrap;
|
|
}
|
|
|
|
void UserInputService::rotateCamera(RBX::Vector2 mouseDelta)
|
|
{
|
|
cameraPanDelta += mouseDelta;
|
|
cameraPanDelta = Vector2(G3D::clamp(cameraPanDelta.x,-100.0f,100.0f),cameraPanDelta.y);
|
|
}
|
|
|
|
void UserInputService::rotateCameraLua(RBX::Vector2 mouseDelta)
|
|
{
|
|
doCameraRotate( G3D::clamp(mouseDelta.x,-100.0f,100.0f), mouseDelta.y, shared_from(DataModel::get(this)));
|
|
}
|
|
|
|
static void doCameraZoom(float zoomAmount, weak_ptr<DataModel> weakDataModel)
|
|
{
|
|
if(shared_ptr<DataModel> dataModel = weakDataModel.lock())
|
|
{
|
|
RBX::Workspace* workspace = dataModel->getWorkspace();
|
|
if(!workspace)
|
|
return;
|
|
|
|
Camera* camera = workspace->getCamera();
|
|
if(!camera)
|
|
return;
|
|
|
|
if (camera->getCameraType() != Camera::LOCKED_CAMERA)
|
|
camera->zoom(zoomAmount);
|
|
}
|
|
}
|
|
|
|
void UserInputService::zoomCameraLua(float zoom)
|
|
{
|
|
doCameraZoom(zoom, shared_from(DataModel::get(this)));
|
|
}
|
|
|
|
void UserInputService::zoomCamera(float zoom)
|
|
{
|
|
cameraZoom += zoom;
|
|
}
|
|
|
|
void UserInputService::mouseTrackCamera(RBX::Vector2 mouseDelta)
|
|
{
|
|
cameraMouseTrack = mouseDelta;
|
|
}
|
|
|
|
static void doNothing(Reflection::Variant var) {}
|
|
|
|
bool UserInputService::showStatsBasedOnInputString(const char* text)
|
|
{
|
|
if (DFFlag::EnableShowStatsLua)
|
|
return false;
|
|
|
|
bool retVal = false;
|
|
shared_ptr<DataModel> dataModel = shared_from(DataModel::get(this));
|
|
|
|
if (!dataModel)
|
|
return retVal;
|
|
|
|
|
|
if (strcmp(text, "Genstats") == 0)
|
|
{
|
|
dataModel->getGuiBuilder().toggleGeneralStats();
|
|
retVal = true;
|
|
}
|
|
else if (strcmp(text, "Renstats") == 0)
|
|
{
|
|
dataModel->getGuiBuilder().toggleRenderStats();
|
|
retVal = true;
|
|
}
|
|
else if (strcmp(text, "Netstats") == 0)
|
|
{
|
|
dataModel->getGuiBuilder().toggleNetworkStats();
|
|
retVal = true;
|
|
}
|
|
else if (strcmp(text, "Phystats") == 0)
|
|
{
|
|
dataModel->getGuiBuilder().togglePhysicsStats();
|
|
retVal = true;
|
|
}
|
|
else if (strcmp(text, "Sumstats") == 0)
|
|
{
|
|
dataModel->getGuiBuilder().toggleSummaryStats();
|
|
retVal = true;
|
|
}
|
|
else if (strcmp(text, "Cusstats") == 0)
|
|
{
|
|
dataModel->getGuiBuilder().toggleCustomStats();
|
|
retVal = true;
|
|
}
|
|
else if (boost::iequals(text, "console"))
|
|
{
|
|
if (CoreGuiService* cgs = dataModel->find<CoreGuiService>())
|
|
{
|
|
if (Instance* parent = cgs->findFirstChildByName("RobloxGui"))
|
|
{
|
|
if (Instance* controlFrame = parent->findFirstChildByName("ControlFrame"))
|
|
{
|
|
parent = controlFrame;
|
|
}
|
|
if (BindableFunction* toggle = Instance::fastDynamicCast<BindableFunction>(parent->findFirstChildByName("ToggleDevConsole")))
|
|
{
|
|
toggle->invoke(rbx::make_shared<Reflection::Tuple>(),
|
|
boost::bind(&doNothing, _1), boost::bind(&doNothing, _1));
|
|
}
|
|
}
|
|
}
|
|
retVal = true;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
void UserInputService::textboxDidFinishEditing(const char* text, bool shouldReturn)
|
|
{
|
|
shared_ptr<std::string> textString(new std::string(text));
|
|
|
|
boost::mutex::scoped_lock lock(TextboxEventsMutex);
|
|
textboxFinishedVector.push_back(std::pair<shared_ptr<std::string>, bool>(textString,shouldReturn));
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Begin Gesture Input signals/functions
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
void UserInputService::addGestureEventToProcess(const UserInputService::Gesture gesture, shared_ptr<RBX::Reflection::ValueArray>& touchPositions, shared_ptr<Reflection::Tuple>& args)
|
|
{
|
|
if(touchPositions && args)
|
|
{
|
|
boost::mutex::scoped_lock lock(GestureEventsMutex);
|
|
|
|
gestureEventsToProcess.push_back(std::pair<UserInputService::Gesture, std::pair<shared_ptr<RBX::Reflection::ValueArray>,shared_ptr<const Reflection::Tuple> > >(gesture, std::pair<shared_ptr<RBX::Reflection::ValueArray>, shared_ptr<const Reflection::Tuple> >(touchPositions, args)));
|
|
}
|
|
}
|
|
|
|
void UserInputService::fireGestureEvent(const UserInputService::Gesture gesture, const shared_ptr<const RBX::Reflection::ValueArray>& touchPositions, const shared_ptr<const Reflection::Tuple>& args, const bool wasSunk)
|
|
{
|
|
switch (gesture)
|
|
{
|
|
case UserInputService::GESTURE_TAP:
|
|
tapGestureEvent(touchPositions, wasSunk);
|
|
break;
|
|
case UserInputService::GESTURE_SWIPE:
|
|
RBXASSERT(args->values.size() == 2);
|
|
swipeGestureEvent(args->values[0].cast<UserInputService::SwipeDirection>(),args->values[1].cast<int>(), wasSunk);
|
|
break;
|
|
case UserInputService::GESTURE_ROTATE:
|
|
RBXASSERT(args->values.size() == 3);
|
|
rotateGestureEvent(touchPositions,args->values[0].cast<float>(),args->values[1].cast<float>(), args->values[2].cast<InputObject::UserInputState>(), wasSunk);
|
|
break;
|
|
case UserInputService::GESTURE_PINCH:
|
|
RBXASSERT(args->values.size() == 3);
|
|
pinchGestureEvent(touchPositions,args->values[0].cast<float>(),args->values[1].cast<float>(), args->values[2].cast<InputObject::UserInputState>(), wasSunk);
|
|
break;
|
|
case UserInputService::GESTURE_LONGPRESS:
|
|
RBXASSERT(args->values.size() == 1);
|
|
longPressGestureEvent(touchPositions,args->values[0].cast<InputObject::UserInputState>(), wasSunk);
|
|
break;
|
|
case UserInputService::GESTURE_PAN:
|
|
RBXASSERT(args->values.size() == 3);
|
|
panGestureEvent(touchPositions,args->values[0].cast<RBX::Vector2>(),args->values[1].cast<RBX::Vector2>(),args->values[2].cast<InputObject::UserInputState>(), wasSunk);
|
|
break;
|
|
case UserInputService::GESTURE_NONE:
|
|
default: // intentional fall thru
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
void UserInputService::processGestures()
|
|
{
|
|
GestureEventsVector tmpGestureEventsMap;
|
|
{
|
|
boost::mutex::scoped_lock lock(GestureEventsMutex);
|
|
tmpGestureEventsMap.swap(gestureEventsToProcess);
|
|
}
|
|
|
|
if (tmpGestureEventsMap.size() > 0)
|
|
{
|
|
Vector2 guiOffset = Vector2::zero();
|
|
if (GuiService* guiService = RBX::ServiceProvider::find<GuiService>(this))
|
|
{
|
|
guiOffset = guiService->getGlobalGuiInset().xy();
|
|
}
|
|
|
|
for (GestureEventsVector::iterator iter = tmpGestureEventsMap.begin(); iter != tmpGestureEventsMap.end(); ++iter)
|
|
{
|
|
GuiResponse response;
|
|
|
|
shared_ptr<RBX::Reflection::ValueArray> touchPositions = (*iter).second.first;
|
|
|
|
if (guiOffset != Vector2::zero())
|
|
{
|
|
for (RBX::Reflection::ValueArray::iterator valueIter = touchPositions->begin(); valueIter != touchPositions->end(); ++valueIter)
|
|
{
|
|
if ((*valueIter).isType<RBX::Vector2>())
|
|
{
|
|
RBX::Vector2 touchPosition = (*valueIter).cast<RBX::Vector2>();
|
|
touchPosition -= guiOffset;
|
|
*valueIter = touchPosition;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(CoreGuiService* coreGui = ServiceProvider::find<CoreGuiService>(this))
|
|
response = coreGui->processGesture((*iter).first, touchPositions, (*iter).second.second);
|
|
|
|
if (!response.wasSunk())
|
|
{
|
|
if (RBX::Network::Players* players = ServiceProvider::create<RBX::Network::Players>(this))
|
|
if (RBX::Network::Player* player = players->getLocalPlayer())
|
|
if (PlayerGui* playerGui = player->findFirstChildOfType<PlayerGui>())
|
|
response = playerGui->processGesture((*iter).first, touchPositions, (*iter).second.second);
|
|
}
|
|
|
|
fireGestureEvent((*iter).first, touchPositions, (*iter).second.second, response.wasSunk());
|
|
}
|
|
}
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Begin Generic Input signals/functions
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
bool UserInputService::inputVectorHasInputPair(const std::pair<shared_ptr<InputObject>, void*>& eventPair, const EventsVector& vectorToCheck)
|
|
{
|
|
if (vectorToCheck.size() > 0)
|
|
{
|
|
for (EventsVector::const_iterator iter = vectorToCheck.begin(); iter != vectorToCheck.end(); ++iter)
|
|
{
|
|
if (eventPair == (*iter))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void UserInputService::eraseInputPairFromInputVector(const std::pair<shared_ptr<InputObject>, void*>& eventPair, EventsVector& vectorToCheck)
|
|
{
|
|
if (vectorToCheck.size() > 0)
|
|
{
|
|
for (EventsVector::iterator iter = vectorToCheck.begin(); iter != vectorToCheck.end(); ++iter)
|
|
{
|
|
if (eventPair == (*iter))
|
|
{
|
|
vectorToCheck.erase(iter);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool UserInputService::eraseInputTypeFromInputVector(const InputObject::UserInputType inputType, EventsVector& vectorToEraseFrom)
|
|
{
|
|
if(vectorToEraseFrom.size() > 0)
|
|
{
|
|
for (EventsVector::iterator iter = vectorToEraseFrom.begin(); iter != vectorToEraseFrom.end(); ++iter)
|
|
{
|
|
if (shared_ptr<InputObject> storedChangedInput = (*iter).first)
|
|
{
|
|
if (storedChangedInput && storedChangedInput.get() && storedChangedInput->getUserInputType() == inputType)
|
|
{
|
|
vectorToEraseFrom.erase(iter);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// fires input events on datamodel w/o taking lock!!!! If you use this make sure you have lock!
|
|
void UserInputService::dangerousFireInputEvent(const shared_ptr<InputObject>& inputObject, void* nativeInputObject)
|
|
{
|
|
dataModelProcessInput(inputObject, nativeInputObject, inputObject->getUserInputState());
|
|
fireLegacyMouseEvent(inputObject, nativeInputObject, true);
|
|
}
|
|
|
|
void UserInputService::fireLegacyMouseEvent(const shared_ptr<InputObject>& inputObject, void* nativeInputObject, bool fireImmediately)
|
|
{
|
|
if (inputObject->isTouchEvent())
|
|
{
|
|
InputObject::UserInputType fakeInputType = InputObject::TYPE_NONE;
|
|
switch (inputObject->getUserInputState())
|
|
{
|
|
case InputObject::INPUT_STATE_BEGIN:
|
|
case InputObject::INPUT_STATE_END:
|
|
fakeInputType = InputObject::TYPE_MOUSEBUTTON1;
|
|
break;
|
|
case InputObject::INPUT_STATE_CHANGE:
|
|
fakeInputType = InputObject::TYPE_MOUSEMOVEMENT;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if ((fakeInputType != InputObject::TYPE_NONE) && (fakeMouseEventsMap.find(fakeInputType) != fakeMouseEventsMap.end()))
|
|
{
|
|
fakeMouseEventsMap[fakeInputType]->setInputState(inputObject->getUserInputState());
|
|
fakeMouseEventsMap[fakeInputType]->setPosition(inputObject->getRawPosition());
|
|
|
|
if (fireImmediately)
|
|
{
|
|
dangerousFireInputEvent(fakeMouseEventsMap[fakeInputType], nativeInputObject);
|
|
}
|
|
else
|
|
{
|
|
fireInputEvent(fakeMouseEventsMap[fakeInputType], nativeInputObject);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UserInputService::fireInputEvent(const shared_ptr<InputObject>& inputObject, void* nativeInputObject, bool processed)
|
|
{
|
|
if(inputObject && inputObject.get())
|
|
{
|
|
if (!inputObject->isPublicEvent())
|
|
{
|
|
return;
|
|
}
|
|
|
|
{
|
|
boost::mutex::scoped_lock lock(InputEventsMutex);
|
|
|
|
std::pair<shared_ptr<InputObject>, void*> eventPair( inputObject, nativeInputObject);
|
|
|
|
// always get start/end events
|
|
if (inputObject->getUserInputState() == InputObject::INPUT_STATE_BEGIN)
|
|
{
|
|
beginEventsToProcess.push_back(eventPair);
|
|
|
|
if (inputObject->isTouchEvent())
|
|
{
|
|
currentTouches.push_back(inputObject);
|
|
}
|
|
}
|
|
else if (inputObject->getUserInputState() == InputObject::INPUT_STATE_END)
|
|
{
|
|
endEventsToProcess.push_back(eventPair);
|
|
}
|
|
else // only process one 'change' event for each type, but make sure it isn't in a different queue!
|
|
{
|
|
if (!inputVectorHasInputPair(eventPair, beginEventsToProcess) && !inputVectorHasInputPair(eventPair, endEventsToProcess))
|
|
{
|
|
if (inputObject->isTouchEvent()) // since more than one finger can be on the screen at once, only remove this particular pair
|
|
{
|
|
eraseInputPairFromInputVector(eventPair, changeEventsToProcess);
|
|
}
|
|
// erase the type, since all other input is singular (only can have multiple touches)
|
|
else
|
|
{
|
|
if (inputObject->getUserInputType() == InputObject::TYPE_MOUSEMOVEMENT)
|
|
{
|
|
|
|
if(changeEventsToProcess.size() > 0)
|
|
{
|
|
for (EventsVector::iterator iter = changeEventsToProcess.begin(); iter != changeEventsToProcess.end() && !processed; ++iter)
|
|
{
|
|
if (shared_ptr<InputObject> storedChangedInput = (*iter).first)
|
|
{
|
|
if (storedChangedInput && storedChangedInput.get() && storedChangedInput->getUserInputType() == InputObject::TYPE_MOUSEMOVEMENT)
|
|
{
|
|
storedChangedInput->setDelta(storedChangedInput->getDelta() + inputObject->getDelta());
|
|
storedChangedInput->setPosition(inputObject->getRawPosition());
|
|
processed = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!processed)
|
|
{
|
|
|
|
if (mouseEventObject)
|
|
{
|
|
mouseEventObject->setInputType(inputObject->getUserInputType());
|
|
mouseEventObject->setInputState(inputObject->getUserInputState());
|
|
mouseEventObject->setDelta(inputObject->getDelta());
|
|
mouseEventObject->setPosition(inputObject->getRawPosition());
|
|
} else {
|
|
mouseEventObject = RBX::Creatable<RBX::Instance>::create<RBX::InputObject>(inputObject->getUserInputType(), inputObject->getUserInputState(),
|
|
inputObject->getRawPosition(), inputObject->getDelta(), DataModel::get(this));
|
|
}
|
|
eventPair = std::pair<shared_ptr<InputObject>, void*>( mouseEventObject, nativeInputObject);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
eraseInputTypeFromInputVector(inputObject->getUserInputType(), changeEventsToProcess);
|
|
}
|
|
}
|
|
|
|
if (!processed)
|
|
changeEventsToProcess.push_back(eventPair);
|
|
}
|
|
}
|
|
}
|
|
|
|
fireLegacyMouseEvent(inputObject, nativeInputObject);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Begin Motion Input signals/functions
|
|
//////////////////////////////////////////////////////////////////////////
|
|
shared_ptr<Instance> UserInputService::getAcceleration()
|
|
{
|
|
return accelerationInputObject;
|
|
}
|
|
shared_ptr<const Reflection::Tuple> UserInputService::getRotation()
|
|
{
|
|
shared_ptr<Reflection::Tuple> returnValues(new Reflection::Tuple(2));
|
|
returnValues->values[0] = rotationInputObject;
|
|
returnValues->values[1] = rotationCFrame;
|
|
|
|
return returnValues;
|
|
}
|
|
shared_ptr<Instance> UserInputService::getGravity()
|
|
{
|
|
return gravityInputObject;
|
|
}
|
|
|
|
void UserInputService::fireAccelerationEvent(const RBX::Vector3& newAcceleration)
|
|
{
|
|
boost::mutex::scoped_lock lock(MotionEventsMutex);
|
|
|
|
if (InputObject* accelInput = Instance::fastDynamicCast<InputObject>(accelerationInputObject.get()))
|
|
{
|
|
accelInput->setPosition(newAcceleration);
|
|
}
|
|
|
|
shouldFireAccelerationEvent = true;
|
|
}
|
|
|
|
void UserInputService::fireGravityEvent(const RBX::Vector3& newGravity)
|
|
{
|
|
boost::mutex::scoped_lock lock(MotionEventsMutex);
|
|
|
|
if (InputObject* gravInput = Instance::fastDynamicCast<InputObject>(gravityInputObject.get()))
|
|
{
|
|
gravInput->setPosition(newGravity);
|
|
}
|
|
|
|
shouldFireGravityEvent = true;
|
|
}
|
|
|
|
void UserInputService::fireRotationEvent(const RBX::Vector3& newRotation, const RBX::Vector4& quaternion)
|
|
{
|
|
boost::mutex::scoped_lock lock(MotionEventsMutex);
|
|
|
|
if (InputObject* gyroInput = Instance::fastDynamicCast<InputObject>(rotationInputObject.get()))
|
|
{
|
|
gyroInput->setDelta(newRotation - gyroInput->getPosition());
|
|
gyroInput->setPosition(newRotation);
|
|
|
|
Quaternion quat(quaternion.w,quaternion.y,quaternion.z,quaternion.x);
|
|
quat.normalize();
|
|
|
|
quat.toRotationMatrix(rotationCFrame.rotation);
|
|
}
|
|
|
|
shouldFireRotationEvent = true;
|
|
}
|
|
|
|
void UserInputService::processMotionEvents()
|
|
{
|
|
boost::mutex::scoped_lock lock(MotionEventsMutex);
|
|
|
|
if (shouldFireAccelerationEvent)
|
|
{
|
|
accelerometerChangedSignal(accelerationInputObject);
|
|
shouldFireAccelerationEvent = false;
|
|
}
|
|
|
|
if (shouldFireGravityEvent)
|
|
{
|
|
gravityChangedSignal(gravityInputObject);
|
|
shouldFireGravityEvent = false;
|
|
}
|
|
|
|
if (shouldFireRotationEvent)
|
|
{
|
|
gyroChangedSignal(rotationInputObject, rotationCFrame);
|
|
shouldFireRotationEvent = false;
|
|
}
|
|
}
|
|
|
|
rbx::signal<void(shared_ptr<Instance>, CoordinateFrame)>* UserInputService::getOrCreateScriptGyroEventSignal(bool create)
|
|
{
|
|
if (create)
|
|
{
|
|
if (!Network::Players::frontendProcessing(this))
|
|
{
|
|
throw std::runtime_error("DeviceRotationChanged event can only be used from local scripts");
|
|
}
|
|
else if (!gyroscopeEnabled)
|
|
{
|
|
RBX::StandardOut::singleton()->printf(MESSAGE_WARNING, "Trying to listen to rotation events on a device without a gyroscope.");
|
|
}
|
|
else
|
|
{
|
|
motionEventListeningStarted("gyro");
|
|
}
|
|
}
|
|
|
|
return &gyroChangedSignal;
|
|
}
|
|
|
|
rbx::signal<void(shared_ptr<Instance>)>* UserInputService::getOrCreateScriptGravityEventSignal(bool create )
|
|
{
|
|
if (create)
|
|
{
|
|
if (!Network::Players::frontendProcessing(this))
|
|
{
|
|
throw std::runtime_error("DeviceGravityChanged event can only be used from local scripts");
|
|
}
|
|
else if (!accelerometerEnabled)
|
|
{
|
|
RBX::StandardOut::singleton()->printf(MESSAGE_WARNING, "Trying to listen to acceleration events on a device without a accelerometer.");
|
|
}
|
|
else
|
|
{
|
|
// uses accelerometer so make sure we are listening to that
|
|
motionEventListeningStarted("accelerometer");
|
|
}
|
|
}
|
|
|
|
return &gravityChangedSignal;
|
|
}
|
|
|
|
rbx::signal<void(shared_ptr<Instance>)>* UserInputService::getOrCreateScriptAccelerometerEventSignal(bool create )
|
|
{
|
|
if (create)
|
|
{
|
|
if (!Network::Players::frontendProcessing(this))
|
|
{
|
|
throw std::runtime_error("DeviceAccelerationChanged event can only be used from local scripts");
|
|
}
|
|
else
|
|
{
|
|
motionEventListeningStarted("accelerometer");
|
|
}
|
|
}
|
|
|
|
return &accelerometerChangedSignal;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Begin Keyboard/Mouse Input signals/functions
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void UserInputService::sendMouseEvent(const shared_ptr<RBX::InputObject>& event, void* nativeEventObject)
|
|
{
|
|
fireInputEvent(event, nativeEventObject);
|
|
}
|
|
|
|
std::string UserInputService::getPasteText()
|
|
{
|
|
std::string clipBoardText = "";
|
|
|
|
#if defined(_WIN32) && !defined(RBX_PLATFORM_DURANGO) && !defined(RBX_PLATFORM_UWP)
|
|
if(::OpenClipboard(NULL))
|
|
{
|
|
HANDLE hData = ::GetClipboardData(CF_TEXT);
|
|
if(hData)
|
|
{
|
|
clipBoardText = (char*) ::GlobalLock(hData);
|
|
GlobalUnlock(hData);
|
|
}
|
|
::CloseClipboard();
|
|
}
|
|
#endif
|
|
|
|
#if defined(__APPLE__)
|
|
#if !RBX_PLATFORM_IOS
|
|
NSString* pasteString = [[NSPasteboard generalPasteboard] stringForType:NSPasteboardTypeString];
|
|
if(pasteString)
|
|
clipBoardText = [pasteString UTF8String];
|
|
#endif
|
|
#endif
|
|
|
|
return clipBoardText;
|
|
}
|
|
|
|
std::vector<RBX::ModCode> UserInputService::getCommandModCodes()
|
|
{
|
|
std::vector<RBX::ModCode> modCodes;
|
|
#ifdef _WIN32
|
|
modCodes.push_back(KMOD_LCTRL);
|
|
modCodes.push_back(KMOD_RCTRL);
|
|
#elif __APPLE__
|
|
modCodes.push_back(KMOD_LMETA);
|
|
modCodes.push_back(KMOD_RMETA);
|
|
#endif
|
|
return modCodes;
|
|
}
|
|
|
|
RBX::ModCode UserInputService::getCurrentModKey()
|
|
{
|
|
for(std::vector<std::pair<RBX::KeyCode, RBX::ModCode> >::iterator it = modPairs.begin(); it != modPairs.end(); ++it)
|
|
{
|
|
if (newKeyState.find(it->first) != newKeyState.end())
|
|
{
|
|
if (newKeyState[it->first]->getUserInputState() == InputObject::INPUT_STATE_BEGIN)
|
|
{
|
|
return it->second;
|
|
}
|
|
}
|
|
}
|
|
return KMOD_NONE;
|
|
}
|
|
|
|
char UserInputService::getModifiedKey(RBX::KeyCode key, RBX::ModCode mod)
|
|
{
|
|
// if our keycode is actually a modifier, don't return a char
|
|
switch(key)
|
|
{
|
|
case RBX::SDLK_LSHIFT:
|
|
case RBX::SDLK_RSHIFT:
|
|
case RBX::SDLK_LCTRL:
|
|
case RBX::SDLK_RCTRL:
|
|
case RBX::SDLK_CAPSLOCK:
|
|
case RBX::SDLK_LALT:
|
|
case RBX::SDLK_RALT:
|
|
return 0;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
bool shift = ((mod & RBX::KMOD_RSHIFT) != 0) || ((mod & RBX::KMOD_LSHIFT) != 0);
|
|
bool caps = ((mod & RBX::KMOD_CAPS) != 0);
|
|
|
|
//XOR
|
|
bool isUpper = (shift && !caps) || (!shift && caps);
|
|
|
|
if ((key >= RBX::SDLK_a) && (key <= RBX::SDLK_z))
|
|
{
|
|
if (isUpper)
|
|
{
|
|
return 'A' + (key - RBX::SDLK_a);
|
|
}
|
|
else
|
|
{
|
|
return 'a' + (key - RBX::SDLK_a);
|
|
}
|
|
}
|
|
|
|
// this is bad, doesn't work with non-standard keyboards
|
|
if ((key >= RBX::SDLK_SPACE) && (key <= RBX::SDLK_AT)) {
|
|
if(shift)
|
|
{
|
|
switch(key)
|
|
{
|
|
case RBX::SDLK_1:
|
|
return RBX::SDLK_EXCLAIM;
|
|
break;
|
|
case RBX::SDLK_2:
|
|
return RBX::SDLK_AT;
|
|
break;
|
|
case RBX::SDLK_3:
|
|
return RBX::SDLK_HASH;
|
|
break;
|
|
case RBX::SDLK_4:
|
|
return RBX::SDLK_DOLLAR;
|
|
break;
|
|
case RBX::SDLK_5:
|
|
return RBX::SDLK_PERCENT;
|
|
break;
|
|
case RBX::SDLK_6:
|
|
return RBX::SDLK_CARET;
|
|
break;
|
|
case RBX::SDLK_7:
|
|
return RBX::SDLK_AMPERSAND;
|
|
break;
|
|
case RBX::SDLK_8:
|
|
return RBX::SDLK_ASTERISK;
|
|
break;
|
|
case RBX::SDLK_9:
|
|
return RBX::SDLK_LEFTPAREN;
|
|
break;
|
|
case RBX::SDLK_0:
|
|
return RBX::SDLK_RIGHTPAREN;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return ' ' + (key - RBX::SDLK_SPACE);
|
|
}
|
|
}
|
|
|
|
if(shift)
|
|
{
|
|
switch(key)
|
|
{
|
|
case RBX::SDLK_SEMICOLON:
|
|
return RBX::SDLK_COLON;
|
|
break;
|
|
case RBX::SDLK_COMMA:
|
|
return RBX::SDLK_LESS;
|
|
break;
|
|
case RBX::SDLK_PERIOD:
|
|
return RBX::SDLK_GREATER;
|
|
break;
|
|
case RBX::SDLK_SLASH:
|
|
return RBX::SDLK_QUESTION;
|
|
break;
|
|
case RBX::SDLK_MINUS:
|
|
return RBX::SDLK_UNDERSCORE;
|
|
break;
|
|
case RBX::SDLK_BACKQUOTE:
|
|
return RBX::SDLK_TILDE;
|
|
break;
|
|
case RBX::SDLK_LEFTBRACKET:
|
|
return RBX::SDLK_LEFTCURLY;
|
|
break;
|
|
case RBX::SDLK_RIGHTBRACKET:
|
|
return RBX::SDLK_RIGHTCURLY;
|
|
break;
|
|
case RBX::SDLK_BACKSLASH:
|
|
return RBX::SDLK_PIPE;
|
|
break;
|
|
case RBX::SDLK_EQUALS:
|
|
return RBX::SDLK_PLUS;
|
|
break;
|
|
case RBX::SDLK_QUOTE:
|
|
return RBX::SDLK_QUOTEDBL;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return key;
|
|
}
|
|
|
|
void UserInputService::processKeyboardEvents()
|
|
{
|
|
std::vector<KeyboardData> tmpKeyboardEventsVector;
|
|
{
|
|
boost::mutex::scoped_lock lock(KeyboardEventsMutex);
|
|
keyboardEventsToProcess.swap(tmpKeyboardEventsVector);
|
|
}
|
|
|
|
for (std::vector<KeyboardData>::iterator iter = tmpKeyboardEventsVector.begin(); iter != tmpKeyboardEventsVector.end(); ++iter )
|
|
{
|
|
setKeyStateInternal(iter->keyCode, iter->modCode, iter->modifiedKey, iter->isDown);
|
|
}
|
|
}
|
|
|
|
void UserInputService::setKeyStateInternal(RBX::KeyCode keyCode, ModCode modCode, char modifiedKey, bool isDown)
|
|
{
|
|
if (keyCode == SDLK_CAPSLOCK && isDown)
|
|
{
|
|
capsLocked = !capsLocked;
|
|
}
|
|
|
|
shared_ptr<InputObject> inputObjectToFire = shared_ptr<InputObject>();
|
|
|
|
if (newKeyState.find(keyCode) == newKeyState.end() )
|
|
{
|
|
newKeyState[keyCode] = RBX::Creatable<Instance>::create<InputObject>(InputObject::TYPE_KEYBOARD,
|
|
isDown ? InputObject::INPUT_STATE_BEGIN : InputObject::INPUT_STATE_END,
|
|
keyCode,
|
|
modCode,
|
|
modifiedKey,
|
|
DataModel::get(this));
|
|
inputObjectToFire = newKeyState[keyCode];
|
|
}
|
|
else
|
|
{
|
|
shared_ptr<InputObject> inputObject = newKeyState[keyCode];
|
|
inputObject->setInputState(isDown ? InputObject::INPUT_STATE_BEGIN : InputObject::INPUT_STATE_END);
|
|
inputObject->mod = modCode;
|
|
inputObject->modifiedKey = modifiedKey;
|
|
inputObjectToFire = inputObject;
|
|
}
|
|
}
|
|
|
|
void UserInputService::setKeyState(RBX::KeyCode keyCode, ModCode modCode, char modifiedKey, bool isDown)
|
|
{
|
|
KeyboardData data;
|
|
data.keyCode = keyCode;
|
|
data.modCode = modCode;
|
|
data.modifiedKey = modifiedKey;
|
|
data.isDown = isDown;
|
|
|
|
boost::mutex::scoped_lock lock(KeyboardEventsMutex);
|
|
keyboardEventsToProcess.push_back(data);
|
|
}
|
|
|
|
bool UserInputService::isKeyDown(RBX::KeyCode keyCode)
|
|
{
|
|
boost::unordered_map<RBX::KeyCode, shared_ptr<InputObject> >::iterator iter = newKeyState.find(keyCode);
|
|
if (iter == newKeyState.end())
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (iter->second && iter->second.get())
|
|
{
|
|
return (iter->second->getUserInputState() == InputObject::INPUT_STATE_BEGIN);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
bool UserInputService::isCapsLocked()
|
|
{
|
|
return capsLocked;
|
|
}
|
|
|
|
void UserInputService::clearKeyStateInternal()
|
|
{
|
|
newKeyState.clear();
|
|
}
|
|
|
|
void UserInputService::resetKeyState()
|
|
{
|
|
newKeyState.clear();
|
|
}
|
|
|
|
shared_ptr<const Reflection::ValueArray> UserInputService::getKeyboardState()
|
|
{
|
|
shared_ptr<Reflection::ValueArray> keyboardArray = rbx::make_shared<Reflection::ValueArray>();
|
|
|
|
for (boost::unordered_map<RBX::KeyCode, shared_ptr<InputObject> >::iterator iter = newKeyState.begin(); iter != newKeyState.end(); ++iter)
|
|
{
|
|
if ((*iter).second->getUserInputState() == InputObject::INPUT_STATE_BEGIN)
|
|
{
|
|
shared_ptr<Instance> inputObject = (*iter).second;
|
|
keyboardArray->push_back(inputObject);
|
|
}
|
|
}
|
|
|
|
return keyboardArray;
|
|
}
|
|
|
|
Vector2 UserInputService::getKeyboardGuiSelectionDirection(const shared_ptr<InputObject>& inputObject)
|
|
{
|
|
// todo: allow these to be mapped?
|
|
if (inputObject->isKeyDownEvent())
|
|
{
|
|
switch (inputObject->getKeyCode())
|
|
{
|
|
case SDLK_LEFT:
|
|
case SDLK_a:
|
|
return Vector2(-1,0);
|
|
case SDLK_RIGHT:
|
|
case SDLK_d:
|
|
return Vector2(1,0);
|
|
case SDLK_UP:
|
|
case SDLK_w:
|
|
return Vector2(0,-1);
|
|
case SDLK_DOWN:
|
|
case SDLK_s:
|
|
return Vector2(0,1);
|
|
|
|
default:
|
|
return Vector2::zero();
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Vector2::zero();
|
|
}
|
|
|
|
bool UserInputService::isMouseOverGui()
|
|
{
|
|
if (!currentMousePosition)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (DataModel* dm = DataModel::get(this))
|
|
{
|
|
GuiResponse playerGuiResponse = dm->processPlayerGui(currentMousePosition);
|
|
if (playerGuiResponse.wasSunk() || playerGuiResponse.getMouseWasOver())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (Workspace* workspace = ServiceProvider::find<Workspace>(this))
|
|
{
|
|
GuiResponse surfaceGuiResponse = workspace->handleSurfaceGui(currentMousePosition);
|
|
if (surfaceGuiResponse.wasSunk() || surfaceGuiResponse.getMouseWasOver())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Mouse Manipulation
|
|
///////////////////////////////////////////////////////////////////////////
|
|
ContentId UserInputService::getDefaultMouseCursor(bool hoverOverActive, bool relativePathNoExtension)
|
|
{
|
|
std::string defaultKeyboardMouseCursorString;
|
|
std::string defaultGamepadCursorString;
|
|
|
|
if (hoverOverActive)
|
|
{
|
|
if (relativePathNoExtension)
|
|
{
|
|
defaultKeyboardMouseCursorString = "ArrowCursor";
|
|
defaultGamepadCursorString = "Cursors/Gamepad/PointerOver";
|
|
}
|
|
else
|
|
{
|
|
defaultKeyboardMouseCursorString = "textures/ArrowCursor.png";
|
|
defaultGamepadCursorString = "textures/Cursors/Gamepad/PointerOver.png";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (relativePathNoExtension)
|
|
{
|
|
defaultKeyboardMouseCursorString = "ArrowFarCursor";
|
|
defaultGamepadCursorString = "Cursors/Gamepad/Pointer";
|
|
}
|
|
else
|
|
{
|
|
defaultKeyboardMouseCursorString = "textures/ArrowFarCursor.png";
|
|
defaultGamepadCursorString = "textures/Cursors/Gamepad/Pointer.png";
|
|
}
|
|
}
|
|
|
|
if (GamepadService::getGamepadIntForEnum(lastInputType) != -1)
|
|
{
|
|
if (relativePathNoExtension)
|
|
{
|
|
return ContentId(defaultGamepadCursorString);
|
|
}
|
|
return ContentId::fromAssets(defaultGamepadCursorString.c_str());
|
|
}
|
|
else if (lastInputType == InputObject::TYPE_MOUSEBUTTON1 || lastInputType == InputObject::TYPE_MOUSEBUTTON2 ||
|
|
lastInputType == InputObject::TYPE_MOUSEDELTA || lastInputType == InputObject::TYPE_MOUSEMOVEMENT ||
|
|
lastInputType == InputObject::TYPE_MOUSEWHEEL || lastInputType == InputObject::TYPE_KEYBOARD)
|
|
{
|
|
if (relativePathNoExtension)
|
|
{
|
|
return ContentId(defaultKeyboardMouseCursorString);
|
|
}
|
|
return ContentId::fromAssets(defaultKeyboardMouseCursorString.c_str());
|
|
}
|
|
|
|
if (keyboardEnabled)
|
|
{
|
|
if (relativePathNoExtension)
|
|
{
|
|
return ContentId(defaultKeyboardMouseCursorString);
|
|
}
|
|
return ContentId::fromAssets(defaultKeyboardMouseCursorString.c_str());
|
|
}
|
|
else if (gamepadEnabled)
|
|
{
|
|
if (relativePathNoExtension)
|
|
{
|
|
return ContentId(defaultGamepadCursorString);
|
|
}
|
|
return ContentId::fromAssets(defaultGamepadCursorString.c_str());
|
|
}
|
|
|
|
return ContentId();
|
|
}
|
|
TextureId UserInputService::getCurrentMouseIcon()
|
|
{
|
|
return (( mouseIconStack.size() > 0) ? mouseIconStack.back() : TextureId());
|
|
}
|
|
void UserInputService::pushMouseIcon(TextureId newMouseIcon)
|
|
{
|
|
mouseIconStack.push_back(newMouseIcon);
|
|
}
|
|
void UserInputService::popMouseIcon()
|
|
{
|
|
if (mouseIconStack.size() > 0)
|
|
mouseIconStack.pop_back();
|
|
}
|
|
void UserInputService::popMouseIcon(TextureId mouseIconToRemove)
|
|
{
|
|
mouseIconStack.erase(std::remove(mouseIconStack.begin(), mouseIconStack.end(), mouseIconToRemove), mouseIconStack.end());
|
|
}
|
|
|
|
void UserInputService::setMouseIconEnabled(bool enabled)
|
|
{
|
|
if (enabled != mouseIconEnabled)
|
|
{
|
|
mouseIconEnabled = enabled;
|
|
mouseIconEnabledEvent(mouseIconEnabled);
|
|
raisePropertyChanged(prop_MouseEnabled);
|
|
}
|
|
}
|
|
|
|
void UserInputService::setMouseType(MouseType value)
|
|
{
|
|
if (value != mouseType)
|
|
{
|
|
mouseType = value;
|
|
|
|
switch (mouseType)
|
|
{
|
|
case MOUSETYPE_DEFAULT:
|
|
{
|
|
setMouseWrapMode(UserInputService::WRAP_AUTO);
|
|
break;
|
|
}
|
|
case MOUSETYPE_LOCKCENTER:
|
|
{
|
|
if ( ControllerService* service = ServiceProvider::find<ControllerService>(this) )
|
|
{
|
|
if ( UserInputBase* userInput = service->getHardwareDevice() )
|
|
{
|
|
userInput->centerCursor();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case MOUSETYPE_LOCKCURRENT:
|
|
default: // intentional fall-thru
|
|
break;
|
|
}
|
|
|
|
raisePropertyChanged(prop_mouseType);
|
|
}
|
|
}
|
|
|
|
void UserInputService::setLastDownGuiObject(weak_ptr<GuiObject> object, InputObject::UserInputType type)
|
|
{
|
|
if (type == InputObject::TYPE_MOUSEBUTTON1)
|
|
{
|
|
lastDownGuiObjectLeft = object;
|
|
}
|
|
else if (type == InputObject::TYPE_MOUSEBUTTON2)
|
|
{
|
|
lastDownGuiObjectRight = object;
|
|
}
|
|
}
|
|
|
|
weak_ptr<GuiObject> UserInputService::getLastDownGuiObject(InputObject::UserInputType type)
|
|
{
|
|
if (type == InputObject::TYPE_MOUSEBUTTON1)
|
|
{
|
|
return lastDownGuiObjectLeft;
|
|
}
|
|
else if (type == InputObject::TYPE_MOUSEBUTTON2)
|
|
{
|
|
return lastDownGuiObjectRight;
|
|
}
|
|
GuiObject* none = NULL;
|
|
return weak_from(none);
|
|
}
|
|
|
|
UserInputService::WrapMode UserInputService::getMouseWrapMode() const
|
|
{
|
|
switch (mouseType)
|
|
{
|
|
case MOUSETYPE_LOCKCENTER:
|
|
case MOUSETYPE_LOCKCURRENT: // intentional fall-thru
|
|
{
|
|
if (canUseMouseLockCenter())
|
|
{
|
|
return ((mouseType == MOUSETYPE_LOCKCENTER) ? UserInputService::WRAP_NONEANDCENTER : UserInputService::WRAP_NONE);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return wrapMode;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Gamepad Functions
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
static void fireGamepadConnectionEvents(weak_ptr<DataModel> weakDM, bool isConnected, InputObject::UserInputType gamepadType)
|
|
{
|
|
if (shared_ptr<DataModel> sharedDM = weakDM.lock())
|
|
{
|
|
if (UserInputService* inputService = ServiceProvider::find<UserInputService>(sharedDM.get()))
|
|
{
|
|
inputService->setConnectedGamepad(gamepadType, isConnected);
|
|
|
|
isConnected ? inputService->gamepadConnectedSignal(gamepadType) :
|
|
inputService->gamepadDisconnectedSignal(gamepadType);
|
|
}
|
|
}
|
|
}
|
|
|
|
shared_ptr<const Reflection::ValueArray> UserInputService::getConnectedGamepads()
|
|
{
|
|
shared_ptr<Reflection::ValueArray> gamepadConnectedArray = rbx::make_shared<Reflection::ValueArray>();
|
|
for (boost::unordered_map<InputObject::UserInputType, bool >::iterator iter = connectedGamepadsMap.begin(); iter != connectedGamepadsMap.end(); ++iter)
|
|
{
|
|
if ((*iter).second)
|
|
{
|
|
gamepadConnectedArray->push_back((*iter).first);
|
|
}
|
|
}
|
|
|
|
return gamepadConnectedArray;
|
|
}
|
|
|
|
bool UserInputService::getGamepadConnected(InputObject::UserInputType gamepadType)
|
|
{
|
|
if (connectedGamepadsMap.find(gamepadType) != connectedGamepadsMap.end())
|
|
{
|
|
return connectedGamepadsMap[gamepadType];
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void UserInputService::setConnectedGamepad(InputObject::UserInputType gamepadType, bool isConnected)
|
|
{
|
|
connectedGamepadsMap[gamepadType] = isConnected;
|
|
|
|
for (boost::unordered_map<InputObject::UserInputType, bool >::iterator iter = connectedGamepadsMap.begin(); iter != connectedGamepadsMap.end(); ++iter)
|
|
{
|
|
if ((*iter).second)
|
|
{
|
|
setGamepadEnabled(true);
|
|
return;
|
|
}
|
|
}
|
|
|
|
setGamepadEnabled(false);
|
|
}
|
|
|
|
void UserInputService::safeFireGamepadConnected(InputObject::UserInputType gamepadType)
|
|
{
|
|
if (DataModel* dm = DataModel::get(this))
|
|
{
|
|
dm->submitTask(boost::bind(&fireGamepadConnectionEvents, weak_from(dm), true, gamepadType), DataModelJob::Write);
|
|
}
|
|
}
|
|
|
|
void UserInputService::safeFireGamepadDisconnected(InputObject::UserInputType gamepadType)
|
|
{
|
|
if (DataModel* dm = DataModel::get(this))
|
|
{
|
|
dm->submitTask(boost::bind(&fireGamepadConnectionEvents, weak_from(dm), false, gamepadType), DataModelJob::Write);
|
|
}
|
|
}
|
|
|
|
void UserInputService::setSupportedGamepadKeyCodes(InputObject::UserInputType gamepadType, shared_ptr<const Reflection::ValueArray> newSupportedGamepadKeyCodes)
|
|
{
|
|
supportedGamepadKeyCodes[gamepadType] = newSupportedGamepadKeyCodes;
|
|
}
|
|
shared_ptr<const Reflection::ValueArray> UserInputService::getSupportedGamepadKeyCodes(InputObject::UserInputType gamepadType)
|
|
{
|
|
// this signal is listened to by SDLGameController, which calls setSupportedGamepadKeyCodes
|
|
getSupportedGamepadKeyCodesSignal(gamepadType);
|
|
|
|
// after signal this has been set
|
|
return supportedGamepadKeyCodes[gamepadType];
|
|
}
|
|
bool UserInputService::gamepadSupports(InputObject::UserInputType gamepadType, RBX::KeyCode keyCode)
|
|
{
|
|
shared_ptr<const Reflection::ValueArray> gamepadKeyCodes = getSupportedGamepadKeyCodes(gamepadType);
|
|
const Reflection::Variant keyCodeVariant(keyCode);
|
|
|
|
if (gamepadKeyCodes)
|
|
{
|
|
for (Reflection::ValueArray::const_iterator iter = gamepadKeyCodes->begin(); iter != gamepadKeyCodes->end(); ++iter)
|
|
{
|
|
const Reflection::Variant iterVariant = (*iter);
|
|
if (iterVariant.isType<RBX::KeyCode>())
|
|
{
|
|
RBX::KeyCode iterKeyCode = iterVariant.cast<RBX::KeyCode>();
|
|
if (iterKeyCode == keyCode)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
shared_ptr<const Reflection::ValueArray> UserInputService::getGamepadState(InputObject::UserInputType gamepadType)
|
|
{
|
|
shared_ptr<Reflection::ValueArray> gamepadArray = rbx::make_shared<Reflection::ValueArray>();
|
|
|
|
if (GamepadService* gamepadService = ServiceProvider::create<RBX::GamepadService>(this))
|
|
{
|
|
Gamepad gamepad = gamepadService->getGamepadState(GamepadService::getGamepadIntForEnum(gamepadType));
|
|
|
|
for (Gamepad::iterator iter = gamepad.begin(); iter != gamepad.end(); ++iter)
|
|
{
|
|
shared_ptr<Instance> inputObject = (*iter).second;
|
|
gamepadArray->push_back(inputObject);
|
|
}
|
|
}
|
|
|
|
return gamepadArray;
|
|
}
|
|
|
|
shared_ptr<const Reflection::ValueArray> UserInputService::getNavigationGamepads()
|
|
{
|
|
shared_ptr<Reflection::ValueArray> gamepadNavigationArray = rbx::make_shared<Reflection::ValueArray>();
|
|
if (GamepadService* gamepadService = ServiceProvider::create<RBX::GamepadService>(this))
|
|
{
|
|
boost::unordered_map<InputObject::UserInputType, bool> navigationGamepadMap = gamepadService->getNavigationGamepadMap();
|
|
for (boost::unordered_map<InputObject::UserInputType, bool>::iterator iter = navigationGamepadMap.begin(); iter != navigationGamepadMap.end(); ++iter)
|
|
{
|
|
if ((*iter).second)
|
|
{
|
|
gamepadNavigationArray->push_back((*iter).first);
|
|
}
|
|
}
|
|
}
|
|
|
|
return gamepadNavigationArray;
|
|
}
|
|
|
|
bool UserInputService::isNavigationGamepad(InputObject::UserInputType gamepadType)
|
|
{
|
|
if (GamepadService* gamepadService = ServiceProvider::create<RBX::GamepadService>(this))
|
|
{
|
|
return gamepadService->isNavigationGamepad(gamepadType);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void UserInputService::setNavigationGamepad(InputObject::UserInputType gamepadType, bool enabled)
|
|
{
|
|
if (GamepadService* gamepadService = ServiceProvider::create<RBX::GamepadService>(this))
|
|
{
|
|
gamepadService->setNavigationGamepad(gamepadType, enabled);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Misc Input signals/functions
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
void UserInputService::sendJumpRequestEvent()
|
|
{
|
|
fireJumpRequestEvent = true;
|
|
}
|
|
|
|
bool UserInputService::canUseMouseLockCenter() const
|
|
{
|
|
Workspace* workspace = ServiceProvider::find<Workspace>(this);
|
|
|
|
return workspace && !workspace->hasModalGuiObjects() && !Profiler::isCapturingMouseInput();
|
|
}
|
|
|
|
void UserInputService::setVREnabled(bool value)
|
|
{
|
|
if (vrEnabled != value)
|
|
{
|
|
vrEnabled = value;
|
|
raisePropertyChanged(prop_VREnabled);
|
|
raisePropertyChanged(prop_IsVREnabled);
|
|
}
|
|
}
|
|
|
|
void UserInputService::setUserHeadCFrame(const CoordinateFrame& value)
|
|
{
|
|
if (userHeadCFrame != value)
|
|
{
|
|
userHeadCFrame = value;
|
|
raisePropertyChanged(prop_UserHeadCFrame);
|
|
}
|
|
}
|
|
|
|
void UserInputService::setUserCFrame(UserCFrame type, const CoordinateFrame& value)
|
|
{
|
|
if (userCFrame[type] != value)
|
|
{
|
|
userCFrame[type] = value;
|
|
userCFrameChanged(type, value);
|
|
}
|
|
}
|
|
|
|
void UserInputService::recenterUserHeadCFrame()
|
|
{
|
|
recenterUserHeadCFrameRequested = true;
|
|
}
|
|
|
|
bool UserInputService::checkAndClearRecenterUserHeadCFrameRequest()
|
|
{
|
|
bool result = recenterUserHeadCFrameRequested;
|
|
|
|
recenterUserHeadCFrameRequested = false;
|
|
|
|
return result;
|
|
}
|
|
|
|
} // Namespace RBX
|