Skip to content

Commit 823cd68

Browse files
committed
update sunshine base script, add new refresh rate option
1 parent cbe7f34 commit 823cd68

6 files changed

Lines changed: 320 additions & 65 deletions

File tree

Events.ps1

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ function OnStreamStart() {
2424
$script:arguments['original_resolution'] = Get-HostResolution
2525
$expectedRes = Join-Overrides -width $env:SUNSHINE_CLIENT_WIDTH -height $env:SUNSHINE_CLIENT_HEIGHT -refresh $env:SUNSHINE_CLIENT_FPS
2626
$expectedRes = Set-10bitCompatibilityIfApplicable -width $expectedRes.Width -height $expectedRes.Height -refresh $expectedRes.Refresh
27+
# If highest refresh rate is enabled in settings, override the refresh rate with the highest available
28+
if ($settings.highestRefreshRate -eq $true) {
29+
$highest = Get-HighestRefreshRateForResolution $expectedRes.Width $expectedRes.Height
30+
Write-Host "Highest refresh rate enabled. Overriding refresh rate to $highest."
31+
$expectedRes.Refresh = $highest
32+
}
2733
Set-ScreenResolution -Width $expectedRes.Width -Height $expectedRes.Height -Freq $expectedRes.Refresh
2834
Assert-ResolutionChange -width $expectedRes.Width -height $expectedRes.Height -refresh $expectedRes.Refresh
2935
}
@@ -53,6 +59,8 @@ function OnStreamEnd($kwargs) {
5359
}
5460

5561

62+
63+
5664
Function Set-ScreenResolution($width, $height, $frequency) {
5765
Write-Debug "Function Set-ScreenResolution called with Width: $width, Height: $height, Frequency: $frequency"
5866
Write-Host "Setting screen resolution to $width x $height x $frequency"
@@ -258,4 +266,22 @@ function Set-10bitCompatibilityIfApplicable($width, $height, $refresh) {
258266
Height = $height
259267
Refresh = $refresh
260268
}
269+
}
270+
271+
function Get-HighestRefreshRateForResolution($width, $height) {
272+
Write-Debug "Function Get-HighestRefreshRateForResolution called with Width: $width, Height: $height"
273+
$highestRefresh = 0
274+
$modeNum = 0
275+
$devMode = New-Object DisplaySettings+DEVMODE
276+
$devMode.dmSize = [System.Runtime.InteropServices.Marshal]::SizeOf($devMode)
277+
while ([DisplaySettings]::EnumDisplaySettings([NullString]::Value, $modeNum, [ref]$devMode)) {
278+
if (($devMode.dmPelsWidth -eq $width) -and ($devMode.dmPelsHeight -eq $height)) {
279+
if ($devMode.dmDisplayFrequency -gt $highestRefresh) {
280+
$highestRefresh = $devMode.dmDisplayFrequency
281+
}
282+
}
283+
$modeNum++
284+
}
285+
Write-Debug "Highest refresh rate for resolution $width x $height is $highestRefresh"
286+
return $highestRefresh
261287
}

