Merge pull request #6107 from jherico/controllers

Move controller test to app
This commit is contained in:
Brad Hefta-Gaub 2015-10-18 14:42:32 -07:00
commit a75316f27b
33 changed files with 391 additions and 297 deletions

View file

@ -22,7 +22,7 @@ Item {
property string label: ""
function update() {
value = Controllers.getValue(controlId);
value = Controller.getValue(controlId);
canvas.requestPaint();
}

View file

@ -0,0 +1,171 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import "controller"
import "controls" as HifiControls
import "styles"
HifiControls.VrDialog {
id: root
HifiConstants { id: hifi }
title: "Controller Test"
resizable: true
contentImplicitWidth: clientArea.implicitWidth
contentImplicitHeight: clientArea.implicitHeight
backgroundColor: "beige"
property var actions: Controller.Actions
property var standard: Controller.Standard
property var hydra: null
property var testMapping: null
property var xbox: null
Component.onCompleted: {
enabled = true
var xboxRegex = /^X360Controller/;
var hydraRegex = /^Hydra/;
for (var prop in Controller.Hardware) {
if(xboxRegex.test(prop)) {
root.xbox = Controller.Hardware[prop]
print("found xbox")
continue
}
if (hydraRegex.test(prop)) {
root.hydra = Controller.Hardware[prop]
print("found hydra")
continue
}
}
}
Column {
id: clientArea
spacing: 12
x: root.clientX
y: root.clientY
Row {
spacing: 8
Button {
text: "Default Mapping"
onClicked: {
var mapping = Controller.newMapping("Default");
mapping.from(xbox.A).to(standard.A);
mapping.from(xbox.B).to(standard.B);
mapping.from(xbox.X).to(standard.X);
mapping.from(xbox.Y).to(standard.Y);
mapping.from(xbox.Up).to(standard.DU);
mapping.from(xbox.Down).to(standard.DD);
mapping.from(xbox.Left).to(standard.DL);
mapping.from(xbox.Right).to(standard.Right);
mapping.from(xbox.LB).to(standard.LB);
mapping.from(xbox.RB).to(standard.RB);
mapping.from(xbox.LS).to(standard.LS);
mapping.from(xbox.RS).to(standard.RS);
mapping.from(xbox.Start).to(standard.Start);
mapping.from(xbox.Back).to(standard.Back);
mapping.from(xbox.LY).to(standard.LY);
mapping.from(xbox.LX).to(standard.LX);
mapping.from(xbox.RY).to(standard.RY);
mapping.from(xbox.RX).to(standard.RX);
mapping.from(xbox.LT).to(standard.LT);
mapping.from(xbox.RT).to(standard.RT);
Controller.enableMapping("Default");
enabled = false;
text = "Built"
}
}
Button {
text: "Build Mapping"
onClicked: {
var mapping = Controller.newMapping();
// Inverting a value
mapping.from(hydra.RY).invert().to(standard.RY);
mapping.from(hydra.RX).to(standard.RX);
mapping.from(hydra.LY).to(standard.LY);
mapping.from(hydra.LY).to(standard.LX);
// Assigning a value from a function
// mapping.from(function() { return Math.sin(Date.now() / 250); }).to(standard.RX);
// Constrainting a value to -1, 0, or 1, with a deadzone
// mapping.from(xbox.LY).deadZone(0.5).constrainToInteger().to(standard.LY);
mapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw);
// mapping.from(actions.Yaw).clamp(0, 1).invert().to(actions.YAW_RIGHT);
// mapping.from(actions.Yaw).clamp(-1, 0).to(actions.YAW_LEFT);
// mapping.modifier(keyboard.Ctrl).scale(2.0)
// mapping.from(keyboard.A).to(actions.TranslateLeft)
// mapping.from(keyboard.A, keyboard.Shift).to(actions.TurnLeft)
// mapping.from(keyboard.A, keyboard.Shift, keyboard.Ctrl).scale(2.0).to(actions.TurnLeft)
// // First loopbacks
// // Then non-loopbacks by constraint level (number of inputs)
// mapping.from(xbox.RX).deadZone(0.2).to(xbox.RX)
// mapping.from(standard.RB, standard.LB, keyboard.Shift).to(actions.TurnLeft)
// mapping.from(keyboard.A, keyboard.Shift).to(actions.TurnLeft)
// mapping.from(keyboard.W).when(keyboard.Shift).to(actions.Forward)
testMapping = mapping;
enabled = false
text = "Built"
}
}
Button {
text: "Enable Mapping"
onClicked: root.testMapping.enable()
}
Button {
text: "Disable Mapping"
onClicked: root.testMapping.disable()
}
Button {
text: "Enable Mapping"
onClicked: print(Controller.getValue(root.xbox.LY));
}
}
Row {
Xbox { device: root.standard; label: "Standard"; width: 360 }
}
Row {
spacing: 8
Xbox { device: root.xbox; label: "XBox"; width: 360 }
Hydra { device: root.hydra; width: 360 }
}
// Row {
// spacing: 8
// ScrollingGraph {
// controlId: Controller.Actions.Yaw
// label: "Yaw"
// min: -3.0
// max: 3.0
// size: 128
// }
//
// ScrollingGraph {
// controlId: Controller.Actions.YAW_LEFT
// label: "Yaw Left"
// min: -3.0
// max: 3.0
// size: 128
// }
//
// ScrollingGraph {
// controlId: Controller.Actions.YAW_RIGHT
// label: "Yaw Right"
// min: -3.0
// max: 3.0
// size: 128
// }
// }
}
} // dialog

View file

@ -13,7 +13,7 @@ Item {
property color color: 'black'
function update() {
value = Controllers.getValue(controlId);
value = Controller.getValue(controlId);
canvas.requestPaint();
}

View file

@ -17,8 +17,8 @@ Item {
function update() {
value = Qt.vector2d(
Controllers.getValue(controlIds[0]),
Controllers.getValue(controlIds[1])
Controller.getValue(controlIds[0]),
Controller.getValue(controlIds[1])
);
if (root.invertY) {
value.y = value.y * -1.0

View file

@ -3,8 +3,7 @@ import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import "./hydra"
import "./controls"
import "hydra"
Item {
id: root

View file

@ -15,7 +15,7 @@ Item {
Timer {
interval: 50; running: true; repeat: true
onTriggered: {
root.value = Controllers.getValue(root.controlId);
root.value = Controller.getValue(root.controlId);
canvas.requestPaint();
}
}

View file

@ -3,8 +3,7 @@ import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import "./xbox"
import "./controls"
import "xbox"
Item {
id: root

View file

@ -3,7 +3,7 @@ import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import "./../controls"
import ".."
Item {
id: root

View file

@ -3,7 +3,7 @@ import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import "./../controls"
import ".."
Item {
id: root

View file

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View file

@ -3,8 +3,7 @@ import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import "./../controls"
import ".."
Item {
id: root

View file

@ -3,7 +3,7 @@ import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import "./../controls"
import ".."
Item {
id: root

View file

@ -3,7 +3,7 @@ import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import "./../controls"
import ".."
Item {
id: root

View file

@ -3,7 +3,7 @@ import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import "./../controls"
import ".."
Item {
id: root

View file

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View file

@ -17,27 +17,32 @@
#include <glm/gtx/vector_angle.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <QAbstractNativeEventFilter>
#include <QActionGroup>
#include <QDebug>
#include <QDesktopServices>
#include <QDesktopWidget>
#include <QImage>
#include <QFileDialog>
#include <QInputDialog>
#include <QKeyEvent>
#include <QMediaPlayer>
#include <QMenuBar>
#include <QMessageBox>
#include <QMimeData>
#include <QMouseEvent>
#include <QNetworkDiskCache>
#include <QObject>
#include <QScreen>
#include <QTimer>
#include <QUrl>
#include <QWheelEvent>
#include <QWindow>
#include <QtCore/QDebug>
#include <QtCore/QObject>
#include <QtCore/QUrl>
#include <QtCore/QTimer>
#include <QtCore/QAbstractNativeEventFilter>
#include <QtCore/QMimeData>
#include <QtGui/QScreen>
#include <QtGui/QImage>
#include <QtGui/QWheelEvent>
#include <QtGui/QWindow>
#include <QtQml/QQmlContext>
#include <QtGui/QKeyEvent>
#include <QtGui/QMouseEvent>
#include <QtGui/QDesktopServices>
#include <QtWidgets/QActionGroup>
#include <QtWidgets/QDesktopWidget>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QMessageBox>
#include <QtMultimedia/QMediaPlayer>
#include <QtNetwork/QNetworkDiskCache>
#include <AccountManager.h>
#include <AddressManager.h>
@ -120,6 +125,7 @@
#include "scripting/SettingsScriptingInterface.h"
#include "scripting/WebWindowClass.h"
#include "scripting/WindowScriptingInterface.h"
#include "scripting/ControllerScriptingInterface.h"
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
#include "SpeechRecognizer.h"
#endif
@ -316,6 +322,7 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::set<EntityScriptingInterface>();
DependencyManager::set<WindowScriptingInterface>();
DependencyManager::set<HMDScriptingInterface>();
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
DependencyManager::set<SpeechRecognizer>();
#endif
@ -327,7 +334,7 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::set<InterfaceActionFactory>();
DependencyManager::set<AssetClient>();
DependencyManager::set<UserInputMapper>();
DependencyManager::set<controller::ScriptingInterface, ControllerScriptingInterface>();
return true;
}
@ -374,6 +381,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
setInstance(this);
auto controllerScriptingInterface = DependencyManager::get<controller::ScriptingInterface>().data();
_controllerScriptingInterface = dynamic_cast<ControllerScriptingInterface*>(controllerScriptingInterface);
// to work around the Qt constant wireless scanning, set the env for polling interval very high
const QByteArray EXTREME_BEARER_POLL_TIMEOUT = QString::number(INT_MAX).toLocal8Bit();
qputenv("QT_BEARER_POLL_TIMEOUT", EXTREME_BEARER_POLL_TIMEOUT);
@ -618,7 +627,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
// Setup the userInputMapper with the actions
auto userInputMapper = DependencyManager::get<UserInputMapper>();
connect(userInputMapper.data(), &UserInputMapper::actionEvent, &_controllerScriptingInterface, &ControllerScriptingInterface::actionEvent);
connect(userInputMapper.data(), &UserInputMapper::actionEvent, _controllerScriptingInterface, &ControllerScriptingInterface::actionEvent);
connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) {
if (state) {
switch (action) {
@ -975,6 +984,8 @@ void Application::initializeUi() {
offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
offscreenUi->load("Root.qml");
offscreenUi->load("RootMenu.qml");
auto scriptingInterface = DependencyManager::get<controller::ScriptingInterface>();
offscreenUi->getRootContext()->setContextProperty("Controller", scriptingInterface.data());
_glWidget->installEventFilter(offscreenUi.data());
VrMenu::load();
VrMenu::executeQueuedLambdas();
@ -1456,7 +1467,7 @@ bool Application::event(QEvent* event) {
}
if (HFActionEvent::types().contains(event->type())) {
_controllerScriptingInterface.handleMetaEvent(static_cast<HFMetaEvent*>(event));
_controllerScriptingInterface->handleMetaEvent(static_cast<HFMetaEvent*>(event));
}
return QApplication::event(event);
@ -1470,7 +1481,7 @@ bool Application::eventFilter(QObject* object, QEvent* event) {
}
// Filter out captured keys before they're used for shortcut actions.
if (_controllerScriptingInterface.isKeyCaptured(static_cast<QKeyEvent*>(event))) {
if (_controllerScriptingInterface->isKeyCaptured(static_cast<QKeyEvent*>(event))) {
event->accept();
return true;
}
@ -1485,10 +1496,10 @@ void Application::keyPressEvent(QKeyEvent* event) {
_altPressed = event->key() == Qt::Key_Alt;
_keysPressed.insert(event->key());
_controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts
_controllerScriptingInterface->emitKeyPressEvent(event); // send events to any registered scripts
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isKeyCaptured(event)) {
if (_controllerScriptingInterface->isKeyCaptured(event)) {
return;
}
@ -1518,6 +1529,13 @@ void Application::keyPressEvent(QKeyEvent* event) {
if (isMeta) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->load("Browser.qml");
}
break;
case Qt::Key_X:
if (isMeta && isShifted) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->load("TestControllers.qml");
}
break;
@ -1752,10 +1770,10 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
_keysPressed.remove(event->key());
_controllerScriptingInterface.emitKeyReleaseEvent(event); // send events to any registered scripts
_controllerScriptingInterface->emitKeyReleaseEvent(event); // send events to any registered scripts
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isKeyCaptured(event)) {
if (_controllerScriptingInterface->isKeyCaptured(event)) {
return;
}
@ -1844,10 +1862,10 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
_entities.mouseMoveEvent(&mappedEvent, deviceID);
_controllerScriptingInterface.emitMouseMoveEvent(&mappedEvent, deviceID); // send events to any registered scripts
_controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent, deviceID); // send events to any registered scripts
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isMouseCaptured()) {
if (_controllerScriptingInterface->isMouseCaptured()) {
return;
}
@ -1872,10 +1890,10 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
_entities.mousePressEvent(&mappedEvent, deviceID);
}
_controllerScriptingInterface.emitMousePressEvent(&mappedEvent); // send events to any registered scripts
_controllerScriptingInterface->emitMousePressEvent(&mappedEvent); // send events to any registered scripts
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isMouseCaptured()) {
if (_controllerScriptingInterface->isMouseCaptured()) {
return;
}
@ -1897,11 +1915,11 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
void Application::mouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID) {
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isMouseCaptured()) {
if (_controllerScriptingInterface->isMouseCaptured()) {
return;
}
_controllerScriptingInterface.emitMouseDoublePressEvent(event);
_controllerScriptingInterface->emitMouseDoublePressEvent(event);
}
void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
@ -1917,10 +1935,10 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
_entities.mouseReleaseEvent(&mappedEvent, deviceID);
}
_controllerScriptingInterface.emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts
_controllerScriptingInterface->emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isMouseCaptured()) {
if (_controllerScriptingInterface->isMouseCaptured()) {
return;
}
@ -1943,12 +1961,12 @@ void Application::touchUpdateEvent(QTouchEvent* event) {
if (event->type() == QEvent::TouchUpdate) {
TouchEvent thisEvent(*event, _lastTouchEvent);
_controllerScriptingInterface.emitTouchUpdateEvent(thisEvent); // send events to any registered scripts
_controllerScriptingInterface->emitTouchUpdateEvent(thisEvent); // send events to any registered scripts
_lastTouchEvent = thisEvent;
}
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isTouchCaptured()) {
if (_controllerScriptingInterface->isTouchCaptured()) {
return;
}
@ -1960,13 +1978,13 @@ void Application::touchUpdateEvent(QTouchEvent* event) {
void Application::touchBeginEvent(QTouchEvent* event) {
_altPressed = false;
TouchEvent thisEvent(*event); // on touch begin, we don't compare to last event
_controllerScriptingInterface.emitTouchBeginEvent(thisEvent); // send events to any registered scripts
_controllerScriptingInterface->emitTouchBeginEvent(thisEvent); // send events to any registered scripts
_lastTouchEvent = thisEvent; // and we reset our last event to this event before we call our update
touchUpdateEvent(event);
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isTouchCaptured()) {
if (_controllerScriptingInterface->isTouchCaptured()) {
return;
}
@ -1979,11 +1997,11 @@ void Application::touchBeginEvent(QTouchEvent* event) {
void Application::touchEndEvent(QTouchEvent* event) {
_altPressed = false;
TouchEvent thisEvent(*event, _lastTouchEvent);
_controllerScriptingInterface.emitTouchEndEvent(thisEvent); // send events to any registered scripts
_controllerScriptingInterface->emitTouchEndEvent(thisEvent); // send events to any registered scripts
_lastTouchEvent = thisEvent;
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isTouchCaptured()) {
if (_controllerScriptingInterface->isTouchCaptured()) {
return;
}
@ -1996,10 +2014,10 @@ void Application::touchEndEvent(QTouchEvent* event) {
void Application::wheelEvent(QWheelEvent* event) {
_altPressed = false;
_controllerScriptingInterface.emitWheelEvent(event); // send events to any registered scripts
_controllerScriptingInterface->emitWheelEvent(event); // send events to any registered scripts
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isWheelCaptured()) {
if (_controllerScriptingInterface->isWheelCaptured()) {
return;
}
@ -2709,12 +2727,12 @@ void Application::update(float deltaTime) {
}
// Dispatch input events
_controllerScriptingInterface.update();
_controllerScriptingInterface->update();
// Transfer the user inputs to the driveKeys
myAvatar->clearDriveKeys();
if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) {
if (!_controllerScriptingInterface.areActionsCaptured()) {
if (!_controllerScriptingInterface->areActionsCaptured()) {
myAvatar->setDriveKeys(FWD, userInputMapper->getActionState(UserInputMapper::LONGITUDINAL_FORWARD));
myAvatar->setDriveKeys(BACK, userInputMapper->getActionState(UserInputMapper::LONGITUDINAL_BACKWARD));
myAvatar->setDriveKeys(UP, userInputMapper->getActionState(UserInputMapper::VERTICAL_UP));

View file

@ -14,13 +14,15 @@
#include <functional>
#include <QApplication>
#include <QHash>
#include <QImage>
#include <QPointer>
#include <QSet>
#include <QStringList>
#include <QUndoStack>
#include <QtCore/QHash>
#include <QtCore/QPointer>
#include <QtCore/QSet>
#include <QtCore/QStringList>
#include <QtGui/QImage>
#include <QtWidgets/QApplication>
#include <QtWidgets/QUndoStack>
#include <AbstractScriptingServicesInterface.h>
#include <AbstractViewStateInterface.h>
@ -161,7 +163,7 @@ public:
ToolWindow* getToolWindow() { return _toolWindow ; }
virtual controller::ScriptingInterface* getControllerScriptingInterface() { return &_controllerScriptingInterface; }
virtual controller::ScriptingInterface* getControllerScriptingInterface() { return _controllerScriptingInterface; }
virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine);
QImage renderAvatarBillboard(RenderArgs* renderArgs);
@ -474,8 +476,7 @@ private:
NodeToJurisdictionMap _entityServerJurisdictions;
NodeToOctreeSceneStats _octreeServerSceneStats;
ControllerScriptingInterface _controllerScriptingInterface;
ControllerScriptingInterface* _controllerScriptingInterface{ nullptr };
QPointer<LogDialog> _logDialog;
QPointer<SnapshotShareDialog> _snapshotShareDialog;

View file

@ -26,7 +26,6 @@
#include "Logging.h"
#include "InputDevice.h"
namespace controller {
class VirtualEndpoint : public Endpoint {
@ -144,39 +143,9 @@ namespace controller {
}
return deviceMap;
}
ScriptingInterface::ScriptingInterface() {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
qCDebug(controllers) << "Setting up standard controller abstraction";
auto standardDevice = userInputMapper->getStandardDevice();
// Expose the IDs to JS
_standard = createDeviceMap(standardDevice.get());
// Create the endpoints
for (const auto& inputMapping : standardDevice->getAvailabeInputs()) {
const auto& standardInput = inputMapping.first;
// Ignore aliases
if (_endpoints.count(standardInput)) {
continue;
}
_endpoints[standardInput] = std::make_shared<VirtualEndpoint>(standardInput);
}
// FIXME allow custom user actions?
auto actionNames = userInputMapper->getActionNames();
int actionNumber = 0;
qCDebug(controllers) << "Setting up standard actions";
for (const auto& actionName : actionNames) {
UserInputMapper::Input actionInput(UserInputMapper::Input::ACTIONS_DEVICE, actionNumber++, UserInputMapper::ChannelType::AXIS);
qCDebug(controllers) << "\tAction: " << actionName << " " << QString::number(actionInput.getID(), 16);
// Expose the IDs to JS
QString cleanActionName = QString(actionName).remove(ScriptingInterface::SANITIZE_NAME_EXPRESSION);
_actions.insert(cleanActionName, actionInput.getID());
// Create the action endpoints
_endpoints[actionInput] = std::make_shared<ActionEndpoint>(actionInput);
}
updateMaps();
ScriptingInterface::~ScriptingInterface() {
}
QObject* ScriptingInterface::newMapping(const QString& mappingName) {
@ -235,7 +204,7 @@ namespace controller {
if (request->getResult() == ResourceRequest::Success) {
result = parseMapping(QString(request->getData()));
} else {
qDebug() << "Failed to load mapping url <" << jsonUrl << ">" << endl;
qCWarning(controllers) << "Failed to load mapping url <" << jsonUrl << ">" << endl;
}
request->deleteLater();
}
@ -245,6 +214,7 @@ namespace controller {
Q_INVOKABLE QObject* newMapping(const QJsonObject& json);
void ScriptingInterface::enableMapping(const QString& mappingName, bool enable) {
qCDebug(controllers) << "Attempting to enable mapping " << mappingName;
auto iterator = _mappingsByName.find(mappingName);
if (_mappingsByName.end() == iterator) {
qCWarning(controllers) << "Request to enable / disable unknown mapping " << mappingName;
@ -524,9 +494,45 @@ namespace controller {
}
}
}
} // namespace controllers
using namespace controller;
// FIXME this throws a hissy fit on MSVC if I put it in the main controller namespace block
ScriptingInterface::ScriptingInterface() {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
qCDebug(controllers) << "Setting up standard controller abstraction";
auto standardDevice = userInputMapper->getStandardDevice();
// Expose the IDs to JS
_standard = createDeviceMap(standardDevice.get());
// Create the endpoints
for (const auto& inputMapping : standardDevice->getAvailabeInputs()) {
const auto& standardInput = inputMapping.first;
// Ignore aliases
if (_endpoints.count(standardInput)) {
continue;
}
_endpoints[standardInput] = std::make_shared<VirtualEndpoint>(standardInput);
}
// FIXME allow custom user actions?
auto actionNames = userInputMapper->getActionNames();
int actionNumber = 0;
qCDebug(controllers) << "Setting up standard actions";
for (const auto& actionName : actionNames) {
UserInputMapper::Input actionInput(UserInputMapper::Input::ACTIONS_DEVICE, actionNumber++, UserInputMapper::ChannelType::AXIS);
qCDebug(controllers) << "\tAction: " << actionName << " " << QString::number(actionInput.getID(), 16);
// Expose the IDs to JS
QString cleanActionName = QString(actionName).remove(ScriptingInterface::SANITIZE_NAME_EXPRESSION);
_actions.insert(cleanActionName, actionInput.getID());
// Create the action endpoints
_endpoints[actionInput] = std::make_shared<ActionEndpoint>(actionInput);
}
updateMaps();
}
//var mapping = Controller.newMapping();
//mapping.map(hydra.LeftButton0, actions.ContextMenu);
//mapping.map(hydra.LeftButton0).to(xbox.RT);

View file

@ -28,6 +28,8 @@
#include <QtQml/QJSValue>
#include <QtScript/QScriptValue>
#include <DependencyManager.h>
#include "UserInputMapper.h"
#include "StandardControls.h"
#include "Mapping.h"
@ -55,7 +57,7 @@ namespace controller {
};
/// handles scripting of input controller commands from JS
class ScriptingInterface : public QObject {
class ScriptingInterface : public QObject, public Dependency {
Q_OBJECT
Q_PROPERTY(QVariantMap Hardware READ getHardware CONSTANT FINAL)
Q_PROPERTY(QVariantMap Actions READ getActions CONSTANT FINAL)
@ -63,6 +65,7 @@ namespace controller {
public:
ScriptingInterface();
virtual ~ScriptingInterface();
Q_INVOKABLE float getValue(const int& source) const;
Q_INVOKABLE float getButtonValue(StandardButtonChannel source, uint16_t device = 0) const;

View file

@ -14,13 +14,18 @@
#include <QtCore/QJsonObject>
#include <QtCore/QJsonArray>
#include "RouteBuilderProxy.h"
#include "../ScriptingInterface.h"
#include "../Logging.h"
using namespace controller;
QObject* MappingBuilderProxy::from(int input) {
qCDebug(controllers) << "Creating new Route builder proxy from " << input;
auto sourceEndpoint = _parent.endpointFor(UserInputMapper::Input(input));
return from(sourceEndpoint);
}
QObject* MappingBuilderProxy::from(const QJSValue& source) {
qCDebug(controllers) << "Creating new Route builder proxy from " << source.toString();
auto sourceEndpoint = _parent.endpointFor(source);

View file

@ -31,6 +31,7 @@ public:
MappingBuilderProxy(ScriptingInterface& parent, Mapping::Pointer mapping)
: _parent(parent), _mapping(mapping) { }
Q_INVOKABLE QObject* from(int sourceInput);
Q_INVOKABLE QObject* from(const QJSValue& source);
Q_INVOKABLE QObject* from(const QScriptValue& source);
Q_INVOKABLE QObject* makeAxis(const QJSValue& source1, const QJSValue& source2);

View file

@ -20,6 +20,12 @@
namespace controller {
void RouteBuilderProxy::to(int destinationInput) {
qCDebug(controllers) << "Completing route " << destinationInput;
auto destinationEndpoint = _parent.endpointFor(UserInputMapper::Input(destinationInput));
return to(destinationEndpoint);
}
void RouteBuilderProxy::to(const QJSValue& destination) {
qCDebug(controllers) << "Completing route " << destination.toString();
auto destinationEndpoint = _parent.endpointFor(destination);

View file

@ -30,6 +30,7 @@ class RouteBuilderProxy : public QObject {
RouteBuilderProxy(ScriptingInterface& parent, Mapping::Pointer mapping, Route::Pointer route)
: _parent(parent), _mapping(mapping), _route(route) { }
Q_INVOKABLE void to(int destination);
Q_INVOKABLE void to(const QJSValue& destination);
Q_INVOKABLE void to(const QScriptValue& destination);

View file

@ -106,8 +106,7 @@ void EntityTreeRenderer::init() {
entityTree->setFBXService(this);
if (_wantScripts) {
_entitiesScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities",
_scriptingServices->getControllerScriptingInterface());
_entitiesScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities");
_scriptingServices->registerScriptEngineWithApplicationServices(_entitiesScriptEngine);
_entitiesScriptEngine->runInThread();
DependencyManager::get<EntityScriptingInterface>()->setEntitiesScriptEngine(_entitiesScriptEngine);

View file

@ -19,6 +19,7 @@
#include <QMutex>
#include <PerfStat.h>
#include <DependencyManager.h>
#include <NumericalConstants.h>
#include "GLEscrow.h"
@ -614,3 +615,8 @@ QQuickWindow* OffscreenQmlSurface::getWindow() {
QSize OffscreenQmlSurface::size() const {
return _renderer->_quickWindow->geometry().size();
}
QQmlContext* OffscreenQmlSurface::getRootContext() {
return _qmlEngine->rootContext();
}

View file

@ -60,6 +60,7 @@ public:
QQuickItem* getRootItem();
QQuickWindow* getWindow();
QObject* getEventHandler();
QQmlContext* getRootContext();
QPointF mapToVirtualScreen(const QPointF& originalPoint, QObject* originalWidget);
virtual bool eventFilter(QObject* originalDestination, QEvent* event);

View file

@ -12,24 +12,13 @@
#ifndef hifi_AbstractScriptingServicesInterface_h
#define hifi_AbstractScriptingServicesInterface_h
namespace controller {
class ScriptingInterface;
}
class Transform;
class ScriptEngine;
class QThread;
/// Interface provided by Application to other objects that need access to scripting services of the application
class AbstractScriptingServicesInterface {
public:
/// Returns the controller interface for the application
virtual controller::ScriptingInterface* getControllerScriptingInterface() = 0;
/// Registers application specific services with a script engine.
virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) = 0;
};

View file

@ -84,8 +84,7 @@ void inputControllerFromScriptValue(const QScriptValue &object, controller::Inpu
out = qobject_cast<controller::InputController*>(object.toQObject());
}
ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString,
controller::ScriptingInterface* controllerScriptingInterface, bool wantSignals) :
ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, bool wantSignals) :
_scriptContents(scriptContents),
_isFinished(false),
@ -93,7 +92,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam
_isInitialized(false),
_timerFunctionMap(),
_wantSignals(wantSignals),
_controllerScriptingInterface(controllerScriptingInterface),
_fileNameString(fileNameString),
_quatLibrary(),
_vec3Library(),
@ -310,7 +308,8 @@ void ScriptEngine::init() {
registerGlobalObject("Script", this);
registerGlobalObject("Audio", &AudioScriptingInterface::getInstance());
registerGlobalObject("Controller", _controllerScriptingInterface);
auto scriptingInterface = DependencyManager::get<controller::ScriptingInterface>();
registerGlobalObject("Controller", scriptingInterface.data());
registerGlobalObject("Entities", entityScriptingInterface.data());
registerGlobalObject("Quat", &_quatLibrary);
registerGlobalObject("Vec3", &_vec3Library);
@ -320,8 +319,8 @@ void ScriptEngine::init() {
// constants
globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE)));
if (_controllerScriptingInterface) {
_controllerScriptingInterface->registerControllerTypes(this);
if (scriptingInterface) {
scriptingInterface->registerControllerTypes(this);
}

View file

@ -55,7 +55,6 @@ class ScriptEngine : public QScriptEngine, public ScriptUser, public EntitiesScr
public:
ScriptEngine(const QString& scriptContents = NO_SCRIPT,
const QString& fileNameString = QString(""),
controller::ScriptingInterface* controllerScriptingInterface = nullptr,
bool wantSignals = true);
~ScriptEngine();
@ -184,7 +183,6 @@ private:
QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot);
void stopTimer(QTimer* timer);
controller::ScriptingInterface* _controllerScriptingInterface;
QString _fileNameString;
Quat _quatLibrary;
Vec3 _vec3Library;

View file

@ -51,7 +51,10 @@ public:
template<typename T, typename ...Args>
static QSharedPointer<T> set(Args&&... args);
template<typename T, typename I, typename ...Args>
static QSharedPointer<T> set(Args&&... args);
template<typename T>
static void destroy();
@ -89,13 +92,26 @@ QSharedPointer<T> DependencyManager::get() {
template <typename T, typename ...Args>
QSharedPointer<T> DependencyManager::set(Args&&... args) {
static size_t hashCode = _manager.getHashCode<T>();
QSharedPointer<Dependency>& instance = _manager.safeGet(hashCode);
instance.clear(); // Clear instance before creation of new one to avoid edge cases
QSharedPointer<T> newInstance(new T(args...), &T::customDeleter);
QSharedPointer<Dependency> storedInstance = qSharedPointerCast<Dependency>(newInstance);
instance.swap(storedInstance);
return newInstance;
}
template <typename T, typename I, typename ...Args>
QSharedPointer<T> DependencyManager::set(Args&&... args) {
static size_t hashCode = _manager.getHashCode<T>();
QSharedPointer<Dependency>& instance = _manager.safeGet(hashCode);
instance.clear(); // Clear instance before creation of new one to avoid edge cases
QSharedPointer<T> newInstance(new I(args...), &I::customDeleter);
QSharedPointer<Dependency> storedInstance = qSharedPointerCast<Dependency>(newInstance);
instance.swap(storedInstance);
return newInstance;
}

View file

@ -1,157 +0,0 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import "./xbox"
import "./controls"
Column {
id: root
property var actions: Controllers.Actions
property var standard: Controllers.Standard
property var hydra: null
property var testMapping: null
property var xbox: null
Component.onCompleted: {
var xboxRegex = /^X360Controller/;
var hydraRegex = /^Hydra/;
for (var prop in Controllers.Hardware) {
if(xboxRegex.test(prop)) {
root.xbox = Controllers.Hardware[prop]
print("found xbox")
continue
}
if (hydraRegex.test(prop)) {
root.hydra = Controllers.Hardware[prop]
print("found hydra")
continue
}
}
}
spacing: 12
Timer {
interval: 50; running: true; repeat: true
onTriggered: {
Controllers.update();
}
}
Row {
spacing: 8
Button {
text: "Default Mapping"
onClicked: {
var mapping = Controllers.newMapping("Default");
mapping.from(xbox.A).to(standard.A);
mapping.from(xbox.B).to(standard.B);
mapping.from(xbox.X).to(standard.X);
mapping.from(xbox.Y).to(standard.Y);
mapping.from(xbox.Up).to(standard.DU);
mapping.from(xbox.Down).to(standard.DD);
mapping.from(xbox.Left).to(standard.DL);
mapping.from(xbox.Right).to(standard.Right);
mapping.from(xbox.LB).to(standard.LB);
mapping.from(xbox.RB).to(standard.RB);
mapping.from(xbox.LS).to(standard.LS);
mapping.from(xbox.RS).to(standard.RS);
mapping.from(xbox.Start).to(standard.Start);
mapping.from(xbox.Back).to(standard.Back);
mapping.from(xbox.LY).to(standard.LY);
mapping.from(xbox.LX).to(standard.LX);
mapping.from(xbox.RY).to(standard.RY);
mapping.from(xbox.RX).to(standard.RX);
mapping.from(xbox.LT).to(standard.LT);
mapping.from(xbox.RT).to(standard.RT);
Controllers.enableMapping("Default");
enabled = false;
text = "Built"
}
}
Button {
text: "Build Mapping"
onClicked: {
var mapping = Controllers.newMapping();
// Inverting a value
mapping.from(xbox.RY).invert().to(standard.RY);
// Assigning a value from a function
mapping.from(function() { return Math.sin(Date.now() / 250); }).to(standard.RX);
// Constrainting a value to -1, 0, or 1, with a deadzone
mapping.from(xbox.LY).deadZone(0.5).constrainToInteger().to(standard.LY);
mapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw);
mapping.from(actions.Yaw).clamp(0, 1).invert().to(actions.YAW_RIGHT);
mapping.from(actions.Yaw).clamp(-1, 0).to(actions.YAW_LEFT);
// mapping.modifier(keyboard.Ctrl).scale(2.0)
// mapping.from(keyboard.A).to(actions.TranslateLeft)
// mapping.from(keyboard.A, keyboard.Shift).to(actions.TurnLeft)
// mapping.from(keyboard.A, keyboard.Shift, keyboard.Ctrl).scale(2.0).to(actions.TurnLeft)
// // First loopbacks
// // Then non-loopbacks by constraint level (number of inputs)
// mapping.from(xbox.RX).deadZone(0.2).to(xbox.RX)
// mapping.from(standard.RB, standard.LB, keyboard.Shift).to(actions.TurnLeft)
// mapping.from(keyboard.A, keyboard.Shift).to(actions.TurnLeft)
// mapping.from(keyboard.W).when(keyboard.Shift).to(actions.Forward)
testMapping = mapping;
enabled = false
text = "Built"
}
}
Button {
text: "Enable Mapping"
onClicked: root.testMapping.enable()
}
Button {
text: "Disable Mapping"
onClicked: root.testMapping.disable()
}
}
Row {
Xbox { device: root.standard; label: "Standard"; width: 360 }
}
Row {
spacing: 8
Xbox { device: root.xbox; label: "XBox"; width: 360 }
}
Row {
spacing: 8
Hydra { device: root.hydra; width: 360 }
}
Row {
spacing: 8
ScrollingGraph {
controlId: Controllers.Actions.Yaw
label: "Yaw"
min: -3.0
max: 3.0
size: 128
}
ScrollingGraph {
controlId: Controllers.Actions.YAW_LEFT
label: "Yaw Left"
min: -3.0
max: 3.0
size: 128
}
ScrollingGraph {
controlId: Controllers.Actions.YAW_RIGHT
label: "Yaw Right"
min: -3.0
max: 3.0
size: 128
}
}
}

View file

@ -7,8 +7,16 @@ ApplicationWindow {
id: window
visible: true
Timer {
interval: 50; running: true; repeat: true
onTriggered: {
Controller.update();
}
}
Loader {
id: pageLoader
source: "content.qml"
source: ResourcePath + "TestControllers.qml"
}
}

View file

@ -43,17 +43,37 @@
#include <DependencyManager.h>
#include <controllers/UserInputMapper.h>
const QString& getQmlDir() {
const QString& getResourcesDir() {
static QString dir;
if (dir.isEmpty()) {
QDir path(__FILE__);
path.cdUp();
dir = path.cleanPath(path.absoluteFilePath("../qml/")) + "/";
dir = path.cleanPath(path.absoluteFilePath("../../../interface/resources/")) + "/";
qDebug() << "Resources Path: " << dir;
}
return dir;
}
const QString& getQmlDir() {
static QString dir;
if (dir.isEmpty()) {
dir = getResourcesDir() + "qml/";
qDebug() << "Qml Path: " << dir;
}
return dir;
}
const QString& getTestQmlDir() {
static QString dir;
if (dir.isEmpty()) {
QDir path(__FILE__);
path.cdUp();
dir = path.cleanPath(path.absoluteFilePath("../")) + "/qml/";
qDebug() << "Qml Test Path: " << dir;
}
return dir;
}
using namespace controller;
@ -88,6 +108,7 @@ public:
int main(int argc, char** argv) {
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
auto rootContext = engine.rootContext();
new PluginContainerProxy();
// Simulate our application idle loop
@ -119,11 +140,16 @@ int main(int argc, char** argv) {
}
inputPlugin->pluginUpdate(0, false);
}
auto rootContext = engine.rootContext();
rootContext->setContextProperty("Controllers", new MyControllerScriptingInterface());
}
engine.load(getQmlDir() + "main.qml");
qDebug() << getQmlDir();
rootContext->setContextProperty("ResourcePath", getQmlDir());
engine.setBaseUrl(QUrl::fromLocalFile(getQmlDir()));
engine.addImportPath(getQmlDir());
engine.load(getTestQmlDir() + "main.qml");
for (auto pathItem : engine.importPathList()) {
qDebug() << pathItem;
}
app.exec();
return 0;
}