mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2026-06-18 06:30:55 +00:00
Replace fixed "unsupported FPS" options with fully custom FPS option
Fixes #1003
This commit is contained in:
+152
-57
@@ -388,12 +388,124 @@ Flickable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AutoResizingComboBox {
|
AutoResizingComboBox {
|
||||||
function addRefreshRateOrdered(fpsListModel, refreshRate, description) {
|
property int lastIndexValue
|
||||||
|
|
||||||
|
function updateBitrateForSelection() {
|
||||||
|
// Only modify the bitrate if the values actually changed
|
||||||
|
var selectedFps = parseInt(model.get(fpsComboBox.currentIndex).video_fps)
|
||||||
|
if (StreamingPreferences.fps !== selectedFps) {
|
||||||
|
StreamingPreferences.fps = selectedFps
|
||||||
|
|
||||||
|
StreamingPreferences.bitrateKbps = StreamingPreferences.getDefaultBitrate(StreamingPreferences.width,
|
||||||
|
StreamingPreferences.height,
|
||||||
|
StreamingPreferences.fps);
|
||||||
|
slider.value = StreamingPreferences.bitrateKbps
|
||||||
|
}
|
||||||
|
|
||||||
|
lastIndexValue = currentIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
NavigableDialog {
|
||||||
|
function isInputValid() {
|
||||||
|
// If we have text that isn't valid, reject the input.
|
||||||
|
if (!fpsField.acceptableInput && fpsField.text) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// The textbox needs to have text or placeholder text
|
||||||
|
if (!fpsField.text && !fpsField.placeholderText) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
id: customFpsDialog
|
||||||
|
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||||
|
onOpened: {
|
||||||
|
// Force keyboard focus on the textbox so keyboard navigation works
|
||||||
|
fpsField.forceActiveFocus()
|
||||||
|
|
||||||
|
// standardButton() was added in Qt 5.10, so we must check for it first
|
||||||
|
if (customFpsDialog.standardButton) {
|
||||||
|
customFpsDialog.standardButton(Dialog.Ok).enabled = customFpsDialog.isInputValid()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClosed: {
|
||||||
|
fpsField.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
onRejected: {
|
||||||
|
fpsComboBox.currentIndex = fpsComboBox.lastIndexValue
|
||||||
|
}
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
// Reject if there's invalid input
|
||||||
|
if (!isInputValid()) {
|
||||||
|
reject()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var fps = fpsField.text ? fpsField.text : fpsField.placeholderText
|
||||||
|
|
||||||
|
// Find and update the custom entry
|
||||||
|
for (var i = 0; i < fpsListModel.count; i++) {
|
||||||
|
if (fpsListModel.get(i).is_custom) {
|
||||||
|
fpsListModel.setProperty(i, "video_fps", fps)
|
||||||
|
fpsListModel.setProperty(i, "text", qsTr("Custom (%1 FPS)").arg(fps))
|
||||||
|
|
||||||
|
// Now update the bitrate using the custom resolution
|
||||||
|
fpsComboBox.currentIndex = i
|
||||||
|
fpsComboBox.updateBitrateForSelection()
|
||||||
|
|
||||||
|
// Update the combobox width too
|
||||||
|
fpsComboBox.recalculateWidth()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Label {
|
||||||
|
text: qsTr("Enter a custom frame rate:")
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
TextField {
|
||||||
|
id: fpsField
|
||||||
|
maximumLength: 4
|
||||||
|
inputMethodHints: Qt.ImhDigitsOnly
|
||||||
|
placeholderText: fpsListModel.get(fpsComboBox.currentIndex).video_fps
|
||||||
|
validator: IntValidator{bottom:1; top:9999}
|
||||||
|
focus: true
|
||||||
|
|
||||||
|
onTextChanged: {
|
||||||
|
// standardButton() was added in Qt 5.10, so we must check for it first
|
||||||
|
if (customFpsDialog.standardButton) {
|
||||||
|
customFpsDialog.standardButton(Dialog.Ok).enabled = customFpsDialog.isInputValid()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onReturnPressed: {
|
||||||
|
customFpsDialog.accept()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onEnterPressed: {
|
||||||
|
customFpsDialog.accept()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addRefreshRateOrdered(fpsListModel, refreshRate, description, custom) {
|
||||||
var indexToAdd = 0
|
var indexToAdd = 0
|
||||||
for (var j = 0; j < fpsListModel.count; j++) {
|
for (var j = 0; j < fpsListModel.count; j++) {
|
||||||
var existing_fps = parseInt(fpsListModel.get(j).video_fps);
|
var existing_fps = parseInt(fpsListModel.get(j).video_fps);
|
||||||
|
|
||||||
if (refreshRate === existing_fps) {
|
if (refreshRate === existing_fps || (custom && fpsListModel.get(j).is_custom)) {
|
||||||
// Duplicate entry, skip
|
// Duplicate entry, skip
|
||||||
indexToAdd = -1
|
indexToAdd = -1
|
||||||
break
|
break
|
||||||
@@ -404,25 +516,25 @@ Flickable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert this display's resolution if it's not a duplicate
|
// Insert this frame rate if it's not a duplicate
|
||||||
if (indexToAdd >= 0) {
|
if (indexToAdd >= 0) {
|
||||||
|
// Custom values always go at the end of the list
|
||||||
|
if (custom) {
|
||||||
|
indexToAdd = fpsListModel.count
|
||||||
|
}
|
||||||
|
|
||||||
fpsListModel.insert(indexToAdd,
|
fpsListModel.insert(indexToAdd,
|
||||||
{
|
{
|
||||||
"text": description,
|
"text": description,
|
||||||
"video_fps": ""+refreshRate
|
"video_fps": ""+refreshRate,
|
||||||
|
"is_custom": custom
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return indexToAdd
|
return indexToAdd
|
||||||
}
|
}
|
||||||
|
|
||||||
function createModel() {
|
function reinitialize() {
|
||||||
var fpsListModel = Qt.createQmlObject('import QtQuick 2.0; ListModel {}', parent, '')
|
|
||||||
|
|
||||||
// Default entries
|
|
||||||
fpsListModel.append({"text": qsTr("%1 FPS").arg("30"), "video_fps": "30"})
|
|
||||||
fpsListModel.append({"text": qsTr("%1 FPS").arg("60"), "video_fps": "60"})
|
|
||||||
|
|
||||||
// Add native refresh rate for all attached displays
|
// Add native refresh rate for all attached displays
|
||||||
var done = false
|
var done = false
|
||||||
for (var displayIndex = 0; !done; displayIndex++) {
|
for (var displayIndex = 0; !done; displayIndex++) {
|
||||||
@@ -433,21 +545,9 @@ Flickable {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
addRefreshRateOrdered(fpsListModel, refreshRate, qsTr("%1 FPS").arg(refreshRate))
|
addRefreshRateOrdered(fpsListModel, refreshRate, qsTr("%1 FPS").arg(refreshRate), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add unsupported FPS values
|
|
||||||
if (StreamingPreferences.unsupportedFps) {
|
|
||||||
addRefreshRateOrdered(fpsListModel, 90, qsTr("%1 FPS (Unsupported)").arg(90))
|
|
||||||
addRefreshRateOrdered(fpsListModel, 120, qsTr("%1 FPS (Unsupported)").arg(120))
|
|
||||||
}
|
|
||||||
|
|
||||||
return fpsListModel
|
|
||||||
}
|
|
||||||
|
|
||||||
function reinitialize() {
|
|
||||||
model = createModel()
|
|
||||||
|
|
||||||
var saved_fps = StreamingPreferences.fps
|
var saved_fps = StreamingPreferences.fps
|
||||||
currentIndex = -1
|
currentIndex = -1
|
||||||
for (var i = 0; i < model.count; i++) {
|
for (var i = 0; i < model.count; i++) {
|
||||||
@@ -462,11 +562,15 @@ Flickable {
|
|||||||
|
|
||||||
// If we didn't find one, add a custom frame rate for the current value
|
// If we didn't find one, add a custom frame rate for the current value
|
||||||
if (currentIndex === -1) {
|
if (currentIndex === -1) {
|
||||||
currentIndex = addRefreshRateOrdered(model, saved_fps, qsTr("%1 FPS (Custom)").arg(saved_fps))
|
currentIndex = addRefreshRateOrdered(model, saved_fps, qsTr("Custom (%1 FPS)").arg(saved_fps), true)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
addRefreshRateOrdered(model, "", qsTr("Custom"), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Persist the selected value
|
recalculateWidth()
|
||||||
activated(currentIndex)
|
|
||||||
|
lastIndexValue = currentIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore setting the index at first, and actually set it when the component is loaded
|
// ignore setting the index at first, and actually set it when the component is loaded
|
||||||
@@ -475,21 +579,31 @@ Flickable {
|
|||||||
languageChanged.connect(reinitialize)
|
languageChanged.connect(reinitialize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model: ListModel {
|
||||||
|
id: fpsListModel
|
||||||
|
// Other elements may be added at runtime
|
||||||
|
ListElement {
|
||||||
|
text: qsTr("30 FPS")
|
||||||
|
video_fps: "30"
|
||||||
|
is_custom: false
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
text: qsTr("60 FPS")
|
||||||
|
video_fps: "60"
|
||||||
|
is_custom: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
id: fpsComboBox
|
id: fpsComboBox
|
||||||
maximumWidth: parent.width / 2
|
maximumWidth: parent.width / 2
|
||||||
textRole: "text"
|
textRole: "text"
|
||||||
// ::onActivated must be used, as it only listens for when the index is changed by a human
|
// ::onActivated must be used, as it only listens for when the index is changed by a human
|
||||||
onActivated : {
|
onActivated : {
|
||||||
var selectedFps = parseInt(model.get(currentIndex).video_fps)
|
if (model.get(currentIndex).is_custom) {
|
||||||
|
customFpsDialog.open()
|
||||||
// Only modify the bitrate if the values actually changed
|
}
|
||||||
if (StreamingPreferences.fps !== selectedFps) {
|
else {
|
||||||
StreamingPreferences.fps = selectedFps
|
updateBitrateForSelection()
|
||||||
|
|
||||||
StreamingPreferences.bitrateKbps = StreamingPreferences.getDefaultBitrate(StreamingPreferences.width,
|
|
||||||
StreamingPreferences.height,
|
|
||||||
StreamingPreferences.fps);
|
|
||||||
slider.value = StreamingPreferences.bitrateKbps
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1445,25 +1559,6 @@ Flickable {
|
|||||||
qsTr("HDR streaming is not supported on this PC.")
|
qsTr("HDR streaming is not supported on this PC.")
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckBox {
|
|
||||||
id: unlockUnsupportedFps
|
|
||||||
width: parent.width
|
|
||||||
text: qsTr("Unlock unsupported FPS options")
|
|
||||||
font.pointSize: 12
|
|
||||||
checked: StreamingPreferences.unsupportedFps
|
|
||||||
onCheckedChanged: {
|
|
||||||
// This is called on init, so only do the work if we've
|
|
||||||
// actually changed the value.
|
|
||||||
if (StreamingPreferences.unsupportedFps != checked) {
|
|
||||||
StreamingPreferences.unsupportedFps = checked
|
|
||||||
|
|
||||||
// The selectable FPS values depend on whether
|
|
||||||
// this option is enabled or not
|
|
||||||
fpsComboBox.reinitialize()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckBox {
|
CheckBox {
|
||||||
id: enableMdns
|
id: enableMdns
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
#define SER_HDR "hdr"
|
#define SER_HDR "hdr"
|
||||||
#define SER_VIDEODEC "videodec"
|
#define SER_VIDEODEC "videodec"
|
||||||
#define SER_WINDOWMODE "windowmode"
|
#define SER_WINDOWMODE "windowmode"
|
||||||
#define SER_UNSUPPORTEDFPS "unsupportedfps"
|
|
||||||
#define SER_MDNS "mdns"
|
#define SER_MDNS "mdns"
|
||||||
#define SER_QUITAPPAFTER "quitAppAfter"
|
#define SER_QUITAPPAFTER "quitAppAfter"
|
||||||
#define SER_ABSMOUSEMODE "mouseacceleration"
|
#define SER_ABSMOUSEMODE "mouseacceleration"
|
||||||
@@ -88,7 +87,6 @@ void StreamingPreferences::reload()
|
|||||||
gameOptimizations = settings.value(SER_GAMEOPTS, true).toBool();
|
gameOptimizations = settings.value(SER_GAMEOPTS, true).toBool();
|
||||||
playAudioOnHost = settings.value(SER_HOSTAUDIO, false).toBool();
|
playAudioOnHost = settings.value(SER_HOSTAUDIO, false).toBool();
|
||||||
multiController = settings.value(SER_MULTICONT, true).toBool();
|
multiController = settings.value(SER_MULTICONT, true).toBool();
|
||||||
unsupportedFps = settings.value(SER_UNSUPPORTEDFPS, false).toBool();
|
|
||||||
enableMdns = settings.value(SER_MDNS, true).toBool();
|
enableMdns = settings.value(SER_MDNS, true).toBool();
|
||||||
quitAppAfter = settings.value(SER_QUITAPPAFTER, false).toBool();
|
quitAppAfter = settings.value(SER_QUITAPPAFTER, false).toBool();
|
||||||
absoluteMouseMode = settings.value(SER_ABSMOUSEMODE, false).toBool();
|
absoluteMouseMode = settings.value(SER_ABSMOUSEMODE, false).toBool();
|
||||||
@@ -271,7 +269,6 @@ void StreamingPreferences::save()
|
|||||||
settings.setValue(SER_GAMEOPTS, gameOptimizations);
|
settings.setValue(SER_GAMEOPTS, gameOptimizations);
|
||||||
settings.setValue(SER_HOSTAUDIO, playAudioOnHost);
|
settings.setValue(SER_HOSTAUDIO, playAudioOnHost);
|
||||||
settings.setValue(SER_MULTICONT, multiController);
|
settings.setValue(SER_MULTICONT, multiController);
|
||||||
settings.setValue(SER_UNSUPPORTEDFPS, unsupportedFps);
|
|
||||||
settings.setValue(SER_MDNS, enableMdns);
|
settings.setValue(SER_MDNS, enableMdns);
|
||||||
settings.setValue(SER_QUITAPPAFTER, quitAppAfter);
|
settings.setValue(SER_QUITAPPAFTER, quitAppAfter);
|
||||||
settings.setValue(SER_ABSMOUSEMODE, absoluteMouseMode);
|
settings.setValue(SER_ABSMOUSEMODE, absoluteMouseMode);
|
||||||
|
|||||||
@@ -111,7 +111,6 @@ public:
|
|||||||
Q_PROPERTY(bool gameOptimizations MEMBER gameOptimizations NOTIFY gameOptimizationsChanged)
|
Q_PROPERTY(bool gameOptimizations MEMBER gameOptimizations NOTIFY gameOptimizationsChanged)
|
||||||
Q_PROPERTY(bool playAudioOnHost MEMBER playAudioOnHost NOTIFY playAudioOnHostChanged)
|
Q_PROPERTY(bool playAudioOnHost MEMBER playAudioOnHost NOTIFY playAudioOnHostChanged)
|
||||||
Q_PROPERTY(bool multiController MEMBER multiController NOTIFY multiControllerChanged)
|
Q_PROPERTY(bool multiController MEMBER multiController NOTIFY multiControllerChanged)
|
||||||
Q_PROPERTY(bool unsupportedFps MEMBER unsupportedFps NOTIFY unsupportedFpsChanged)
|
|
||||||
Q_PROPERTY(bool enableMdns MEMBER enableMdns NOTIFY enableMdnsChanged)
|
Q_PROPERTY(bool enableMdns MEMBER enableMdns NOTIFY enableMdnsChanged)
|
||||||
Q_PROPERTY(bool quitAppAfter MEMBER quitAppAfter NOTIFY quitAppAfterChanged)
|
Q_PROPERTY(bool quitAppAfter MEMBER quitAppAfter NOTIFY quitAppAfterChanged)
|
||||||
Q_PROPERTY(bool absoluteMouseMode MEMBER absoluteMouseMode NOTIFY absoluteMouseModeChanged)
|
Q_PROPERTY(bool absoluteMouseMode MEMBER absoluteMouseMode NOTIFY absoluteMouseModeChanged)
|
||||||
@@ -148,7 +147,6 @@ public:
|
|||||||
bool gameOptimizations;
|
bool gameOptimizations;
|
||||||
bool playAudioOnHost;
|
bool playAudioOnHost;
|
||||||
bool multiController;
|
bool multiController;
|
||||||
bool unsupportedFps;
|
|
||||||
bool enableMdns;
|
bool enableMdns;
|
||||||
bool quitAppAfter;
|
bool quitAppAfter;
|
||||||
bool absoluteMouseMode;
|
bool absoluteMouseMode;
|
||||||
|
|||||||
@@ -812,14 +812,6 @@ bool Session::validateLaunch(SDL_Window* testWindow)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_Preferences->unsupportedFps && m_StreamConfig.fps > 60) {
|
|
||||||
emitLaunchWarning(tr("Using unsupported FPS options may cause stuttering or lag."));
|
|
||||||
|
|
||||||
if (m_Preferences->enableVsync) {
|
|
||||||
emitLaunchWarning(tr("V-sync will be disabled when streaming at a higher frame rate than the display."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_MASK_AV1) {
|
if (m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_MASK_AV1) {
|
||||||
if (!(m_Computer->serverCodecModeSupport & SCM_MASK_AV1)) {
|
if (!(m_Computer->serverCodecModeSupport & SCM_MASK_AV1)) {
|
||||||
if (m_Preferences->videoCodecConfig == StreamingPreferences::VCC_FORCE_AV1) {
|
if (m_Preferences->videoCodecConfig == StreamingPreferences::VCC_FORCE_AV1) {
|
||||||
|
|||||||
Reference in New Issue
Block a user