Make mouse locking optional (#518)

Co-authored-by: tomciaaa <tomciaaa@chromebook>
This commit is contained in:
tomciaaa 2022-07-05 00:30:08 +01:00 committed by GitHub
parent 6b053e4e71
commit 765e104cb3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 94 additions and 17 deletions

View File

@ -79,6 +79,16 @@
</div>
</div>
<div class="nav-menu-parent">
<label id="mouseLockBtn" class="mdl-icon-toggle mdl-js-icon-toggle mdl-js-ripple-effect" for="mouseLockEnabledSwitch">
<input type="checkbox" id="mouseLockEnabledSwitch" class="mdl-icon-toggle__input">
<i class="mdl-icon-toggle__label material-icons">mouse</i>
</label>
<div id="mouseLockTooltip" class="mdl-tooltip" for="mouseLockBtn">
Enable mouse locking (must disable to use tap-to-click on touchpads)
</div>
</div>
<div class="nav-menu-parent">
<label id="optimizeGamesBtn" class="mdl-icon-toggle mdl-js-icon-toggle mdl-js-ripple-effect" for="optimizeGamesSwitch">
<input type="checkbox" id="optimizeGamesSwitch" class="mdl-icon-toggle__input">

View File

@ -1,6 +1,7 @@
#include "moonlight.hpp"
#include "ppapi/c/ppb_input_event.h"
#include "ppapi/cpp/mouse_cursor.h"
#include "ppapi/cpp/input_event.h"
@ -29,6 +30,29 @@ static int ConvertPPButtonToLiButton(PP_InputEvent_MouseButton ppButton) {
}
}
void MoonlightInstance::LockMouseOrJustCaptureInput() {
if (m_MouseLockingFeatureEnabled) {
LockMouse(m_CallbackFactory.NewCallback(&MoonlightInstance::DidLockMouse));
}
else {
pp::MouseCursor::SetCursor(this, PP_MOUSECURSOR_TYPE_NONE);
}
// Assume it worked until we get a callback telling us otherwise;
// if not locking mouse this just serves to tell us whether to capture input
m_MouseLocked = true;
}
void MoonlightInstance::UnlockMouseOrJustReleaseInput() {
if (m_MouseLockingFeatureEnabled) {
UnlockMouse();
} else {
pp::MouseCursor::SetCursor(this, PP_MOUSECURSOR_TYPE_POINTER);
}
m_MouseLocked = false;
m_WaitingForAllModifiersUp = false;
}
void MoonlightInstance::DidLockMouse(int32_t result) {
m_MouseLocked = (result == PP_OK);
if (m_MouseLocked) {
@ -123,6 +147,10 @@ void MoonlightInstance::ReportMouseMovement() {
if (m_MouseDeltaX != 0 || m_MouseDeltaY != 0) {
LiSendMouseMoveEvent(m_MouseDeltaX, m_MouseDeltaY);
m_MouseDeltaX = m_MouseDeltaY = 0;
} else if (m_MousePositionX != 0 || m_MousePositionY != 0) {
LiSendMousePositionEvent(m_MousePositionX, m_MousePositionY, m_PluginRect.width(), m_PluginRect.height());
m_MousePositionX = 0;
m_MousePositionY = 0;
}
if (m_AccumulatedTicks != 0) {
// We can have fractional ticks here, so multiply by WHEEL_DELTA
@ -137,10 +165,7 @@ bool MoonlightInstance::HandleInputEvent(const pp::InputEvent& event) {
case PP_INPUTEVENT_TYPE_MOUSEDOWN: {
// Lock the mouse cursor when the user clicks on the stream
if (!m_MouseLocked) {
LockMouse(m_CallbackFactory.NewCallback(&MoonlightInstance::DidLockMouse));
// Assume it worked until we get a callback telling us otherwise
m_MouseLocked = true;
LockMouseOrJustCaptureInput();
return true;
}
@ -154,14 +179,23 @@ bool MoonlightInstance::HandleInputEvent(const pp::InputEvent& event) {
if (!m_MouseLocked) {
return false;
}
pp::MouseInputEvent mouseEvent(event);
pp::Point posDelta = mouseEvent.GetMovement();
pp::Point position = mouseEvent.GetPosition();
// Wait to report mouse movement until the next input polling window
// to allow batching to occur which reduces overall input lag.
m_MouseDeltaX += posDelta.x();
m_MouseDeltaY += posDelta.y();
if (m_MouseLocked) {
if (m_MouseLockingFeatureEnabled) {
m_MouseDeltaX += posDelta.x();
m_MouseDeltaY += posDelta.y();
} else {
m_MousePositionX = position.x();
m_MousePositionY = position.y();
}
}
return true;
}
@ -229,9 +263,7 @@ bool MoonlightInstance::HandleInputEvent(const pp::InputEvent& event) {
// Check if all modifiers are up now
if (m_WaitingForAllModifiersUp && modifiers == 0) {
UnlockMouse();
m_MouseLocked = false;
m_WaitingForAllModifiersUp = false;
UnlockMouseOrJustReleaseInput();
}
LiSendKeyboardEvent(KEY_PREFIX << 8 | keyCode,
@ -284,4 +316,4 @@ bool MoonlightInstance::HandleInputEvent(const pp::InputEvent& event) {
return false;
}
}
}
}

View File

@ -59,7 +59,7 @@ void MoonlightInstance::OnConnectionStopped(uint32_t error) {
PP_INPUTEVENT_CLASS_TOUCH);
// Unlock the mouse
UnlockMouse();
UnlockMouseOrJustReleaseInput();
// Notify the JS code that the stream has ended
pp::Var response(std::string(MSG_STREAM_TERMINATED) + std::to_string((int)error));
@ -200,8 +200,9 @@ void MoonlightInstance::HandleStartStream(int32_t callbackId, pp::VarArray args)
std::string bitrate = args.Get(4).AsString();
std::string rikey = args.Get(5).AsString();
std::string rikeyid = args.Get(6).AsString();
std::string appversion = args.Get(7).AsString();
std::string gfeversion = args.Get(8).AsString();
std::string mouse_lock = args.Get(7).AsString();
std::string appversion = args.Get(8).AsString();
std::string gfeversion = args.Get(9).AsString();
pp::Var response("Setting stream width to: " + width);
PostMessage(response);
@ -221,6 +222,8 @@ void MoonlightInstance::HandleStartStream(int32_t callbackId, pp::VarArray args)
PostMessage(response);
response = ("Setting gfeversion to: " + gfeversion);
PostMessage(response);
response = ("Setting mouse lock to: " + mouse_lock);
PostMessage(response);
// Populate the stream configuration
LiInitializeStreamConfiguration(&m_StreamConfig);
@ -245,6 +248,7 @@ void MoonlightInstance::HandleStartStream(int32_t callbackId, pp::VarArray args)
m_Host = host;
m_AppVersion = appversion;
m_GfeVersion = gfeversion;
m_MouseLockingFeatureEnabled = stoi(mouse_lock);
// Initialize the rendering surface before starting the connection
if (InitializeRenderingSurface(m_StreamConfig.width, m_StreamConfig.height)) {

View File

@ -66,6 +66,8 @@ class MoonlightInstance : public pp::Instance, public pp::MouseLock {
m_AccumulatedTicks(0),
m_MouseDeltaX(0),
m_MouseDeltaY(0),
m_MousePositionX(0),
m_MousePositionY(0),
m_LastTouchUpTime(0),
m_HttpThreadPoolSequence(0) {
// This function MUST be used otherwise sockets don't work (nacl_io_init() doesn't work!)
@ -109,6 +111,8 @@ class MoonlightInstance : public pp::Instance, public pp::MouseLock {
void MouseLockLost();
void DidLockMouse(int32_t result);
void LockMouseOrJustCaptureInput();
void UnlockMouseOrJustReleaseInput();
void OnConnectionStopped(uint32_t unused);
void OnConnectionStarted(uint32_t error);
@ -170,6 +174,7 @@ class MoonlightInstance : public pp::Instance, public pp::MouseLock {
std::string m_Host;
std::string m_AppVersion;
std::string m_GfeVersion;
bool m_MouseLockingFeatureEnabled;
STREAM_CONFIGURATION m_StreamConfig;
bool m_Running;
@ -199,6 +204,7 @@ class MoonlightInstance : public pp::Instance, public pp::MouseLock {
bool m_WaitingForAllModifiersUp;
float m_AccumulatedTicks;
int32_t m_MouseDeltaX, m_MouseDeltaY;
int32_t m_MousePositionX, m_MousePositionY;
PP_TimeTicks m_LastTouchUpTime;
pp::FloatPoint m_LastTouchUpPoint;

View File

@ -15,6 +15,7 @@ function attachListeners() {
$('#bitrateSlider').on('input', updateBitrateField); // input occurs every notch you slide
//$('#bitrateSlider').on('change', saveBitrate); //FIXME: it seems not working
$("#remoteAudioEnabledSwitch").on('click', saveRemoteAudio);
$("#mouseLockEnabledSwitch").on('click', saveMouseLock);
$('#optimizeGamesSwitch').on('click', saveOptimize);
$('#addHostCell').on('click', addHost);
$('#backIcon').on('click', showHostsAndSettingsMode);
@ -597,6 +598,7 @@ function showHostsAndSettingsMode() {
$("#main-navigation").show();
$(".nav-menu-parent").show();
$("#externalAudioBtn").show();
$("#mouseLockBtn").show();
$("#main-content").children().not("#listener, #loadingSpinner, #naclSpinner").show();
$('#game-grid').hide();
$('#backIcon').hide();
@ -615,6 +617,7 @@ function showAppsMode() {
$("#streamSettings").hide();
$(".nav-menu-parent").hide();
$("#externalAudioBtn").hide();
$("#mouseLockBtn").hide();
$("#host-grid").hide();
$("#settings").hide();
$("#main-content").removeClass("fullscreen");
@ -684,7 +687,8 @@ function startGame(host, appID) {
var streamHeight = $('#selectResolution').data('value').split(':')[1];
// we told the user it was in Mbps. We're dirty liars and use Kbps behind their back.
var bitrate = parseInt($("#bitrateSlider").val()) * 1000;
console.log('%c[index.js, startGame]', 'color:green;', 'startRequest:' + host.address + ":" + streamWidth + ":" + streamHeight + ":" + frameRate + ":" + bitrate + ":" + optimize);
var mouse_lock_enabled = $("#mouseLockEnabledSwitch").parent().hasClass('is-checked') ? "1" : "0";
console.log('%c[index.js, startGame]', 'color:green;', 'startRequest:' + host.address + ":" + streamWidth + ":" + streamHeight + ":" + frameRate + ":" + bitrate + ":" + optimize + ":" + mouse_lock_enabled);
var rikey = generateRemoteInputKey();
var rikeyid = generateRemoteInputKeyId();
@ -707,7 +711,7 @@ function startGame(host, appID) {
}
sendMessage('startRequest', [host.address, streamWidth, streamHeight, frameRate,
bitrate.toString(), rikey, rikeyid.toString(), host.appVersion, host.gfeVersion
bitrate.toString(), rikey, rikeyid.toString(), mouse_lock_enabled, host.appVersion, host.gfeVersion
]);
}, function(failedResumeApp) {
console.eror('%c[index.js, startGame]', 'color:green;', 'Failed to resume the app! Returned error was' + failedResumeApp);
@ -744,7 +748,7 @@ function startGame(host, appID) {
}
sendMessage('startRequest', [host.address, streamWidth, streamHeight, frameRate,
bitrate.toString(), rikey, rikeyid.toString(), host.appVersion
bitrate.toString(), rikey, rikeyid.toString(), mouse_lock_enabled, host.appVersion
]);
}, function(failedLaunchApp) {
console.error('%c[index.js, launchApp]', 'color: green;', 'Failed to launch app width id: ' + appID + '\nReturned error was: ' + failedLaunchApp);
@ -901,6 +905,16 @@ function saveRemoteAudio() {
}, 100);
}
function saveMouseLock() {
// MaterialDesignLight uses the mouseup trigger, so we give it some time to change the class name before
// checking the new state
setTimeout(function() {
var mouseLockState = $("#mouseLockEnabledSwitch").parent().hasClass('is-checked');
console.log('%c[index.js, saveMouseLock]', 'color: green;', 'Saving mouse lock state : ' + mouseLockState);
storeData('mouseLock', mouseLockState, null);
}, 100);
}
function updateDefaultBitrate() {
var res = $('#selectResolution').data('value');
var frameRate = $('#selectFramerate').data('value').toString();
@ -961,6 +975,17 @@ function onWindowLoad() {
}
});
// Load stored mouse lock prefs
chrome.storage.sync.get('mouseLock', function(previousValue) {
if (previousValue.mouseLock == null) {
document.querySelector('#mouseLockBtn').MaterialIconToggle.check();
} else if (previousValue.mouseLock == false) {
document.querySelector('#mouseLockBtn').MaterialIconToggle.uncheck();
} else {
document.querySelector('#mouseLockBtn').MaterialIconToggle.check();
}
});
// load stored framerate prefs
chrome.storage.sync.get('frameRate', function(previousValue) {
if (previousValue.frameRate != null) {