Created
January 18, 2021 20:14
-
-
Save bender-the-greatest/1b4be1b6d569ae2e268a43863526f646 to your computer and use it in GitHub Desktop.
Script to adjust Xinput mouse settings. Designed for using around event-based triggers, such as xbindkeys. Currently only supports adjusting the mouse speed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env pwsh | |
<# | |
.SYNOPSIS | |
Script to adjust Xinput mouse settings. Designed for using around event-based triggers, such as xbindkeys. | |
.DESCRIPTION | |
Changes certain Xinput mouse settings. Currently only supports adjusting the mouse speed. Leans on the `xinput` | |
command to read and apply Xinput configurations. A high-level explanation of which settings are changed for | |
each operation can be found further on in the description. | |
It's recommended to run `pwsh` with the `-NoProfile` switch to minimize the initialization time | |
of PowerShell when running from an external context. -NonInteractive is also recommended to | |
prevent unforseen interactive prompts in triggered automation scenarios. | |
Operations adjusting mouse speed are done by adjusting the `a` and `e` fields of the device's | |
Coordinate Transformation Matrix. More information on this can be found at | |
https://wiki.ubuntu.com/X/InputCoordinateTransformation. | |
.PARAMETER MouseName | |
Name of the mouse device (e.g. what is returned by `xinput list`). | |
.PARAMETER Value | |
Value to use for the selected Operation (see -Operation parameter). If not provided, a value of 1 is used by default. | |
Use caution when passing a negative or zero (0) -Value parameter or you may experience unintended results. For example, | |
resetting or decrementing the mouse speed to a negative value is a valid operation but will cause the mouse to move | |
in reverse. Conversely, incrementing the mouse speed by a negative value will have the same effect as decrementing the | |
mouse speed instead. A value of zero on a given axis will effectively prevent the mouse from moving at all. | |
.PARAMETER Operation | |
Action you want this script to perform. Any operations requiring a value use the -Value parameter. | |
IncrementMouseSpeed: Increase the mouse speed by -Value | |
DecrementMouseSpeed: Decrease the mouse speed by -Value | |
ResetMouseSpeed: Reset the mouse speed to -Value | |
.PARAMETER NoWait | |
Do not wait for other executions of this script to finish, instead exit immediately without making any changes. | |
Exits 0 by default but this can be changed with the -FailNoWait parameter. | |
.PARAMETER FailNoWait | |
Change the behavior of -NoWait to exit with a return code of 1 instead of 0, signalling a failure. | |
No effect if -NoWait isn't used. | |
.EXAMPLE | |
Increase the mouse speed by the default amount | |
Set-XInputMouseProfile.ps1 -Operation IncrementMouseSpeed -MouseName YOUR_MOUSE_NAME | |
.EXAMPLE | |
Decrease the mouse speed by .5 | |
Set-XInputMouseProfile.ps1 -Operation DecrementMouseSpeed -Value .5 -MouseName YOUR_MOUSE_NAME | |
.EXAMPLE | |
Reset the mouse speed | |
Set-XInputMouseProfile.ps1 -Operation ResetMouseSpeed -MouseName YOUR_MOUSE_NAME | |
.EXAMPLE | |
Example use in xbindkeys. This configuration binds pointer speed control to the "Forward/Back" buttons for this mouse model. | |
"pwsh -NoProfile -NonInteractive -File /home/user/Set-XInputMouseProfile.ps1 -Operation IncrementMouseSpeed -Value .5 -MouseName 'Logitech M570'" | |
b:9 | |
"pwsh -NoProfile -NonInteractive -File /home/user/Set-XInputMouseProfile.ps1 -Operation DecrementMouseSpeed -Value .5 -MouseName 'Logitech M570'" | |
b:8 | |
#> | |
[CmdletBinding(DefaultParameterSetName = 'Default', SupportsShouldProcess)] | |
Param( | |
[Parameter(Mandatory)] | |
[string]$MouseName, | |
[decimal]$Value = 1.0, | |
[Parameter(Mandatory)] | |
[ValidateSet('IncrementMouseSpeed', 'DecrementMouseSpeed', 'ResetMouseSpeed')] | |
[string]$Operation, | |
[switch]$NoWait, | |
[switch]$FailNoWait | |
) | |
# Ensure this script only runs one at a time and in the order executed. | |
# The line below works when invoked via the xbindkey config above | |
# but needs to be modified to work if this script is executed via other contexts. | |
while ( ( $procs = ( Get-Process pwsh | Where-Object { $_.CommandLine -contains $MyInvocation.MyCommand.Name } ) ).Count -gt 1 ) { | |
if ( $procs.Id.IndexOf($PID) -eq 0 ) { | |
break | |
} | |
elseif ( $NoWait ) { | |
Write-Warning "Must wait for other executions to complete, but -NoWait was specified. Exiting cleanly, nothing was done." | |
exit 1 | |
} | |
Start-Sleep .1 | |
} | |
$ErrorActionPreference = 'Stop' | |
# Local functions | |
function Invoke-Executable { | |
[CmdletBinding()] | |
Param( | |
[Parameter(Mandatory, Position = 0)] | |
[string]$Command, | |
[Parameter(Position = 1, ValueFromRemainingArguments)] | |
[string[]]$Arguments, | |
[int[]]$ExitCode = 0 | |
) | |
try { | |
Write-Debug "Executing: $Command $(( $Arguments | Foreach-Object { "'$_'" } ) -join ' ')" | |
& $Command @Arguments | |
} | |
catch [System.Management.Automation.CommandNotFoundException] { | |
$message = "Command ${Command} not found. If an argument is mistaken for a named parameter of this function, prefix positional arguments with --. ", | |
"`te.g. ``Invoke-Executable ping -- www.google.com -c 2``" -join [System.Environment]::NewLine | |
throw [System.Management.Automation.RuntimeException]::new([string]$message, $_.Exception) | |
} | |
Write-Debug "Command exited with exit code ${LASTEXITCODE}" | |
if ( $LASTEXITCODE -notin $ExitCode ) { | |
Write-Error "Command failed with exit code ${LASTEXITCODE}. Use -ExitCode to specify expected exit codes." | |
} | |
} | |
# Check prereqs | |
foreach ( $command in , 'xinput' ) { | |
if ( !( Get-Command $command -ErrorAction Ignore ) ) { | |
throw [System.InvalidOperationException]::new("Required command ${command} not found on the PATH") | |
} | |
} | |
# Determine operation to run | |
switch -Regex ( $Operation ) { | |
'MouseSpeed$' { | |
# Mouse speed operations handled here | |
# Get coordinate transformation matrix (as decimal for easier math later) | |
[decimal[]]$ctm = ( Invoke-Executable xinput list-props $MouseName | | |
Select-String "(?<=Coordinate Transformation Matrix[\s\(a-z0-9\)]*:\s+).*" ).Matches.Value -split ',\s' | | |
ForEach-Object { [Convert]::ToDecimal($_) } | |
Write-Debug "Read Coordinate Transformation Matrix: $($ctm -join ', ')" | |
# Calculate new values for adjusting mouse speed | |
# Index 0 is x multiplier | |
# Index 4 is y multiplier | |
if ( $Operation -match '^Increment' ) { | |
$ctm[0] += $Value | |
$ctm[4] += $Value | |
} | |
elseif ( $Operation -match '^Decrement' ) { | |
$ctm[0] -= $Value | |
$ctm[4] -= $Value | |
} | |
elseif ( $Operation -match '^Reset' ) { | |
$ctm[0] = 1 | |
$ctm[4] = 1 | |
} | |
else { | |
throw [System.InvalidOperationException]::new("Coordinate Transformation Matrix not updated for '${MouseName}'. This is due to a programming oversight.") | |
} | |
# Check ctm to be safe | |
foreach ( $val in $ctm ) { | |
if ( !( $val -is [decimal] ) ) { | |
throw [System.InvalidOperationException]::new('One or more values in the updated coordinate transformation matrix are incorrect. This is a coding error.') | |
} | |
} | |
if ( $PSCmdlet.ShouldProcess($MouseName, "Update 'Coordinate Transformation Matrix' to '$($ctm -join ', ')'") ) { | |
Invoke-Executable xinput set-prop $MouseName 'Coordinate Transformation Matrix' @ctm | |
} | |
} | |
default { | |
Write-Error "Operation ${Operation} not supported" | |
break | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment