Repository navigation
Expand file tree
/
Copy pathmouse-mode.ps1
More file actions
200 lines (178 loc) · 11.2 KB
/
Copy pathmouse-mode.ps1
File metadata and controls
200 lines (178 loc) · 11.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
<#
================================================================================
mouse-mode.ps1 - Toggle Windows "Enhance pointer precision" (mouse accel).
No argument -> toggles:
precision ON (good on the desktop) = WORK
precision OFF (raw aim in games) = GAMING
Optional arg to force a mode:
work | gaming (also: bureau | jeu | on | off | 1 | 0)
Shows the same red AMD-style overlay. Standalone: no svcl, no admin needed.
Bind MouseMode.vbs to one Stream Deck key (it just toggles).
================================================================================
#>
param([Parameter(Position = 0)][string]$Mode)
# ============================== CONFIG =========================================
$Corner = 'TopRight' # TopRight | TopLeft | BottomRight | BottomLeft
$GapX = 28
$GapY = 40
$CloseMs = 2550
# ===============================================================================
$LogFile = Join-Path $PSScriptRoot 'mouse-mode.log'
function Write-Log([string]$m) { try { Add-Content -Path $LogFile -Value ("{0} {1}" -f (Get-Date -Format 'HH:mm:ss'), $m) } catch {} }
Add-Type @'
using System;
using System.Runtime.InteropServices;
public static class MouseAccel {
[DllImport("user32.dll", SetLastError=true)]
public static extern bool SystemParametersInfo(uint uiAction, uint uiParam, int[] pvParam, uint fWinIni);
}
'@
# --- read current acceleration state (SPI_GETMOUSE) --------------------------
$cur = New-Object 'int[]' 3
[void][MouseAccel]::SystemParametersInfo(0x0003, 0, $cur, 0) # SPI_GETMOUSE
$accelOn = ($cur[2] -ne 0)
# --- decide target -----------------------------------------------------------
$norm = ($Mode -replace '^[-/]+', '').Trim().ToLower()
switch -Regex ($norm) {
'^(work|works|bureau|on|1)$' { $target = 'work'; break }
'^(gaming|game|jeu|off|0)$' { $target = 'gaming'; break }
default { $target = if ($accelOn) { 'gaming' } else { 'work' }; break } # toggle
}
if ($target -eq 'gaming') {
$vals = [int[]](0, 0, 0) # precision OFF
$title = 'GAMING'; $sub = 'Précision du pointeur désactivée'; $iconName = 'bolt'
} else {
$vals = [int[]](6, 10, 1) # precision ON (Windows defaults)
$title = 'WORK'; $sub = 'Précision du pointeur activée'; $iconName = 'cursor'
}
# --- apply live + persist (SPI_SETMOUSE, UPDATEINIFILE|SENDWININICHANGE) ------
[void][MouseAccel]::SystemParametersInfo(0x0004, 0, $vals, 3)
Write-Log "set $target (precision was $([int]$accelOn))"
# --- icons (24x24) -----------------------------------------------------------
$IconCursor = 'M5 3 L5 19 L9 15 L11.6 21 L14.1 19.9 L11.5 14 L17 14 Z'
$IconBolt = 'M13 2 L4 14 H10 L9 22 L20 9 H13 Z'
$iconData = if ($iconName -eq 'bolt') { $IconBolt } else { $IconCursor }
# --- overlay (red AMD style) -------------------------------------------------
try { Add-Type -AssemblyName PresentationFramework, PresentationCore, WindowsBase | Out-Null }
catch { Write-Log "WPF load: $($_.Exception.Message)"; exit 0 }
$xaml = @'
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
WindowStartupLocation="Manual" WindowStyle="None" AllowsTransparency="True"
Background="Transparent" ShowInTaskbar="False" Topmost="True"
ShowActivated="False" ResizeMode="NoResize" SizeToContent="WidthAndHeight"
UseLayoutRounding="True" SnapsToDevicePixels="True"
TextOptions.TextFormattingMode="Display" TextOptions.TextRenderingMode="ClearType">
<Grid x:Name="Root" Margin="32" Width="340" Height="76" HorizontalAlignment="Left" VerticalAlignment="Top">
<Grid.Clip><RectangleGeometry x:Name="EraseClip" Rect="0,0,340,76"/></Grid.Clip>
<Border x:Name="Card" Width="340" Height="76" CornerRadius="12" BorderThickness="1" HorizontalAlignment="Left" VerticalAlignment="Top">
<Border.RenderTransform><TranslateTransform x:Name="CardT" X="-360"/></Border.RenderTransform>
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1"><GradientStop Color="#FF202024" Offset="0"/><GradientStop Color="#FF141418" Offset="1"/></LinearGradientBrush>
</Border.Background>
<Border.BorderBrush>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1"><GradientStop Color="#FF3A3A40" Offset="0"/><GradientStop Color="#FF202024" Offset="1"/></LinearGradientBrush>
</Border.BorderBrush>
<Border.Effect><DropShadowEffect x:Name="Shadow" Color="#000000" BlurRadius="16" ShadowDepth="6" Direction="270" Opacity="0"/></Border.Effect>
<Grid x:Name="Content" Margin="18,0,14,0">
<Grid.Clip><RectangleGeometry x:Name="ContentClip" Rect="0,0,0,76"/></Grid.Clip>
<Grid.ColumnDefinitions><ColumnDefinition Width="Auto"/><ColumnDefinition Width="*"/></Grid.ColumnDefinitions>
<Viewbox Grid.Column="0" Width="26" Height="26" Margin="0,0,13,0" VerticalAlignment="Center" HorizontalAlignment="Center">
<Canvas Width="24" Height="24"><Path x:Name="IconPath" Fill="#F4F4F6" Data="M0,0"/></Canvas>
</Viewbox>
<StackPanel Grid.Column="1" VerticalAlignment="Center">
<TextBlock x:Name="Title" Text="MODE" FontFamily="Segoe UI" FontWeight="Bold" FontSize="20" Foreground="#FFF5F5F7"/>
<TextBlock x:Name="Sub" Text="" FontFamily="Segoe UI" FontSize="11.5" Foreground="#FFA8A8AF" Margin="0,1,0,0" TextTrimming="CharacterEllipsis" MaxWidth="250"/>
</StackPanel>
</Grid>
</Border>
<Border x:Name="Writer" Width="3" Height="56" CornerRadius="1.5" HorizontalAlignment="Left" VerticalAlignment="Center" Opacity="0">
<Border.RenderTransform><TranslateTransform x:Name="WriterT" X="16"/></Border.RenderTransform>
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1"><GradientStop Color="#FFFF4D45" Offset="0"/><GradientStop Color="#FFED1C24" Offset="1"/></LinearGradientBrush>
</Border.Background>
<Border.Effect><DropShadowEffect Color="#FFED1C24" BlurRadius="10" ShadowDepth="0" Opacity="0.9"/></Border.Effect>
</Border>
<Border x:Name="CovLight" Width="340" Height="76" CornerRadius="12" Background="#FFFF564D" HorizontalAlignment="Left" VerticalAlignment="Top">
<Border.RenderTransform><TranslateTransform x:Name="CovLT" X="360"/></Border.RenderTransform>
</Border>
<Border x:Name="CovMid" Width="340" Height="76" CornerRadius="12" Background="#FFED1C24" HorizontalAlignment="Left" VerticalAlignment="Top">
<Border.RenderTransform><TranslateTransform x:Name="CovMT" X="360"/></Border.RenderTransform>
</Border>
<Border x:Name="CovDark" Width="340" Height="76" CornerRadius="12" Background="#FFA10E13" HorizontalAlignment="Left" VerticalAlignment="Top">
<Border.RenderTransform><TranslateTransform x:Name="CovDT" X="360"/></Border.RenderTransform>
</Border>
</Grid>
<Window.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="CardT" Storyboard.TargetProperty="X" From="-360" To="0" Duration="0:0:0.22">
<DoubleAnimation.EasingFunction><ExponentialEase EasingMode="EaseOut" Exponent="6"/></DoubleAnimation.EasingFunction>
</DoubleAnimation>
<DoubleAnimation Storyboard.TargetName="Shadow" Storyboard.TargetProperty="Opacity" From="0" To="0.45" BeginTime="0:0:0.14" Duration="0:0:0.22"/>
<DoubleAnimation Storyboard.TargetName="Writer" Storyboard.TargetProperty="Opacity" From="0" To="1" BeginTime="0:0:0.18" Duration="0:0:0.06"/>
<DoubleAnimation Storyboard.TargetName="WriterT" Storyboard.TargetProperty="X" From="16" To="322" BeginTime="0:0:0.20" Duration="0:0:0.32">
<DoubleAnimation.EasingFunction><CubicEase EasingMode="EaseInOut"/></DoubleAnimation.EasingFunction>
</DoubleAnimation>
<RectAnimation Storyboard.TargetName="ContentClip" Storyboard.TargetProperty="Rect" From="0,0,0,76" To="0,0,320,76" BeginTime="0:0:0.20" Duration="0:0:0.32">
<RectAnimation.EasingFunction><CubicEase EasingMode="EaseInOut"/></RectAnimation.EasingFunction>
</RectAnimation>
<DoubleAnimation Storyboard.TargetName="Writer" Storyboard.TargetProperty="Opacity" To="0" BeginTime="0:0:0.50" Duration="0:0:0.12"/>
<DoubleAnimation Storyboard.TargetName="CovLT" Storyboard.TargetProperty="X" From="360" To="0" BeginTime="0:0:1.77" Duration="0:0:0.42"/>
<DoubleAnimation Storyboard.TargetName="CovMT" Storyboard.TargetProperty="X" From="360" To="0" BeginTime="0:0:1.855" Duration="0:0:0.42"/>
<DoubleAnimation Storyboard.TargetName="CovDT" Storyboard.TargetProperty="X" From="360" To="0" BeginTime="0:0:1.94" Duration="0:0:0.42"/>
<RectAnimation Storyboard.TargetName="EraseClip" Storyboard.TargetProperty="Rect" From="0,0,340,76" To="340,0,0,76" BeginTime="0:0:2.06" Duration="0:0:0.30">
<RectAnimation.EasingFunction><ExponentialEase EasingMode="EaseIn" Exponent="5"/></RectAnimation.EasingFunction>
</RectAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Window.Triggers>
</Window>
'@
try {
$reader = New-Object System.Xml.XmlNodeReader ([xml]$xaml)
$win = [Windows.Markup.XamlReader]::Load($reader)
} catch { Write-Log "XAML load: $($_.Exception.Message)"; exit 0 }
$win.FindName('Title').Text = $title
$win.FindName('Sub').Text = $sub
$win.FindName('IconPath').Data = [Windows.Media.Geometry]::Parse($iconData)
# position (work-area based -> any resolution; clamped on-screen)
$wa = [System.Windows.SystemParameters]::WorkArea
$m = 32; $cw = 340; $ch = 76
switch ($Corner) {
'TopLeft' { $L = $wa.Left + $GapX - $m; $T = $wa.Top + $GapY - $m }
'BottomLeft' { $L = $wa.Left + $GapX - $m; $T = $wa.Bottom - $GapY - $m - $ch }
'BottomRight' { $L = $wa.Right - $GapX - $m - $cw; $T = $wa.Bottom - $GapY - $m - $ch }
default { $L = $wa.Right - $GapX - $m - $cw; $T = $wa.Top + $GapY - $m }
}
$winW = $cw + 2*$m; $winH = $ch + 2*$m
if ($L -lt $wa.Left) { $L = $wa.Left }
if ($T -lt $wa.Top) { $T = $wa.Top }
if (($L + $winW) -gt $wa.Right) { $L = $wa.Right - $winW }
if (($T + $winH) -gt $wa.Bottom) { $T = $wa.Bottom - $winH }
$win.Left = $L; $win.Top = $T
$win.Add_Loaded({
try {
$sig = @'
using System;
using System.Runtime.InteropServices;
public static class WinEx2 {
[DllImport("user32.dll")] public static extern int GetWindowLong(IntPtr h, int i);
[DllImport("user32.dll")] public static extern int SetWindowLong(IntPtr h, int i, int v);
}
'@
if (-not ('WinEx2' -as [type])) { Add-Type -TypeDefinition $sig }
$h = (New-Object System.Windows.Interop.WindowInteropHelper($win)).Handle
if ($h -ne [IntPtr]::Zero) {
$ex = [WinEx2]::GetWindowLong($h, -20)
[WinEx2]::SetWindowLong($h, -20, $ex -bor 0x80 -bor 0x08000000 -bor 0x20) | Out-Null
}
} catch { Write-Log "exstyle: $($_.Exception.Message)" }
})
$timer = New-Object System.Windows.Threading.DispatcherTimer
$timer.Interval = [TimeSpan]::FromMilliseconds($CloseMs)
$timer.Add_Tick({ $timer.Stop(); $win.Close() })
$timer.Start()
try { [void]$win.ShowDialog() } catch { Write-Log "ShowDialog: $($_.Exception.Message)" }