From fad623ae48c2da5a9d9098dde925480c03658f1e Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Mon, 4 May 2015 10:47:33 -0700 Subject: [PATCH] ANd really add the 2 files we need --- interface/src/devices/KeyboardMouseDevice.cpp | 253 ++++++++++++++++++ interface/src/devices/KeyboardMouseDevice.h | 108 ++++++++ interface/src/ui/UserInputMapper.cpp | 2 +- 3 files changed, 362 insertions(+), 1 deletion(-) create mode 100755 interface/src/devices/KeyboardMouseDevice.cpp create mode 100755 interface/src/devices/KeyboardMouseDevice.h diff --git a/interface/src/devices/KeyboardMouseDevice.cpp b/interface/src/devices/KeyboardMouseDevice.cpp new file mode 100755 index 0000000000..94994462c3 --- /dev/null +++ b/interface/src/devices/KeyboardMouseDevice.cpp @@ -0,0 +1,253 @@ + +// +// KeyboardMouseDevice.cpp +// interface/src/devices +// +// Created by Sam Gateau on 4/27/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "KeyboardMouseDevice.h" + +void KeyboardMouseDevice::update() { + _axisStateMap.clear(); + + // For touch event, we need to check that the last event is not too long ago + // Maybe it's a Qt issue, but the touch event sequence (begin, update, end) is not always called properly + // The following is a workaround to detect that the touch sequence is over in case we didn;t see the end event + if (_isTouching) { + const auto TOUCH_EVENT_MAXIMUM_WAIT = 100; //ms + auto currentTime = _clock.now(); + auto sinceLastTouch = std::chrono::duration_cast(currentTime - _lastTouchTime); + if (sinceLastTouch.count() > TOUCH_EVENT_MAXIMUM_WAIT) { + _isTouching = false; + } + } +} + +void KeyboardMouseDevice::focusOutEvent(QFocusEvent* event) { + _buttonPressedMap.clear(); +}; + +void KeyboardMouseDevice::keyPressEvent(QKeyEvent* event) { + auto input = makeInput((Qt::Key) event->key()); + auto result = _buttonPressedMap.insert(input.getChannel()); + if (!result.second) { + // key pressed again ? without catching the release event ? + } +} + +void KeyboardMouseDevice::keyReleaseEvent(QKeyEvent* event) { + auto input = makeInput((Qt::Key) event->key()); + _buttonPressedMap.erase(input.getChannel()); +} + +void KeyboardMouseDevice::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { + auto input = makeInput((Qt::MouseButton) event->button()); + auto result = _buttonPressedMap.insert(input.getChannel()); + if (!result.second) { + // key pressed again ? without catching the release event ? + } + _lastCursor = event->pos(); +} + +void KeyboardMouseDevice::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { + auto input = makeInput((Qt::MouseButton) event->button()); + _buttonPressedMap.erase(input.getChannel()); +} + +void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { + QPoint currentPos = event->pos(); + QPoint currentMove = currentPos - _lastCursor; + + _axisStateMap[makeInput(MOUSE_AXIS_X_POS).getChannel()] = (currentMove.x() > 0 ? currentMove.x() : 0.0f); + _axisStateMap[makeInput(MOUSE_AXIS_X_NEG).getChannel()] = (currentMove.x() < 0 ? -currentMove.x() : 0.0f); + // Y mouse is inverted positive is pointing up the screen + _axisStateMap[makeInput(MOUSE_AXIS_Y_POS).getChannel()] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f); + _axisStateMap[makeInput(MOUSE_AXIS_Y_NEG).getChannel()] = (currentMove.y() > 0 ? currentMove.y() : 0.0f); + + _lastCursor = currentPos; +} + +void KeyboardMouseDevice::wheelEvent(QWheelEvent* event) { + auto currentMove = event->angleDelta() / 120.0f; + + _axisStateMap[makeInput(MOUSE_AXIS_WHEEL_X_POS).getChannel()] = (currentMove.x() > 0 ? currentMove.x() : 0.0f); + _axisStateMap[makeInput(MOUSE_AXIS_WHEEL_X_NEG).getChannel()] = (currentMove.x() < 0 ? -currentMove.x() : 0.0f); + _axisStateMap[makeInput(MOUSE_AXIS_WHEEL_Y_POS).getChannel()] = (currentMove.y() > 0 ? currentMove.y() : 0.0f); + _axisStateMap[makeInput(MOUSE_AXIS_WHEEL_Y_NEG).getChannel()] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f); +} + +glm::vec2 KeyboardMouseDevice::evalAverageTouchPoints(const QList& points) const { + glm::vec2 averagePoint(0.0f); + if (points.count() > 0) { + for (auto& point : points) { + averagePoint += glm::vec2(point.pos().x(), point.pos().y()); + } + averagePoint /= (float)(points.count()); + } + return averagePoint; +} + +void KeyboardMouseDevice::touchBeginEvent(const QTouchEvent* event) { + _isTouching = event->touchPointStates().testFlag(Qt::TouchPointPressed); + _lastTouch = evalAverageTouchPoints(event->touchPoints()); + _lastTouchTime = _clock.now(); +} + +void KeyboardMouseDevice::touchEndEvent(const QTouchEvent* event) { + _isTouching = false; + _lastTouch = evalAverageTouchPoints(event->touchPoints()); + _lastTouchTime = _clock.now(); +} + +void KeyboardMouseDevice::touchUpdateEvent(const QTouchEvent* event) { + auto currentPos = evalAverageTouchPoints(event->touchPoints()); + _lastTouchTime = _clock.now(); + + if (!_isTouching) { + _isTouching = event->touchPointStates().testFlag(Qt::TouchPointPressed); + } else { + auto currentMove = currentPos - _lastTouch; + + _axisStateMap[makeInput(TOUCH_AXIS_X_POS).getChannel()] = (currentMove.x > 0 ? currentMove.x : 0.0f); + _axisStateMap[makeInput(TOUCH_AXIS_X_NEG).getChannel()] = (currentMove.x < 0 ? -currentMove.x : 0.0f); + // Y mouse is inverted positive is pointing up the screen + _axisStateMap[makeInput(TOUCH_AXIS_Y_POS).getChannel()] = (currentMove.y < 0 ? -currentMove.y : 0.0f); + _axisStateMap[makeInput(TOUCH_AXIS_Y_NEG).getChannel()] = (currentMove.y > 0 ? currentMove.y : 0.0f); + } + + _lastTouch = currentPos; +} + +UserInputMapper::Input KeyboardMouseDevice::makeInput(Qt::Key code) { + return UserInputMapper::Input(_deviceID, code & KEYBOARD_MASK, UserInputMapper::ChannelType::BUTTON); +} + +UserInputMapper::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code) { + switch (code) { + case Qt::LeftButton: + return UserInputMapper::Input(_deviceID, MOUSE_BUTTON_LEFT, UserInputMapper::ChannelType::BUTTON); + case Qt::RightButton: + return UserInputMapper::Input(_deviceID, MOUSE_BUTTON_RIGHT, UserInputMapper::ChannelType::BUTTON); + case Qt::MiddleButton: + return UserInputMapper::Input(_deviceID, MOUSE_BUTTON_MIDDLE, UserInputMapper::ChannelType::BUTTON); + default: + return UserInputMapper::Input(); + }; +} + +UserInputMapper::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::MouseAxisChannel axis) { + return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); +} + +UserInputMapper::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchAxisChannel axis) { + return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); +} + +UserInputMapper::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchButtonChannel button) { + return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); +} + +void KeyboardMouseDevice::registerToUserInputMapper(UserInputMapper& mapper) { + // Grab the current free device ID + _deviceID = mapper.getFreeDeviceID(); + + auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy()); + proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input._channel); }; + proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input._channel); }; + mapper.registerDevice(_deviceID, proxy); +} + +void KeyboardMouseDevice::assignDefaultInputMapping(UserInputMapper& mapper) { + const float BUTTON_MOVE_SPEED = 1.0f; + const float BUTTON_YAW_SPEED = 0.75f; + const float BUTTON_PITCH_SPEED = 0.5f; + const float MOUSE_YAW_SPEED = 0.5f; + const float MOUSE_PITCH_SPEED = 0.25f; + const float TOUCH_YAW_SPEED = 0.5f; + const float TOUCH_PITCH_SPEED = 0.25f; + //const float BUTTON_BOOM_SPEED = 0.1f; + + // AWSD keys mapping + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(Qt::Key_S), BUTTON_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(Qt::Key_W), BUTTON_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_A), BUTTON_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_D), BUTTON_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(Qt::Key_C), BUTTON_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(Qt::Key_E), BUTTON_MOVE_SPEED); + + // mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(Qt::Key_W), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED); + // mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(Qt::Key_S), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED); + mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(Qt::Key_A), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED); + mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(Qt::Key_D), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED); + mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(Qt::Key_C), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED); + mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(Qt::Key_E), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED); + + // Arrow keys mapping + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(Qt::Key_Down), BUTTON_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(Qt::Key_Up), BUTTON_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_Left), BUTTON_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_Right), BUTTON_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(Qt::Key_PageDown), BUTTON_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(Qt::Key_PageUp), BUTTON_MOVE_SPEED); + + // mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(Qt::Key_Up), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED); + // mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(Qt::Key_Down), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED); + mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(Qt::Key_Left), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED); + mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(Qt::Key_Right), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED); + mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(Qt::Key_Down), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED); + mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(Qt::Key_Up), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED); + + // Mouse move + mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(MOUSE_AXIS_Y_NEG), makeInput(Qt::RightButton), MOUSE_PITCH_SPEED); + mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(MOUSE_AXIS_Y_POS), makeInput(Qt::RightButton), MOUSE_PITCH_SPEED); + mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(MOUSE_AXIS_X_NEG), makeInput(Qt::RightButton), MOUSE_YAW_SPEED); + mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(MOUSE_AXIS_X_POS), makeInput(Qt::RightButton), MOUSE_YAW_SPEED); + + +#ifdef Q_OS_MAC + // wheel event modifier on Mac collide with the touchpad scroll event + mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(TOUCH_AXIS_Y_NEG), TOUCH_PITCH_SPEED); + mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(TOUCH_AXIS_Y_POS), TOUCH_PITCH_SPEED); + mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(TOUCH_AXIS_X_NEG), TOUCH_YAW_SPEED); + mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(TOUCH_AXIS_X_POS), TOUCH_YAW_SPEED); +#else + // Touch pad yaw pitch + mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(TOUCH_AXIS_Y_NEG), TOUCH_PITCH_SPEED); + mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(TOUCH_AXIS_Y_POS), TOUCH_PITCH_SPEED); + mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(TOUCH_AXIS_X_NEG), TOUCH_YAW_SPEED); + mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(TOUCH_AXIS_X_POS), TOUCH_YAW_SPEED); + + // Wheel move + //mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(MOUSE_AXIS_WHEEL_Y_NEG), BUTTON_BOOM_SPEED); + //mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(MOUSE_AXIS_WHEEL_Y_POS), BUTTON_BOOM_SPEED); + mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(MOUSE_AXIS_WHEEL_X_NEG), BUTTON_YAW_SPEED); + mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(MOUSE_AXIS_WHEEL_X_POS), BUTTON_YAW_SPEED); + +#endif + +} + +float KeyboardMouseDevice::getButton(int channel) const { + if (!_buttonPressedMap.empty()) { + if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) { + return 1.0f; + } else { + return 0.0f; + } + } + return 0.0f; +} + +float KeyboardMouseDevice::getAxis(int channel) const { + auto axis = _axisStateMap.find(channel); + if (axis != _axisStateMap.end()) { + return (*axis).second; + } else { + return 0.0f; + } +} + diff --git a/interface/src/devices/KeyboardMouseDevice.h b/interface/src/devices/KeyboardMouseDevice.h new file mode 100755 index 0000000000..ce044b2a13 --- /dev/null +++ b/interface/src/devices/KeyboardMouseDevice.h @@ -0,0 +1,108 @@ +// +// KeyboardMouseDevice.h +// interface/src/devices +// +// Created by Sam Gateau on 4/27/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_KeyboardMouseDevice_h +#define hifi_KeyboardMouseDevice_h + +#include +#include +#include "ui/UserInputMapper.h" + +class KeyboardMouseDevice { +public: + + enum KeyboardChannel { + KEYBOARD_FIRST = 0, + KEYBOARD_LAST = 255, + KEYBOARD_MASK = 0x00FF, + }; + + enum MouseButtonChannel { + MOUSE_BUTTON_LEFT = KEYBOARD_LAST + 1, + MOUSE_BUTTON_RIGHT, + MOUSE_BUTTON_MIDDLE, + }; + + enum MouseAxisChannel { + MOUSE_AXIS_X_POS = MOUSE_BUTTON_MIDDLE + 1, + MOUSE_AXIS_X_NEG, + MOUSE_AXIS_Y_POS, + MOUSE_AXIS_Y_NEG, + MOUSE_AXIS_WHEEL_Y_POS, + MOUSE_AXIS_WHEEL_Y_NEG, + MOUSE_AXIS_WHEEL_X_POS, + MOUSE_AXIS_WHEEL_X_NEG, + }; + + enum TouchAxisChannel { + TOUCH_AXIS_X_POS = MOUSE_AXIS_WHEEL_X_NEG + 1, + TOUCH_AXIS_X_NEG, + TOUCH_AXIS_Y_POS, + TOUCH_AXIS_Y_NEG, + }; + + enum TouchButtonChannel { + TOUCH_BUTTON_PRESS = TOUCH_AXIS_Y_NEG + 1, + }; + + typedef std::unordered_set ButtonPressedMap; + typedef std::map AxisStateMap; // 8 axes + + void focusOutEvent(QFocusEvent* event); + + void keyPressEvent(QKeyEvent* event); + void keyReleaseEvent(QKeyEvent* event); + + void mouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0); + void mousePressEvent(QMouseEvent* event, unsigned int deviceID = 0); + void mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0); + + void touchBeginEvent(const QTouchEvent* event); + void touchEndEvent(const QTouchEvent* event); + void touchUpdateEvent(const QTouchEvent* event); + + void wheelEvent(QWheelEvent* event); + + // Get current state for each channels + float getButton(int channel) const; + float getAxis(int channel) const; + + // Let's make it easy for Qt because we assume we love Qt forever + UserInputMapper::Input makeInput(Qt::Key code); + UserInputMapper::Input makeInput(Qt::MouseButton code); + UserInputMapper::Input makeInput(KeyboardMouseDevice::MouseAxisChannel axis); + UserInputMapper::Input makeInput(KeyboardMouseDevice::TouchAxisChannel axis); + UserInputMapper::Input makeInput(KeyboardMouseDevice::TouchButtonChannel button); + + KeyboardMouseDevice() {} + + void registerToUserInputMapper(UserInputMapper& mapper); + void assignDefaultInputMapping(UserInputMapper& mapper); + + // Update call MUST be called once per simulation loop + // It takes care of updating the action states and deltas + void update(); + +protected: + ButtonPressedMap _buttonPressedMap; + AxisStateMap _axisStateMap; + + int _deviceID = 0; + QPoint _lastCursor; + glm::vec2 _lastTouch; + bool _isTouching = false; + + glm::vec2 evalAverageTouchPoints(const QList& points) const; + std::chrono::high_resolution_clock _clock; + std::chrono::high_resolution_clock::time_point _lastTouchTime; +}; + +#endif // hifi_KeyboardMouseDevice_h diff --git a/interface/src/ui/UserInputMapper.cpp b/interface/src/ui/UserInputMapper.cpp index c1f9577efe..892ab6a9b6 100755 --- a/interface/src/ui/UserInputMapper.cpp +++ b/interface/src/ui/UserInputMapper.cpp @@ -26,6 +26,7 @@ UserInputMapper::DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Inpu return DeviceProxy::Pointer(); } } + bool UserInputMapper::addInputChannel(Action action, const Input& input, float scale) { return addInputChannel(action, input, Input(), scale); } @@ -129,7 +130,6 @@ void UserInputMapper::update(float deltaTime) { } } - void UserInputMapper::assignDefaulActionScales() { _actionScales[LONGITUDINAL_BACKWARD] = 1.0f; // 1m per unit _actionScales[LONGITUDINAL_FORWARD] = 1.0f; // 1m per unit