diff --git a/connectionlistener.cpp b/connectionlistener.cpp index 37c84e2..3174a18 100644 --- a/connectionlistener.cpp +++ b/connectionlistener.cpp @@ -61,4 +61,5 @@ CONNECTION_LISTENER_CALLBACKS MoonlightInstance::s_ClCallbacks = { .connectionStarted = MoonlightInstance::ClConnectionStarted, .connectionTerminated = MoonlightInstance::ClConnectionTerminated, .logMessage = MoonlightInstance::ClLogMessage, + .rumble = MoonlightInstance::ClControllerRumble, }; diff --git a/gamepad.cpp b/gamepad.cpp index 93df0fc..eac6979 100644 --- a/gamepad.cpp +++ b/gamepad.cpp @@ -4,6 +4,8 @@ #include +#include + static const unsigned short k_StandardGamepadButtonMapping[] = { A_FLAG, B_FLAG, X_FLAG, Y_FLAG, LB_FLAG, RB_FLAG, @@ -127,3 +129,14 @@ void MoonlightInstance::PollGamepads() { controllerIndex++; } } + +void MoonlightInstance::ClControllerRumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor) +{ + const float weakMagnitude = static_cast(highFreqMotor) / static_cast(UINT16_MAX); + const float strongMagnitude = static_cast(lowFreqMotor) / static_cast(UINT16_MAX); + + std::ostringstream ss; + ss << controllerNumber << "," << weakMagnitude << "," << strongMagnitude; + pp::Var response(std::string("controllerRumble: ") + ss.str()); + g_Instance->PostMessage(response); +} \ No newline at end of file diff --git a/manifest.json b/manifest.json index ac1aef2..2a509bb 100644 --- a/manifest.json +++ b/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "Moonlight Game Streaming", "short_name": "Moonlight", - "version": "0.10.21", + "version": "0.10.22", "description": "Open-source client for NVIDIA GameStream", "icons": { "128": "icons/icon128.png", diff --git a/moonlight.hpp b/moonlight.hpp index 656b0c0..8af689b 100644 --- a/moonlight.hpp +++ b/moonlight.hpp @@ -137,6 +137,7 @@ class MoonlightInstance : public pp::Instance, public pp::MouseLock { static void ClDisplayMessage(const char* message); static void ClDisplayTransientMessage(const char* message); static void ClLogMessage(const char* format, ...); + static void ClControllerRumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor); static Shader CreateProgram(const char* vertexShader, const char* fragmentShader); static void CreateShader(GLuint program, GLenum type, const char* source, int size); diff --git a/static/js/messages.js b/static/js/messages.js index d8ab820..3a0970d 100644 --- a/static/js/messages.js +++ b/static/js/messages.js @@ -81,6 +81,18 @@ function handleMessage(msg) { snackbarLogLong(msg.data.replace('DialogMsg: ', '')); } else if (msg.data === 'displayVideo') { $("#listener").addClass("fullscreen"); + } else if (msg.data.indexOf('controllerRumble: ' ) === 0) { + const eventData = msg.data.split( ' ' )[1].split(','); + const gamepadIdx = parseInt(eventData[0]); + const weakMagnitude = parseFloat(eventData[1]); + const strongMagnitude = parseFloat(eventData[2]); + console.log("Playing rumble on gamepad " + gamepadIdx + " with weakMagnitude " + weakMagnitude + " and strongMagnitude " + strongMagnitude); + navigator.getGamepads()[gamepadIdx].vibrationActuator.playEffect('dual-rumble', { + startDelay: 0, + duration: 5000, // Moonlight should be sending another rumble event when stopping. + weakMagnitude: weakMagnitude, + strongMagnitude: strongMagnitude, + }); } } }