Skip to content

Instantly share code, notes, and snippets.

@bender-the-greatest
Created January 18, 2021 20:14
Show Gist options
  • Save bender-the-greatest/1b4be1b6d569ae2e268a43863526f646 to your computer and use it in GitHub Desktop.
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.
#!/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