diff --git a/src/components/serverNotifications/gamepadtokey.js b/src/components/input/gamepadtokey.js similarity index 64% rename from src/components/serverNotifications/gamepadtokey.js rename to src/components/input/gamepadtokey.js index 7705aed71f..5356bcbb45 100644 --- a/src/components/serverNotifications/gamepadtokey.js +++ b/src/components/input/gamepadtokey.js @@ -251,114 +251,150 @@ require(['apphost'], function (appHost) { } } + var inputLoopTimer; function runInputLoop() { // Get the latest gamepad state. - var gamepads; - if (navigator.getGamepads) { - gamepads = navigator.getGamepads(); - } else if (navigator.webkitGetGamepads) { - gamepads = navigator.webkitGetGamepads(); - } - gamepads = gamepads || []; - var i; - var j; - var len; - for (i = 0, len = gamepads.length; i < len; i++) { + var gamepads = navigator.getGamepads(); + for (var i = 0, len = gamepads.length; i < len; i++) { var gamepad = gamepads[i]; - if (gamepad) { - // Iterate through the axes - var axes = gamepad.axes; - var leftStickX = axes[0]; - var leftStickY = axes[1]; - if (leftStickX > _THUMB_STICK_THRESHOLD) { // Right - _ButtonPressedState.setleftThumbstickRight(true); - } else if (leftStickX < -_THUMB_STICK_THRESHOLD) { // Left - _ButtonPressedState.setleftThumbstickLeft(true); - } else if (leftStickY < -_THUMB_STICK_THRESHOLD) { // Up - _ButtonPressedState.setleftThumbstickUp(true); - } else if (leftStickY > _THUMB_STICK_THRESHOLD) { // Down - _ButtonPressedState.setleftThumbstickDown(true); - } else { - _ButtonPressedState.setleftThumbstickLeft(false); - _ButtonPressedState.setleftThumbstickRight(false); - _ButtonPressedState.setleftThumbstickUp(false); - _ButtonPressedState.setleftThumbstickDown(false); - } - // Iterate through the buttons to see if Left thumbstick, DPad, A and B are pressed. - var buttons = gamepad.buttons; - for (j = 0, len = buttons.length; j < len; j++) { - if (ProcessedButtons.indexOf(j) !== -1) { - - if (buttons[j].pressed) { - switch (j) { - case _GAMEPAD_DPAD_UP_BUTTON_INDEX: - _ButtonPressedState.setdPadUp(true); - break; - case _GAMEPAD_DPAD_DOWN_BUTTON_INDEX: - _ButtonPressedState.setdPadDown(true); - break; - case _GAMEPAD_DPAD_LEFT_BUTTON_INDEX: - _ButtonPressedState.setdPadLeft(true); - break; - case _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX: - _ButtonPressedState.setdPadRight(true); - break; - case _GAMEPAD_A_BUTTON_INDEX: - _ButtonPressedState.setgamepadA(true); - break; - case _GAMEPAD_B_BUTTON_INDEX: - _ButtonPressedState.setgamepadB(true); - break; - default: - // No-op - break; - } - } else { - switch (j) { - case _GAMEPAD_DPAD_UP_BUTTON_INDEX: - if (_ButtonPressedState.getdPadUp()) { - _ButtonPressedState.setdPadUp(false); - } - break; - case _GAMEPAD_DPAD_DOWN_BUTTON_INDEX: - if (_ButtonPressedState.getdPadDown()) { - _ButtonPressedState.setdPadDown(false); - } - break; - case _GAMEPAD_DPAD_LEFT_BUTTON_INDEX: - if (_ButtonPressedState.getdPadLeft()) { - _ButtonPressedState.setdPadLeft(false); - } - break; - case _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX: - if (_ButtonPressedState.getdPadRight()) { - _ButtonPressedState.setdPadRight(false); - } - break; - case _GAMEPAD_A_BUTTON_INDEX: - if (_ButtonPressedState.getgamepadA()) { - _ButtonPressedState.setgamepadA(false); - } - break; - case _GAMEPAD_B_BUTTON_INDEX: - if (_ButtonPressedState.getgamepadB()) { - _ButtonPressedState.setgamepadB(false); - } - break; - default: - // No-op - break; - } + if (!gamepad) { + continue; + } + // Iterate through the axes + var axes = gamepad.axes; + var leftStickX = axes[0]; + var leftStickY = axes[1]; + if (leftStickX > _THUMB_STICK_THRESHOLD) { // Right + _ButtonPressedState.setleftThumbstickRight(true); + } else if (leftStickX < -_THUMB_STICK_THRESHOLD) { // Left + _ButtonPressedState.setleftThumbstickLeft(true); + } else if (leftStickY < -_THUMB_STICK_THRESHOLD) { // Up + _ButtonPressedState.setleftThumbstickUp(true); + } else if (leftStickY > _THUMB_STICK_THRESHOLD) { // Down + _ButtonPressedState.setleftThumbstickDown(true); + } else { + _ButtonPressedState.setleftThumbstickLeft(false); + _ButtonPressedState.setleftThumbstickRight(false); + _ButtonPressedState.setleftThumbstickUp(false); + _ButtonPressedState.setleftThumbstickDown(false); + } + // Iterate through the buttons to see if Left thumbstick, DPad, A and B are pressed. + var buttons = gamepad.buttons; + for (var j = 0, len = buttons.length; j < len; j++) { + if (ProcessedButtons.indexOf(j) !== -1) { + if (buttons[j].pressed) { + switch (j) { + case _GAMEPAD_DPAD_UP_BUTTON_INDEX: + _ButtonPressedState.setdPadUp(true); + break; + case _GAMEPAD_DPAD_DOWN_BUTTON_INDEX: + _ButtonPressedState.setdPadDown(true); + break; + case _GAMEPAD_DPAD_LEFT_BUTTON_INDEX: + _ButtonPressedState.setdPadLeft(true); + break; + case _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX: + _ButtonPressedState.setdPadRight(true); + break; + case _GAMEPAD_A_BUTTON_INDEX: + _ButtonPressedState.setgamepadA(true); + break; + case _GAMEPAD_B_BUTTON_INDEX: + _ButtonPressedState.setgamepadB(true); + break; + default: + // No-op + break; + } + } else { + switch (j) { + case _GAMEPAD_DPAD_UP_BUTTON_INDEX: + if (_ButtonPressedState.getdPadUp()) { + _ButtonPressedState.setdPadUp(false); + } + break; + case _GAMEPAD_DPAD_DOWN_BUTTON_INDEX: + if (_ButtonPressedState.getdPadDown()) { + _ButtonPressedState.setdPadDown(false); + } + break; + case _GAMEPAD_DPAD_LEFT_BUTTON_INDEX: + if (_ButtonPressedState.getdPadLeft()) { + _ButtonPressedState.setdPadLeft(false); + } + break; + case _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX: + if (_ButtonPressedState.getdPadRight()) { + _ButtonPressedState.setdPadRight(false); + } + break; + case _GAMEPAD_A_BUTTON_INDEX: + if (_ButtonPressedState.getgamepadA()) { + _ButtonPressedState.setgamepadA(false); + } + break; + case _GAMEPAD_B_BUTTON_INDEX: + if (_ButtonPressedState.getgamepadB()) { + _ButtonPressedState.setgamepadB(false); + } + break; + default: + // No-op + break; } } } } } // Schedule the next one - requestAnimationFrame(runInputLoop); + inputLoopTimer = requestAnimationFrame(runInputLoop); } - runInputLoop(); + function startInputLoop() { + if (!inputLoopTimer) { + runInputLoop(); + } + } + + function stopInputLoop() { + cancelAnimationFrame(inputLoopTimer); + inputLoopTimer = undefined; + } + + function isGamepadConnected() { + var gamepads = navigator.getGamepads(); + for (var i = 0, len = gamepads.length; i < len; i++) { + var gamepad = gamepads[i]; + if (gamepad && gamepad.connected) { + return true; + } + } + return false; + } + + function onFocusOrGamepadAttach(e) { + if (isGamepadConnected() && document.hasFocus()) { + console.log("Gamepad connected! Starting input loop"); + startInputLoop(); + } + } + + function onFocusOrGamepadDetach(e) { + if (!isGamepadConnected() || !document.hasFocus()) { + console.log("Gamepad disconnected! No other gamepads are connected, stopping input loop"); + stopInputLoop(); + } else { + console.log("Gamepad disconnected! There are gamepads still connected."); + } + } + + // Event listeners for any change in gamepads' state. + window.addEventListener("gamepaddisconnected", onFocusOrGamepadDetach); + window.addEventListener("gamepadconnected", onFocusOrGamepadAttach); + window.addEventListener("blur", onFocusOrGamepadDetach); + window.addEventListener("focus", onFocusOrGamepadAttach); + + onFocusOrGamepadAttach(); // The gamepadInputEmulation is a string property that exists in JavaScript UWAs and in WebViews in UWAs. // It won't exist in Win8.1 style apps or browsers. diff --git a/src/components/keyboardnavigation.js b/src/components/input/keyboardnavigation.js similarity index 86% rename from src/components/keyboardnavigation.js rename to src/components/input/keyboardnavigation.js index 12246a0d80..0359ee7430 100644 --- a/src/components/keyboardnavigation.js +++ b/src/components/input/keyboardnavigation.js @@ -146,6 +146,17 @@ define(["inputManager", "layoutManager"], function (inputManager, layoutManager) }); } + // Gamepad initialisation. No script is required if no gamepads are present at init time, saving a bit of resources. + // Whenever the gamepad is connected, we hand all the control of the gamepad to gamepadtokey.js by removing the event handler + function attachGamepadScript(e) { + console.log("Gamepad connected! Attaching gamepadtokey.js script"); + window.removeEventListener("gamepadconnected", attachGamepadScript); + require(["components/input/gamepadtokey"]); + } + + // No need to check for gamepads manually at load time, the eventhandler will be fired for that + window.addEventListener("gamepadconnected", attachGamepadScript); + return { enable: enable, getKeyName: getKeyName, diff --git a/src/components/serverNotifications/mouseManager.js b/src/components/input/mouseManager.js similarity index 100% rename from src/components/serverNotifications/mouseManager.js rename to src/components/input/mouseManager.js diff --git a/src/components/serverNotifications/serverNotifications.js b/src/components/serverNotifications.js similarity index 92% rename from src/components/serverNotifications/serverNotifications.js rename to src/components/serverNotifications.js index 1087e46675..5554925272 100644 --- a/src/components/serverNotifications/serverNotifications.js +++ b/src/components/serverNotifications.js @@ -191,36 +191,14 @@ define(['connectionManager', 'playbackManager', 'events', 'inputManager', 'focus events.trigger(serverNotifications, msg.MessageType, [apiClient, msg.Data]); } } - function bindEvents(apiClient) { events.off(apiClient, "message", onMessageReceived); events.on(apiClient, "message", onMessageReceived); } - function enableNativeGamepadKeyMapping() { - if (window.navigator && "string" == typeof window.navigator.gamepadInputEmulation) { - window.navigator.gamepadInputEmulation = "keyboard"; - return true; - } - - return false; - } - - function isGamepadSupported() { - return "ongamepadconnected" in window || navigator.getGamepads || navigator.webkitGetGamepads; - } - connectionManager.getApiClients().forEach(bindEvents); - events.on(connectionManager, 'apiclientcreated', function (e, newApiClient) { bindEvents(newApiClient); }); - - if (!enableNativeGamepadKeyMapping() && isGamepadSupported()) { - require(["components/serverNotifications/gamepadtokey"]); - } - - require(["components/serverNotifications/mouseManager"]); - return serverNotifications; }); diff --git a/src/scripts/site.js b/src/scripts/site.js index 599f69c531..fc18ae9504 100644 --- a/src/scripts/site.js +++ b/src/scripts/site.js @@ -492,6 +492,7 @@ var AppInfo = {}; require(["keyboardnavigation"], function(keyboardnavigation) { keyboardnavigation.enable(); }); + require(["mouseManager"]); require(["focusPreventScroll"]); require(["autoFocuser"], function(autoFocuser) { autoFocuser.enable(); @@ -904,9 +905,10 @@ var AppInfo = {}; define("htmlMediaHelper", [componentsPath + "/htmlMediaHelper"], returnFirstDependency); define("viewContainer", [componentsPath + "/viewContainer"], returnFirstDependency); define("dialogHelper", [componentsPath + "/dialogHelper/dialogHelper"], returnFirstDependency); - define("serverNotifications", [componentsPath + "/serverNotifications/serverNotifications"], returnFirstDependency); + define("serverNotifications", [componentsPath + "/serverNotifications"], returnFirstDependency); define("skinManager", [componentsPath + "/skinManager"], returnFirstDependency); - define("keyboardnavigation", [componentsPath + "/keyboardnavigation"], returnFirstDependency); + define("keyboardnavigation", [componentsPath + "/input/keyboardnavigation"], returnFirstDependency); + define("mouseManager", [componentsPath + "/input/mouseManager"], returnFirstDependency); define("scrollManager", [componentsPath + "/scrollManager"], returnFirstDependency); define("autoFocuser", [componentsPath + "/autoFocuser"], returnFirstDependency); define("connectionManager", [], function () {