// // 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 #ifdef __APPLE__ // needed for pasting ability on OS X #if !RBX_PLATFORM_IOS #import #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::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::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::EnumDesc() :EnumDescriptor("MouseBehavior") { addPair(UserInputService::MOUSETYPE_DEFAULT, "Default"); addPair(UserInputService::MOUSETYPE_LOCKCENTER, "LockCenter"); addPair(UserInputService::MOUSETYPE_LOCKCURRENT, "LockCurrentPosition"); } template<> EnumDesc::EnumDesc() :EnumDescriptor("OverrideMouseIconBehavior") { addPair(UserInputService::OVERRIDE_BEHAVIOR_NONE, "None"); addPair(UserInputService::OVERRIDE_BEHAVIOR_FORCESHOW, "ForceShow"); addPair(UserInputService::OVERRIDE_BEHAVIOR_FORCEHIDE, "ForceHide"); } template<> EnumDesc::EnumDesc() :EnumDescriptor("UserCFrame") { addPair(UserInputService::USERCFRAME_HEAD, "Head"); addPair(UserInputService::USERCFRAME_LEFTHAND, "LeftHand"); addPair(UserInputService::USERCFRAME_RIGHTHAND, "RightHand"); } template<> UserInputService::UserCFrame& Variant::convert(void) { return genericConvert(); } } // namespace Reflection template<> bool StringConverter::convertToValue(const std::string& text, UserInputService::UserCFrame& value) { return Reflection::EnumDesc::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 func_getPlatform(&UserInputService::getPlatformLua, "GetPlatform", Security::RobloxScript); static Reflection::BoundFuncDesc func_getLastInputType(&UserInputService::getLastInputType, "GetLastInputType", Security::None); static Reflection::EventDesc event_lastInputTypeChanged(&UserInputService::lastInputTypeChangedSignal, "LastInputTypeChanged", "lastInputType", Security::None); static const Reflection::PropDescriptor prop_TouchEnabled("TouchEnabled", category_Data, &UserInputService::getTouchEnabled, NULL); static const Reflection::PropDescriptor prop_KeyboardEnabled("KeyboardEnabled", category_Data, &UserInputService::getKeyboardEnabled, NULL); static const Reflection::PropDescriptor prop_MouseEnabled("MouseEnabled", category_Data, &UserInputService::getMouseEnabled, NULL); static const Reflection::PropDescriptor prop_GamepadEnabled("GamepadEnabled", category_Data, &UserInputService::getGamepadEnabled, NULL); //todo: Remove these events/functions static Reflection::EventDesc event_JumpRequest(&UserInputService::jumpRequestEvent, "JumpRequest", Security::None); Reflection::PropDescriptor UserInputService::prop_ModalEnabled("ModalEnabled", category_Behavior, &UserInputService::getModalEnabled, &UserInputService::setModalEnabled, Reflection::PropertyDescriptor::SCRIPTING, Security::None); // High Level touch events static Reflection::EventDesc, bool)> event_TapGesture(&UserInputService::tapGestureEvent, "TouchTap", "touchPositions", "gameProcessedEvent", Security::None); static Reflection::EventDesc,float,float,InputObject::UserInputState, bool)> event_PinchGesture(&UserInputService::pinchGestureEvent, "TouchPinch","touchPositions", "scale", "velocity", "state","gameProcessedEvent", Security::None); static Reflection::EventDesc event_SwipeGesture(&UserInputService::swipeGestureEvent, "TouchSwipe", "swipeDirection", "numberOfTouches", "gameProcessedEvent", Security::None); static Reflection::EventDesc, InputObject::UserInputState, bool)> event_LongPressGesture(&UserInputService::longPressGestureEvent, "TouchLongPress", "touchPositions", "state", "gameProcessedEvent", Security::None); static Reflection::EventDesc, float,float,InputObject::UserInputState, bool)> event_RotateGesture(&UserInputService::rotateGestureEvent, "TouchRotate", "touchPositions", "rotation", "velocity", "state", "gameProcessedEvent", Security::None); static Reflection::EventDesc, 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, bool), rbx::signal, bool)>, rbx::signal, bool)>* (UserInputService::*)(bool)> event_TouchStarted(&UserInputService::getTouchBeganEvent, "TouchStarted", "touch", "gameProcessedEvent", Security::None); static Reflection::EventDesc, bool), rbx::signal, bool)>, rbx::signal, bool)>* (UserInputService::*)(bool)> event_TouchMoved(&UserInputService::getTouchChangedEvent, "TouchMoved", "touch", "gameProcessedEvent", Security::None); static Reflection::EventDesc, bool), rbx::signal, bool)>, rbx::signal, bool)>* (UserInputService::*)(bool)> event_TouchEnded(&UserInputService::getTouchEndedEvent, "TouchEnded", "touch", "gameProcessedEvent", Security::None); // Low Level Generic input events Reflection::EventDesc, bool), rbx::signal, bool)>, rbx::signal, bool)>* (UserInputService::*)(bool)> UserInputService::event_InputBegin(&UserInputService::getInputBeganEvent, "InputBegan", "input", "gameProcessedEvent", Security::None); Reflection::EventDesc, bool), rbx::signal, bool)>, rbx::signal, bool)>* (UserInputService::*)(bool)> UserInputService::event_InputUpdate(&UserInputService::getInputChangedEvent, "InputChanged", "input", "gameProcessedEvent", Security::None); Reflection::EventDesc, bool), rbx::signal, bool)>, rbx::signal, bool)>* (UserInputService::*)(bool)> UserInputService::event_InputEnd(&UserInputService::getInputEndedEvent, "InputEnded", "input", "gameProcessedEvent", Security::None); // Textbox Stuff static Reflection::EventDesc)> event_TextboxFocus(&UserInputService::textBoxGainFocus, "TextBoxFocused", "textboxFocused", Security::None); static Reflection::EventDesc)> event_TextboxRelease(&UserInputService::textBoxReleaseFocus, "TextBoxFocusReleased", "textboxReleased", Security::None); static Reflection::BoundFuncDesc()> func_getFocusedTextBox(&UserInputService::getFocusedTextBox, "GetFocusedTextBox", Security::None); // Window Stuff static Reflection::EventDesc event_windowFocused(&UserInputService::windowFocused, "WindowFocused", Security::None); static Reflection::EventDesc event_windowFocusReleased(&UserInputService::windowFocusReleased, "WindowFocusReleased", Security::None); // Mouse Stuff static const Reflection::PropDescriptor prop_mouseIconEnabled("MouseIconEnabled", category_Data, &UserInputService::getMouseIconEnabled, &UserInputService::setMouseIconEnabled); static const Reflection::EnumPropDescriptor prop_overrideMouseIconBehavior("OverrideMouseIconBehavior", category_Data, &UserInputService::getOverrideMouseIconBehavior, &UserInputService::setOverrideMouseIconBehavior, Reflection::PropertyDescriptor::SCRIPTING, Security::RobloxScript); static const Reflection::EnumPropDescriptor prop_mouseType("MouseBehavior", category_Data, &UserInputService::getMouseType, &UserInputService::setMouseType); // Keyboard Stuff static Reflection::BoundFuncDesc()> func_getKeyboardState(&UserInputService::getKeyboardState, "GetKeysPressed", Security::None); static Reflection::BoundFuncDesc func_isKeyCodeDown(&UserInputService::isKeyDown, "IsKeyDown", "keyCode", Security::None); // Motion Stuff static Reflection::EventDesc), rbx::signal)>, rbx::signal)>* (UserInputService::*)(bool)> event_AccelerometerChanged(&UserInputService::getOrCreateScriptAccelerometerEventSignal, "DeviceAccelerationChanged","acceleration", Security::None); static Reflection::EventDesc), rbx::signal)>, rbx::signal)>* (UserInputService::*)(bool)> event_gravityChanged(&UserInputService::getOrCreateScriptGravityEventSignal, "DeviceGravityChanged","gravity", Security::None); static Reflection::EventDesc, CoordinateFrame), rbx::signal, CoordinateFrame)>, rbx::signal, CoordinateFrame)>* (UserInputService::*)(bool)> event_GyroChanged(&UserInputService::getOrCreateScriptGyroEventSignal, "DeviceRotationChanged","rotation","cframe", Security::None); static Reflection::BoundFuncDesc(void)> func_getCurrentAcceleration(&UserInputService::getAcceleration, "GetDeviceAcceleration", Security::None); static Reflection::BoundFuncDesc(void)> func_getCurrentGravity(&UserInputService::getGravity, "GetDeviceGravity", Security::None); static Reflection::BoundFuncDesc(void)> func_getCurrentRotation(&UserInputService::getRotation, "GetDeviceRotation", Security::None); static const Reflection::PropDescriptor prop_accelerometerEnabled("AccelerometerEnabled", category_Data, &UserInputService::getAccelerometerEnabled, NULL); static const Reflection::PropDescriptor prop_gyroscopeEnabled("GyroscopeEnabled", category_Data, &UserInputService::getGyroscopeEnabled, NULL); // Gamepad Stuff static Reflection::BoundFuncDesc()> func_getConnectedGamepads(&UserInputService::getConnectedGamepads, "GetConnectedGamepads", Security::None); static Reflection::BoundFuncDesc func_getGamepadConnected(&UserInputService::getGamepadConnected, "GetGamepadConnected", "gamepadNum", Security::None); static Reflection::EventDesc event_gamepadConnected(&UserInputService::gamepadDisconnectedSignal, "GamepadDisconnected", "gamepadNum", Security::None); static Reflection::EventDesc event_gamepadDisconnected(&UserInputService::gamepadConnectedSignal, "GamepadConnected", "gamepadNum", Security::None); static Reflection::BoundFuncDesc(InputObject::UserInputType)> func_getGamepadState(&UserInputService::getGamepadState, "GetGamepadState", "gamepadNum", Security::None); static Reflection::BoundFuncDesc func_getgamepadSupportsKeyCode(&UserInputService::gamepadSupports, "GamepadSupports", "gamepadNum", "gamepadKeyCode", Security::None); static Reflection::BoundFuncDesc(InputObject::UserInputType)> func_getSupportedGamepadKeyCodes(&UserInputService::getSupportedGamepadKeyCodes, "GetSupportedGamepadKeyCodes", "gamepadNum", Security::None); static Reflection::BoundFuncDesc()> func_getNavigationGamepads(&UserInputService::getNavigationGamepads, "GetNavigationGamepads", Security::None); static Reflection::BoundFuncDesc func_isNavigationGamepad(&UserInputService::isNavigationGamepad, "IsNavigationGamepad", "gamepadEnum", Security::None); static Reflection::BoundFuncDesc func_setNavigationGamepad(&UserInputService::setNavigationGamepad, "SetNavigationGamepad", "gamepadEnum", "enabled", Security::None); // VR Stuff static const Reflection::PropDescriptor prop_IsVREnabled("IsVREnabled", category_Data, &UserInputService::getVREnabled, NULL, Reflection::PropertyDescriptor::Attributes::deprecated()); static const Reflection::PropDescriptor prop_UserHeadCFrame("UserHeadCFrame", category_Data, &UserInputService::getUserHeadCFrame, NULL, Reflection::PropertyDescriptor::Attributes::deprecated()); static const Reflection::PropDescriptor prop_VREnabled("VREnabled", category_Data, &UserInputService::getVREnabled, NULL); static const Reflection::BoundFuncDesc func_RecenterUserHeadCFrame(&UserInputService::recenterUserHeadCFrame, "RecenterUserHeadCFrame", Security::None); static const Reflection::BoundFuncDesc func_GetUserCFrame(&UserInputService::getUserCFrameLua, "GetUserCFrame", "type", Security::None); static const Reflection::EventDesc 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::create(); 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::create(InputObject::TYPE_GYRO, InputObject::INPUT_STATE_CHANGE, Vector3(0,0,0), SDLK_UNKNOWN, RBX::DataModel::get(newProvider)); accelerationInputObject = RBX::Creatable::create(InputObject::TYPE_ACCELEROMETER, InputObject::INPUT_STATE_CHANGE, Vector3(0,0,0), SDLK_UNKNOWN, RBX::DataModel::get(newProvider)); gravityInputObject = RBX::Creatable::create(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::create(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::create(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::create(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, bool)>* UserInputService::getInputBeganEvent(bool whatever) { return RBX::Security::Context::current().hasPermission(RBX::Security::RobloxScript) ? &coreInputBeganEvent : &inputBeganEvent; } rbx::signal, bool)>* UserInputService::getInputChangedEvent(bool whatever) { return RBX::Security::Context::current().hasPermission(RBX::Security::RobloxScript) ? &coreInputUpdatedEvent : &inputUpdatedEvent; } rbx::signal, bool)>* UserInputService::getInputEndedEvent(bool whatever) { return RBX::Security::Context::current().hasPermission(RBX::Security::RobloxScript) ? &coreInputEndedEvent : &inputEndedEvent; } rbx::signal, bool)>* UserInputService::getTouchBeganEvent(bool whatever) { return RBX::Security::Context::current().hasPermission(RBX::Security::RobloxScript) ? &coreTouchStartedEvent : &touchStartedEvent; } rbx::signal, bool)>* UserInputService::getTouchChangedEvent(bool whatever) { return RBX::Security::Context::current().hasPermission(RBX::Security::RobloxScript) ? &coreTouchMovedEvent : &touchMovedEvent; } rbx::signal, 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, 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, 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(dataModel)) { menuIsOpen = guiService->getMenuOpen(); } signalInputEventOnService(inputObject, inputState, processedEvent, menuIsOpen); } void UserInputService::dataModelProcessInput(const shared_ptr& 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) { 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) { 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(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::create(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 newObj = drawTouchFrame->clone(RBX::ScriptingCreator); Frame* newFrame = Instance::fastDynamicCast(newObj.get()); Vector2 framePos = (*iter).first->get2DPosition(); newFrame->setPosition(UDim2(0,framePos.x,0,framePos.y)); newFrame->setBackgroundColor3(DrawTouchColor); shared_ptr dataModel = shared_from(DataModel::get(this)); if (CoreGuiService* cgs = dataModel->find()) { 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(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 dummyReturnInput = RBX::Creatable::create(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()); } } } void UserInputService::textboxFocused(shared_ptr textBox) { currentTextBox = textBox; } void UserInputService::textboxFocusReleased(shared_ptr textBox) { currentTextBox = shared_ptr(); } shared_ptr UserInputService::getFocusedTextBox() { if (!DFFlag::GetFocusedTextBoxEnabled) { RBX::StandardOut::singleton()->printf(MESSAGE_ERROR, "UserInputService:GetFocusedTextBox() is not yet enabled"); return shared_ptr(); } 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 = (*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(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 weakDataModel, bool isJump) { if(shared_ptr 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 weakDataModel, Vector2 walkDir, float walkDelta) { if(shared_ptr 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& event) { boost::mutex::scoped_lock lock(ToolEventsMutex); toolEventsVector.push_back(event); } static void doCameraRotate(float panAmount, float tiltAmount, weak_ptr weakDataModel) { if(shared_ptr 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 weakDataModel) { if(shared_ptr 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 = 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()) { if (Instance* parent = cgs->findFirstChildByName("RobloxGui")) { if (Instance* controlFrame = parent->findFirstChildByName("ControlFrame")) { parent = controlFrame; } if (BindableFunction* toggle = Instance::fastDynamicCast(parent->findFirstChildByName("ToggleDevConsole"))) { toggle->invoke(rbx::make_shared(), boost::bind(&doNothing, _1), boost::bind(&doNothing, _1)); } } } retVal = true; } return retVal; } void UserInputService::textboxDidFinishEditing(const char* text, bool shouldReturn) { shared_ptr textString(new std::string(text)); boost::mutex::scoped_lock lock(TextboxEventsMutex); textboxFinishedVector.push_back(std::pair, bool>(textString,shouldReturn)); } ////////////////////////////////////////////////////////////////////////// // Begin Gesture Input signals/functions ////////////////////////////////////////////////////////////////////////// void UserInputService::addGestureEventToProcess(const UserInputService::Gesture gesture, shared_ptr& touchPositions, shared_ptr& args) { if(touchPositions && args) { boost::mutex::scoped_lock lock(GestureEventsMutex); gestureEventsToProcess.push_back(std::pair,shared_ptr > >(gesture, std::pair, shared_ptr >(touchPositions, args))); } } void UserInputService::fireGestureEvent(const UserInputService::Gesture gesture, const shared_ptr& touchPositions, const shared_ptr& 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(),args->values[1].cast(), wasSunk); break; case UserInputService::GESTURE_ROTATE: RBXASSERT(args->values.size() == 3); rotateGestureEvent(touchPositions,args->values[0].cast(),args->values[1].cast(), args->values[2].cast(), wasSunk); break; case UserInputService::GESTURE_PINCH: RBXASSERT(args->values.size() == 3); pinchGestureEvent(touchPositions,args->values[0].cast(),args->values[1].cast(), args->values[2].cast(), wasSunk); break; case UserInputService::GESTURE_LONGPRESS: RBXASSERT(args->values.size() == 1); longPressGestureEvent(touchPositions,args->values[0].cast(), wasSunk); break; case UserInputService::GESTURE_PAN: RBXASSERT(args->values.size() == 3); panGestureEvent(touchPositions,args->values[0].cast(),args->values[1].cast(),args->values[2].cast(), 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(this)) { guiOffset = guiService->getGlobalGuiInset().xy(); } for (GestureEventsVector::iterator iter = tmpGestureEventsMap.begin(); iter != tmpGestureEventsMap.end(); ++iter) { GuiResponse response; shared_ptr 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 touchPosition = (*valueIter).cast(); touchPosition -= guiOffset; *valueIter = touchPosition; } } } if(CoreGuiService* coreGui = ServiceProvider::find(this)) response = coreGui->processGesture((*iter).first, touchPositions, (*iter).second.second); if (!response.wasSunk()) { if (RBX::Network::Players* players = ServiceProvider::create(this)) if (RBX::Network::Player* player = players->getLocalPlayer()) if (PlayerGui* playerGui = player->findFirstChildOfType()) 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, 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, 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 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, void* nativeInputObject) { dataModelProcessInput(inputObject, nativeInputObject, inputObject->getUserInputState()); fireLegacyMouseEvent(inputObject, nativeInputObject, true); } void UserInputService::fireLegacyMouseEvent(const shared_ptr& 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, void* nativeInputObject, bool processed) { if(inputObject && inputObject.get()) { if (!inputObject->isPublicEvent()) { return; } { boost::mutex::scoped_lock lock(InputEventsMutex); std::pair, 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 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::create(inputObject->getUserInputType(), inputObject->getUserInputState(), inputObject->getRawPosition(), inputObject->getDelta(), DataModel::get(this)); } eventPair = std::pair, void*>( mouseEventObject, nativeInputObject); } } else { eraseInputTypeFromInputVector(inputObject->getUserInputType(), changeEventsToProcess); } } if (!processed) changeEventsToProcess.push_back(eventPair); } } } fireLegacyMouseEvent(inputObject, nativeInputObject); } } ////////////////////////////////////////////////////////////////////////// // Begin Motion Input signals/functions ////////////////////////////////////////////////////////////////////////// shared_ptr UserInputService::getAcceleration() { return accelerationInputObject; } shared_ptr UserInputService::getRotation() { shared_ptr returnValues(new Reflection::Tuple(2)); returnValues->values[0] = rotationInputObject; returnValues->values[1] = rotationCFrame; return returnValues; } shared_ptr UserInputService::getGravity() { return gravityInputObject; } void UserInputService::fireAccelerationEvent(const RBX::Vector3& newAcceleration) { boost::mutex::scoped_lock lock(MotionEventsMutex); if (InputObject* accelInput = Instance::fastDynamicCast(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(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(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, 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)>* 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)>* 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& 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 UserInputService::getCommandModCodes() { std::vector 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 >::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 tmpKeyboardEventsVector; { boost::mutex::scoped_lock lock(KeyboardEventsMutex); keyboardEventsToProcess.swap(tmpKeyboardEventsVector); } for (std::vector::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 inputObjectToFire = shared_ptr(); if (newKeyState.find(keyCode) == newKeyState.end() ) { newKeyState[keyCode] = RBX::Creatable::create(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 = 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 >::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 UserInputService::getKeyboardState() { shared_ptr keyboardArray = rbx::make_shared(); for (boost::unordered_map >::iterator iter = newKeyState.begin(); iter != newKeyState.end(); ++iter) { if ((*iter).second->getUserInputState() == InputObject::INPUT_STATE_BEGIN) { shared_ptr inputObject = (*iter).second; keyboardArray->push_back(inputObject); } } return keyboardArray; } Vector2 UserInputService::getKeyboardGuiSelectionDirection(const shared_ptr& 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(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(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 object, InputObject::UserInputType type) { if (type == InputObject::TYPE_MOUSEBUTTON1) { lastDownGuiObjectLeft = object; } else if (type == InputObject::TYPE_MOUSEBUTTON2) { lastDownGuiObjectRight = object; } } weak_ptr 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 weakDM, bool isConnected, InputObject::UserInputType gamepadType) { if (shared_ptr sharedDM = weakDM.lock()) { if (UserInputService* inputService = ServiceProvider::find(sharedDM.get())) { inputService->setConnectedGamepad(gamepadType, isConnected); isConnected ? inputService->gamepadConnectedSignal(gamepadType) : inputService->gamepadDisconnectedSignal(gamepadType); } } } shared_ptr UserInputService::getConnectedGamepads() { shared_ptr gamepadConnectedArray = rbx::make_shared(); for (boost::unordered_map::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::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 newSupportedGamepadKeyCodes) { supportedGamepadKeyCodes[gamepadType] = newSupportedGamepadKeyCodes; } shared_ptr 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 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 iterKeyCode = iterVariant.cast(); if (iterKeyCode == keyCode) { return true; } } } } return false; } shared_ptr UserInputService::getGamepadState(InputObject::UserInputType gamepadType) { shared_ptr gamepadArray = rbx::make_shared(); if (GamepadService* gamepadService = ServiceProvider::create(this)) { Gamepad gamepad = gamepadService->getGamepadState(GamepadService::getGamepadIntForEnum(gamepadType)); for (Gamepad::iterator iter = gamepad.begin(); iter != gamepad.end(); ++iter) { shared_ptr inputObject = (*iter).second; gamepadArray->push_back(inputObject); } } return gamepadArray; } shared_ptr UserInputService::getNavigationGamepads() { shared_ptr gamepadNavigationArray = rbx::make_shared(); if (GamepadService* gamepadService = ServiceProvider::create(this)) { boost::unordered_map navigationGamepadMap = gamepadService->getNavigationGamepadMap(); for (boost::unordered_map::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(this)) { return gamepadService->isNavigationGamepad(gamepadType); } return false; } void UserInputService::setNavigationGamepad(InputObject::UserInputType gamepadType, bool enabled) { if (GamepadService* gamepadService = ServiceProvider::create(this)) { gamepadService->setNavigationGamepad(gamepadType, enabled); } } ////////////////////////////////////////////////////////////////////////// // Misc Input signals/functions ////////////////////////////////////////////////////////////////////////// void UserInputService::sendJumpRequestEvent() { fireJumpRequestEvent = true; } bool UserInputService::canUseMouseLockCenter() const { Workspace* workspace = ServiceProvider::find(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