Helpers.ps1

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ $script:attempt = 0
1212
function OnStreamEndAsJob() {
1313
return Start-Job -Name "$scriptName-OnStreamEnd" -ScriptBlock {
1414
param($path, $scriptName, $arguments)
15+
16+
function Write-Debug($message){
17+
if ($arguments['debug']) {
18+
Write-Host "DEBUG: $message"
19+
}
20+
}
21+
22+
Write-Host $arguments
1523

1624
Write-Debug "Setting location to $path"
1725
Set-Location $path
@@ -41,6 +49,12 @@ function OnStreamEndAsJob() {
4149
break;
4250
}
4351

52+
53+
if ((IsCurrentlyStreaming)) {
54+
Write-Host "Streaming is active. To prevent potential conflicts, this script will now terminate prematurely."
55+
}
56+
57+
4458
while (($tries -lt $maxTries) -and ($job.State -ne "Completed")) {
4559
Start-Sleep -Milliseconds 200
4660
$tries++
@@ -56,7 +70,7 @@ function OnStreamEndAsJob() {
5670
function IsCurrentlyStreaming() {
5771
$sunshineProcess = Get-Process sunshine -ErrorAction SilentlyContinue
5872

59-
if($null -eq $sunshineProcess) {
73+
if ($null -eq $sunshineProcess) {
6074
return $false
6175
}
6276
return $null -ne (Get-NetUDPEndpoint -OwningProcess $sunshineProcess.Id -ErrorAction Ignore)
@@ -91,7 +105,8 @@ function Send-PipeMessage($pipeName, $message) {
91105
# We don't care if the disposal fails, this is common with async pipes.
92106
# Also, this powershell script will terminate anyway.
93107
}
94-
} else {
108+
}
109+
else {
95110
Write-Debug "Pipe not found: $pipeName"
96111
}
97112
}
@@ -105,12 +120,12 @@ function Create-Pipe($pipeName) {
105120
$pipe = New-Object System.IO.Pipes.NamedPipeServerStream($pipeName, [System.IO.Pipes.PipeDirection]::In, 10, [System.IO.Pipes.PipeTransmissionMode]::Byte, [System.IO.Pipes.PipeOptions]::Asynchronous)
106121

107122
$streamReader = New-Object System.IO.StreamReader($pipe)
108-
Write-Output "Waiting for named pipe to recieve kill command"
123+
Write-Host "Waiting for named pipe to recieve kill command"
109124
$pipe.WaitForConnection()
110125

111126
$message = $streamReader.ReadLine()
112127
if ($message) {
113-
Write-Output "Terminating pipe..."
128+
Write-Host "Terminating pipe..."
114129
$pipe.Dispose()
115130
$streamReader.Dispose()
116131
New-Event -SourceIdentifier $scriptName -MessageData $message
@@ -186,6 +201,47 @@ function Get-Settings {
186201
}
187202
}
188203

204+
function Update-JsonProperty {
205+
[CmdletBinding()]
206+
param(
207+
[Parameter(Mandatory = $true)]
208+
[string]$FilePath,
209+
210+
[Parameter(Mandatory = $true)]
211+
[string]$Property,
212+
213+
[Parameter(Mandatory = $true)]
214+
[object]$NewValue
215+
)
216+
217+
# Read the file as a single string.
218+
$content = Get-Content -Path $FilePath -Raw
219+
220+
if ($NewValue -is [string]) {
221+
# Convert the string to a JSON-compliant string.
222+
# ConvertTo-Json will take care of escaping characters properly.
223+
$formattedValue = (ConvertTo-Json $NewValue -Compress)
224+
}
225+
else {
226+
$formattedValue = $NewValue.ToString()
227+
}
228+
229+
# Build a regex pattern for matching the property.
230+
$escapedProperty = [regex]::Escape($Property)
231+
$pattern = '"' + $escapedProperty + '"\s*:\s*[^,}\r\n]+'
232+
233+
# Build the replacement string.
234+
$replacement = '"' + $Property + '": ' + $formattedValue
235+
236+
# Replace the matching part in the content.
237+
$updatedContent = [regex]::Replace($content, $pattern, { param($match) $replacement })
238+
239+
# Write the updated content back.
240+
Set-Content -Path $FilePath -Value $updatedContent
241+
}
242+
243+
244+
189245
function Wait-ForStreamEndJobToComplete() {
190246
$job = OnStreamEndAsJob
191247
while ($job.State -ne "Completed") {

Installer.ps1

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ function Test-AndRequest-SunshineConfig {
7373

7474
# Define the path to the Sunshine configuration file
7575
$confPath = Test-AndRequest-SunshineConfig -InitialPath "C:\Program Files\Sunshine\config\sunshine.conf"
76+
Update-JsonProperty -FilePath "./settings.json" -Property "sunshineConfigPath" -NewValue $confPath
7677
$scriptRoot = Split-Path $scriptPath -Parent
7778

7879

@@ -107,7 +108,7 @@ function Remove-Command {
107108

108109
# Remove any existing matching Commands
109110
for ($i = 0; $i -lt $globalPrepCmdArray.Count; $i++) {
110-
if (-not ($globalPrepCmdArray[$i].do -like "*$scriptName*")) {
111+
if (-not ($globalPrepCmdArray[$i].do -like "*$scriptRoot*")) {
111112
$filteredCommands += $globalPrepCmdArray[$i]
112113
}
113114
}
@@ -213,7 +214,7 @@ function Add-Command {
213214
$command = [PSCustomObject]@{
214215
do = "powershell.exe -executionpolicy bypass -file `"$($scriptPath)`" -n $scriptName"
215216
elevated = "false"
216-
undo = "powershell.exe -executionpolicy bypass -file `"$($scriptRoot)\Helpers.ps1`" -n $scriptName -t 1"
217+
undo = "powershell.exe -executionpolicy bypass -file `"$($scriptRoot)\UndoScript.ps1`" -n $scriptName"
217218
}
218219

219220
# Add the new object to the global_prep_cmd array

Readme.md

Lines changed: 58 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,58 @@
1-
## Host Resolution Matching for Moonlight Streaming
2-
3-
This script changes your host resolution to match exactly with Moonlight's resolution. This is mostly used for users who have different aspect ratios between the client and host, or anyone who wishes to match the resolution while streaming.
4-
5-
### Requirements
6-
7-
- Host must be Windows.
8-
- Sunshine 0.21.0 or higher
9-
10-
### Caveats:
11-
- If using Windows 11, you'll need to set the default terminal to Windows Console Host as there is currently a bug in Windows Terminal that prevents hidden consoles from working properly.
12-
* That can be changed at Settings > System > For Developers > Terminal [Let Windows decide] >> (change to) >> Terminal [Windows Console Host]
13-
* On older versions of Windows 11 it can be found at: Settings > Privacy & security > Security > For developers > Terminal [Let Windows decide] >> (change to) >> Terminal [Windows Console Host]
14-
- The script will stop working if you move the folder, simply reinstall it to resolve that issue.
15-
- Due to Windows API restrictions, this script does not work on cold reboots (hard crashes or shutdowns of your computer).
16-
* If you're cold booting, simply sign into the computer using the "Desktop" app on Moonlight, then end the stream, then start it again.
17-
18-
#### GFE Users
19-
- You'll need to use the Geforce Experience version of this script instead.
20-
- The current release for Geforce Experience users is: https://github.com/Nonary/ResolutionAutomation/releases/tag/2.0.15_gfe
21-
22-
### Installation Instructions
23-
1. Store the downloaded folder in a location you intend to keep. If you delete this folder or move it, the automation will stop working.
24-
2. To install, double click the Install.bat file.
25-
3. To uninstall, double click the Uninstall.bat file.
26-
27-
This script will ask for elevated rights because Sunshine configuration is be locked from modifications for non-administrator users.
28-
29-
### How it Works
30-
1. When you start streaming any application in Sunshine, it will start the script.
31-
2. The script reads the environment variables passed to it via Sunshine, which contains client information such as screen resolution.
32-
3. It sets the host's resolution to match the Moonlight resolution (including refresh rate), unless overridden with the `overrides` file.
33-
4. The script waits for Sunshine to be suspended for more than 120 seconds or until the user ends the stream.
34-
5. It sets the host resolution back to the same resolution it was prior to starting the stream (including refresh rate).
35-
36-
This will only work if the resolution is available to be used, so you will need to make sure to use NVIDIA Custom Resolution or CRU to add the client resolution first.
37-
38-
### Overrides (Setting)
39-
You may have a mobile device that you wish to stream at a lower resolution to save bandwidth or some devices may perform better when streaming at a lower resolution. If you want your host to change the resolution to something higher than the client, make modifications to the overrides section in the settings.json file
40-
41-
#### Format
42-
```
43-
WidthxHeightxRefresh=WidthxHeightxRefresh
44-
```
45-
46-
The resolution on the left is what triggers the override, and the one on the right is what the host will be set to.
47-
48-
#### Example
49-
To stream at 720p and keep the host at 4k resolution, you would add this line:
50-
```
51-
"overrides": [
52-
// recommended for steam deck users, uncomment to enable, but make sure you have 3840x2400 added on your host!
53-
// sunshine has issues downscaling to smaller resolutions, so it is recommended to stream above native (you will see a significant difference)
54-
// simply uncomment below line once done
55-
//"2560x1440x90=3840x2400x60",
56-
"1280x720x60=3840x2160x60"
57-
]
58-
```
1+
## Host Resolution Matching for Moonlight Streaming
2+
3+
This script changes your host resolution to match exactly with Moonlight's resolution. This is mostly used for users who have different aspect ratios between the client and host, or anyone who wishes to match the resolution while streaming.
4+
5+
### Requirements
6+
7+
- Host must be Windows.
8+
- Sunshine 0.21.0 or higher
9+
10+
### Caveats:
11+
- If using Windows 11, you'll need to set the default terminal to Windows Console Host as there is currently a bug in Windows Terminal that prevents hidden consoles from working properly.
12+
* That can be changed at Settings > System > For Developers > Terminal [Let Windows decide] >> (change to) >> Terminal [Windows Console Host]
13+
* On older versions of Windows 11 it can be found at: Settings > Privacy & security > Security > For developers > Terminal [Let Windows decide] >> (change to) >> Terminal [Windows Console Host]
14+
- The script will stop working if you move the folder, simply reinstall it to resolve that issue.
15+
- Due to Windows API restrictions, this script does not work on cold reboots (hard crashes or shutdowns of your computer).
16+
* If you're cold booting, simply sign into the computer using the "Desktop" app on Moonlight, then end the stream, then start it again.
17+
18+
#### GFE Users
19+
- You'll need to use the Geforce Experience version of this script instead.
20+
- The current release for Geforce Experience users is: https://github.com/Nonary/ResolutionAutomation/releases/tag/2.0.15_gfe
21+
22+
### Installation Instructions
23+
1. Store the downloaded folder in a location you intend to keep. If you delete this folder or move it, the automation will stop working.
24+
2. To install, double click the Install.bat file.
25+
3. To uninstall, double click the Uninstall.bat file.
26+
27+
This script will ask for elevated rights because Sunshine configuration is be locked from modifications for non-administrator users.
28+
29+
### How it Works
30+
1. When you start streaming any application in Sunshine, it will start the script.
31+
2. The script reads the environment variables passed to it via Sunshine, which contains client information such as screen resolution.
32+
3. It sets the host's resolution to match the Moonlight resolution (including refresh rate), unless overridden with the `overrides` file.
33+
4. The script waits for Sunshine to be suspended for more than 120 seconds or until the user ends the stream.
34+
5. It sets the host resolution back to the same resolution it was prior to starting the stream (including refresh rate).
35+
36+
This will only work if the resolution is available to be used, so you will need to make sure to use NVIDIA Custom Resolution or CRU to add the client resolution first.
37+
38+
### Overrides (Setting)
39+
You may have a mobile device that you wish to stream at a lower resolution to save bandwidth or some devices may perform better when streaming at a lower resolution. If you want your host to change the resolution to something higher than the client, make modifications to the overrides section in the settings.json file
40+
41+
#### Format
42+
```
43+
WidthxHeightxRefresh=WidthxHeightxRefresh
44+
```
45+
46+
The resolution on the left is what triggers the override, and the one on the right is what the host will be set to.
47+
48+
#### Example
49+
To stream at 720p and keep the host at 4k resolution, you would add this line:
50+
```
51+
"overrides": [
52+
// recommended for steam deck users, uncomment to enable, but make sure you have 3840x2400 added on your host!
53+
// sunshine has issues downscaling to smaller resolutions, so it is recommended to stream above native (you will see a significant difference)
54+
// simply uncomment below line once done
55+
//"2560x1440x90=3840x2400x60",
56+
"1280x720x60=3840x2160x60"
57+
]
58+
```

0 commit comments

Comments
 (0)