PSMouseJiggler.psm1
|
# # PSMouseJiggler Module # A PowerShell module to simulate mouse movements and prevent system idle # # Check for required assemblies Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing # Global variable to track jiggling state $script:JigglingJob = $null $script:JigglingActive = $false function Sync-PSMJState { if (-not $script:JigglingActive) { return } if ($null -eq $script:JigglingJob) { $script:JigglingActive = $false return } $jobState = [string]$script:JigglingJob.State if ($jobState -in @('Completed', 'Failed', 'Stopped')) { if ($script:JigglingJob -is [System.Management.Automation.Job]) { Remove-Job -Job $script:JigglingJob -Force -ErrorAction SilentlyContinue } $script:JigglingJob = $null $script:JigglingActive = $false } } #region Core Mouse Jiggling Functions <# .SYNOPSIS Starts the PSMouseJiggler to simulate mouse movements and/or keep the system awake. .DESCRIPTION Starts mouse jiggling with a specified interval and movement pattern, or combines multiple keep-awake methods (software/hardware mouse, keyboard, system API) to prevent the system from going idle. .PARAMETER Interval Time in milliseconds between actions. Default is 1000ms. .PARAMETER MovementPattern The pattern for mouse movement when using the default 'MouseSoftware' method. Valid values: 'Random', 'Horizontal', 'Vertical', 'Circular'. Default is 'Random'. .PARAMETER Methods Array of keep-awake methods to use: 'MouseSoftware', 'MouseHardware', 'Keyboard', 'SystemAPI', or 'All'. Default is 'MouseSoftware'. .PARAMETER Strategy An optional recommended method combination that overrides -Methods: 'Manual' (default, uses -Methods as-is), 'Adaptive', 'LowImpact', 'Compatibility', 'AppKeepAlive'. .PARAMETER Duration Duration in seconds to run the jiggler. If not specified, runs indefinitely until stopped. .PARAMETER Incognito When enabled, clears the console after starting to maintain privacy/discretion. .EXAMPLE Start-PSMouseJiggler Starts mouse jiggling with default settings. .EXAMPLE Start-PSMouseJiggler -Interval 2000 -MovementPattern 'Circular' -Duration 300 Starts mouse jiggling every 2 seconds using circular pattern for 5 minutes. .EXAMPLE Start-PSMouseJiggler -Methods @('MouseHardware', 'SystemAPI') -Interval 30000 Combines hardware mouse input and the system power API for strict security policies. .EXAMPLE Start-PSMouseJiggler -Strategy AppKeepAlive Uses the SystemAPI + Keyboard combo to keep some Windows apps active without moving the mouse. #> function Start-PSMouseJiggler { [CmdletBinding()] param ( [Parameter()] [ValidateRange(1, 2147483647)] [int]$Interval = 1000, [Parameter()] [ValidateSet('Random', 'Horizontal', 'Vertical', 'Circular')] [string]$MovementPattern = 'Random', [Parameter()] [ValidateSet('MouseSoftware', 'MouseHardware', 'Keyboard', 'SystemAPI', 'All')] [string[]]$Methods = @('MouseSoftware'), [Parameter()] [ValidateSet('Manual', 'Adaptive', 'LowImpact', 'Compatibility', 'AppKeepAlive')] [string]$Strategy = 'Manual', [Parameter()] [ValidateRange(0, 2147483647)] [int]$Duration = 0, [Parameter()] [switch]$Incognito ) Sync-PSMJState if ($script:JigglingActive) { if ($Incognito) { Clear-Host } else { Write-Warning "PSMouseJiggler is already running. Use Stop-PSMouseJiggler to stop it first." } return } if ($Strategy -ne 'Manual') { $Methods = Get-PSMJRecommendedMethods -Strategy $Strategy } elseif ($Methods -contains 'All') { $Methods = @('MouseSoftware', 'MouseHardware', 'Keyboard', 'SystemAPI') } # Preserve the original single-mouse, pattern-based behavior when only the default method is requested $useSinglePatternJob = ($Methods.Count -eq 1 -and $Methods[0] -eq 'MouseSoftware') Write-Host "Starting PSMouseJiggler with methods: $($Methods -join ', '), interval: $Interval ms" -ForegroundColor Green $script:JigglingActive = $true $startTime = Get-Date if ($Methods -contains 'SystemAPI') { Prevent-SystemIdle } if ($useSinglePatternJob) { $script:JigglingJob = Start-Job -ScriptBlock { param($Interval, $MovementPattern, $Duration, $StartTime) # Use raw Win32 cursor calls; System.Windows.Forms.Cursor is unreliable without a UI message pump in a background job Add-Type -TypeDefinition @" using System; using System.Runtime.InteropServices; public static class CursorNative { [StructLayout(LayoutKind.Sequential)] public struct POINT { public int X; public int Y; } [DllImport("user32.dll")] public static extern bool GetCursorPos(out POINT lpPoint); [DllImport("user32.dll")] public static extern bool SetCursorPos(int X, int Y); } "@ $endTime = if ($Duration -gt 0) { $StartTime.AddSeconds($Duration) } else { [DateTime]::MaxValue } while ((Get-Date) -lt $endTime) { $currentPos = New-Object CursorNative+POINT [CursorNative]::GetCursorPos([ref]$currentPos) | Out-Null # Determine movement pattern switch ($MovementPattern) { 'Random' { $xOffset = Get-Random -Minimum -10 -Maximum 11 $yOffset = Get-Random -Minimum -10 -Maximum 11 } 'Horizontal' { $xOffset = if ((Get-Random -Minimum 0 -Maximum 2) -eq 0) { -5 } else { 5 } $yOffset = 0 } 'Vertical' { $xOffset = 0 $yOffset = if ((Get-Random -Minimum 0 -Maximum 2) -eq 0) { -5 } else { 5 } } 'Circular' { $angle = (Get-Date).Millisecond / 1000 * 2 * [Math]::PI $xOffset = [Math]::Round([Math]::Sin($angle) * 10) $yOffset = [Math]::Round([Math]::Cos($angle) * 10) } default { $xOffset = Get-Random -Minimum -5 -Maximum 6 $yOffset = Get-Random -Minimum -5 -Maximum 6 } } # Move the mouse [CursorNative]::SetCursorPos($currentPos.X + $xOffset, $currentPos.Y + $yOffset) | Out-Null # Wait for the specified interval Start-Sleep -Milliseconds $Interval } } -ArgumentList $Interval, $MovementPattern, $Duration, $startTime } else { $script:JigglingJob = Start-Job -ScriptBlock { param($Interval, $Methods, $Duration, $StartTime) # Import required assemblies Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing # Define required P/Invoke structures and methods Add-Type -TypeDefinition @" using System; using System.Runtime.InteropServices; public static class DisplayState { [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern uint SetThreadExecutionState(uint esFlags); public const uint ES_CONTINUOUS = 0x80000000; public const uint ES_SYSTEM_REQUIRED = 0x00000001; public const uint ES_DISPLAY_REQUIRED = 0x00000002; } public static class CursorNative { [StructLayout(LayoutKind.Sequential)] public struct POINT { public int X; public int Y; } [DllImport("user32.dll")] public static extern bool GetCursorPos(out POINT lpPoint); [DllImport("user32.dll")] public static extern bool SetCursorPos(int X, int Y); } public static class MouseSimulator { [StructLayout(LayoutKind.Sequential)] public struct MOUSEINPUT { public int dx; public int dy; public uint mouseData; public uint dwFlags; public uint time; public IntPtr dwExtraInfo; } [StructLayout(LayoutKind.Sequential)] public struct INPUT { public uint type; public MOUSEINPUT mi; } [DllImport("user32.dll", SetLastError = true)] public static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize); public const int INPUT_MOUSE = 0; public const int MOUSEEVENTF_MOVE = 0x0001; } "@ $endTime = if ($Duration -gt 0) { $StartTime.AddSeconds($Duration) } else { [DateTime]::MaxValue } while ((Get-Date) -lt $endTime) { # Randomly select a method from the provided methods $method = $Methods | Get-Random switch ($method) { 'MouseSoftware' { # Move mouse using software method; raw Win32 calls avoid Windows.Forms.Cursor's UI-thread requirement $currentPos = New-Object CursorNative+POINT [CursorNative]::GetCursorPos([ref]$currentPos) | Out-Null $xOffset = Get-Random -Minimum -10 -Maximum 11 $yOffset = Get-Random -Minimum -10 -Maximum 11 [CursorNative]::SetCursorPos($currentPos.X + $xOffset, $currentPos.Y + $yOffset) | Out-Null # Move back to original position after a short delay to minimize disruption Start-Sleep -Milliseconds 100 [CursorNative]::SetCursorPos($currentPos.X, $currentPos.Y) | Out-Null } 'MouseHardware' { # Use hardware-level mouse movement $mouseInputStructure = New-Object MouseSimulator+INPUT $mouseInputStructure.type = [MouseSimulator]::INPUT_MOUSE $mouseInputStructure.mi.dx = Get-Random -Minimum -5 -Maximum 6 $mouseInputStructure.mi.dy = Get-Random -Minimum -5 -Maximum 6 $mouseInputStructure.mi.dwFlags = [MouseSimulator]::MOUSEEVENTF_MOVE $mouseInputStructure.mi.time = 0 $mouseInputStructure.mi.dwExtraInfo = [IntPtr]::Zero $inputArray = @($mouseInputStructure) [MouseSimulator]::SendInput(1, $inputArray, [System.Runtime.InteropServices.Marshal]::SizeOf([type][MouseSimulator+INPUT])) | Out-Null } 'Keyboard' { # Press a non-disruptive key (F15 is rarely used) [System.Windows.Forms.SendKeys]::SendWait("{F15}") } 'SystemAPI' { # Directly tell Windows to stay awake [DisplayState]::SetThreadExecutionState( [DisplayState]::ES_CONTINUOUS -bor [DisplayState]::ES_SYSTEM_REQUIRED -bor [DisplayState]::ES_DISPLAY_REQUIRED) } } # Wait for the specified interval Start-Sleep -Milliseconds $Interval } # Reset execution state if we used the API if ($Methods -contains 'SystemAPI') { [DisplayState]::SetThreadExecutionState([DisplayState]::ES_CONTINUOUS) } } -ArgumentList $Interval, $Methods, $Duration, $startTime } if ($Duration -gt 0) { Write-Host "PSMouseJiggler will run for $Duration seconds" -ForegroundColor Yellow } else { Write-Host "PSMouseJiggler is running indefinitely. Use Stop-PSMouseJiggler to stop." -ForegroundColor Yellow } # Clear console if incognito mode is enabled if ($Incognito) { Clear-Host } } <# .SYNOPSIS Stops the PSMouseJiggler. .DESCRIPTION Stops any running mouse jiggling job. .EXAMPLE Stop-PSMouseJiggler Stops the currently running mouse jiggler. #> function Stop-PSMouseJiggler { [CmdletBinding()] param() Sync-PSMJState if (-not $script:JigglingActive) { Write-Warning "PSMouseJiggler is not currently running." return } if ($script:JigglingJob) { # Add type checking to handle both real jobs and mock objects used in testing if ($script:JigglingJob -is [System.Management.Automation.Job]) { Stop-Job -Job $script:JigglingJob -ErrorAction SilentlyContinue Remove-Job -Job $script:JigglingJob -ErrorAction SilentlyContinue } else { Write-Verbose "Stopping non-Job object (likely a test mock)" } $script:JigglingJob = $null } $script:JigglingActive = $false Write-Host "PSMouseJiggler stopped." -ForegroundColor Green } <# .SYNOPSIS Calculates a new mouse position based on current position and pattern. .DESCRIPTION Internal function to determine new mouse coordinates based on movement pattern. .PARAMETER CurrentPosition The current mouse position as a System.Drawing.Point. .PARAMETER Pattern The movement pattern to use. .EXAMPLE $newPos = Get-NewMousePosition -CurrentPosition $currentPos -Pattern 'Random' #> function Get-NewMousePosition { [CmdletBinding()] param ( [Parameter(Mandatory)] [System.Drawing.Point]$CurrentPosition, [Parameter(Mandatory)] [string]$Pattern ) switch ($Pattern) { 'Random' { $xOffset = Get-Random -Minimum -10 -Maximum 11 $yOffset = Get-Random -Minimum -10 -Maximum 11 return New-Object System.Drawing.Point($CurrentPosition.X + $xOffset, $CurrentPosition.Y + $yOffset) } 'Horizontal' { $xOffset = if ((Get-Random -Minimum 0 -Maximum 2) -eq 0) { -5 } else { 5 } return New-Object System.Drawing.Point($CurrentPosition.X + $xOffset, $CurrentPosition.Y) } 'Vertical' { $yOffset = if ((Get-Random -Minimum 0 -Maximum 2) -eq 0) { -5 } else { 5 } return New-Object System.Drawing.Point($CurrentPosition.X, $CurrentPosition.Y + $yOffset) } 'Circular' { $angle = (Get-Date).Millisecond / 1000 * 2 * [Math]::PI $xOffset = [Math]::Round([Math]::Sin($angle) * 10) $yOffset = [Math]::Round([Math]::Cos($angle) * 10) return New-Object System.Drawing.Point($CurrentPosition.X + $xOffset, $CurrentPosition.Y + $yOffset) } default { $xOffset = Get-Random -Minimum -5 -Maximum 6 $yOffset = Get-Random -Minimum -5 -Maximum 6 return New-Object System.Drawing.Point($CurrentPosition.X + $xOffset, $CurrentPosition.Y + $yOffset) } } } #endregion #region GUI Functions <# .SYNOPSIS Shows the PSMouseJiggler GUI interface with tabbed controls. .DESCRIPTION Displays a modern graphical user interface with three main tabs: - Basic: Simple mouse jiggling with movement patterns and input methods - Advanced: Multi-method keep-awake with configurable techniques - Quick Launch: Five pre-configured profiles for common scenarios The GUI provides comprehensive controls including incognito mode, duration settings, and detailed help information about each feature. .EXAMPLE Show-PSMouseJigglerGUI Opens the GUI interface with all tabs available. .EXAMPLE Show-PSMouseJigglerGUI # Use the Quick Launch tab for one-click start with pre-configured profiles: # - [Mouse] Basic Discrete # - [Lock] Maximum Security # - [Key] Keyboard Only # - [API] System API Only # - [MAX] All Methods #> function Show-PSMouseJigglerGUI { [CmdletBinding()] param() # Create the main form (rememeber to update version number in the title if you change it!) $form = New-Object System.Windows.Forms.Form $form.Text = "PSMouseJiggler v2.2.0" $form.Size = New-Object System.Drawing.Size(600, 550) $form.StartPosition = "CenterScreen" $form.FormBorderStyle = "FixedDialog" $form.MaximizeBox = $false $form.BackColor = [System.Drawing.Color]::FromArgb(240, 240, 240) # Create TabControl $tabControl = New-Object System.Windows.Forms.TabControl $tabControl.Location = New-Object System.Drawing.Point(10, 10) $tabControl.Size = New-Object System.Drawing.Size(560, 420) $form.Controls.Add($tabControl) #region Basic Tab $basicTab = New-Object System.Windows.Forms.TabPage $basicTab.Text = "Basic Mode" $basicTab.BackColor = [System.Drawing.Color]::White $tabControl.Controls.Add($basicTab) # Status Panel $statusGroupBox = New-Object System.Windows.Forms.GroupBox $statusGroupBox.Text = "Current Status" $statusGroupBox.Location = New-Object System.Drawing.Point(20, 20) $statusGroupBox.Size = New-Object System.Drawing.Size(500, 70) $basicTab.Controls.Add($statusGroupBox) $statusLabel = New-Object System.Windows.Forms.Label $statusLabel.Text = "Status: Stopped" $statusLabel.Location = New-Object System.Drawing.Point(15, 25) $statusLabel.Size = New-Object System.Drawing.Size(470, 20) $statusLabel.Font = New-Object System.Drawing.Font("Segoe UI", 10, [System.Drawing.FontStyle]::Bold) $statusLabel.ForeColor = [System.Drawing.Color]::DarkRed $statusGroupBox.Controls.Add($statusLabel) $statusDetailsLabel = New-Object System.Windows.Forms.Label $statusDetailsLabel.Text = "Ready to start mouse jiggling" $statusDetailsLabel.Location = New-Object System.Drawing.Point(15, 45) $statusDetailsLabel.Size = New-Object System.Drawing.Size(470, 20) $statusDetailsLabel.Font = New-Object System.Drawing.Font("Segoe UI", 8) $statusDetailsLabel.ForeColor = [System.Drawing.Color]::Gray $statusGroupBox.Controls.Add($statusDetailsLabel) # Settings GroupBox $settingsGroupBox = New-Object System.Windows.Forms.GroupBox $settingsGroupBox.Text = "Basic Settings" $settingsGroupBox.Location = New-Object System.Drawing.Point(20, 100) $settingsGroupBox.Size = New-Object System.Drawing.Size(500, 230) $basicTab.Controls.Add($settingsGroupBox) # Movement Pattern $patternLabel = New-Object System.Windows.Forms.Label $patternLabel.Text = "Movement Pattern:" $patternLabel.Location = New-Object System.Drawing.Point(15, 30) $patternLabel.Size = New-Object System.Drawing.Size(120, 20) $settingsGroupBox.Controls.Add($patternLabel) $patternComboBox = New-Object System.Windows.Forms.ComboBox $patternComboBox.Location = New-Object System.Drawing.Point(150, 28) $patternComboBox.Size = New-Object System.Drawing.Size(150, 20) $patternComboBox.DropDownStyle = "DropDownList" $patternComboBox.Items.AddRange(@("Random", "Horizontal", "Vertical", "Circular")) $patternComboBox.SelectedIndex = 0 $settingsGroupBox.Controls.Add($patternComboBox) $patternDescLabel = New-Object System.Windows.Forms.Label $patternDescLabel.Text = "Simulates natural mouse movements" $patternDescLabel.Location = New-Object System.Drawing.Point(310, 30) $patternDescLabel.Size = New-Object System.Drawing.Size(180, 20) $patternDescLabel.Font = New-Object System.Drawing.Font("Segoe UI", 7) $patternDescLabel.ForeColor = [System.Drawing.Color]::Gray $settingsGroupBox.Controls.Add($patternDescLabel) # Update description based on pattern selection $patternComboBox.Add_SelectedIndexChanged({ switch ($patternComboBox.SelectedItem.ToString()) { 'Random' { $patternDescLabel.Text = "Random movements in all directions" } 'Horizontal' { $patternDescLabel.Text = "Left-right movements only" } 'Vertical' { $patternDescLabel.Text = "Up-down movements only" } 'Circular' { $patternDescLabel.Text = "Smooth circular motion pattern" } } }) # Interval $intervalLabel = New-Object System.Windows.Forms.Label $intervalLabel.Text = "Interval (milliseconds):" $intervalLabel.Location = New-Object System.Drawing.Point(15, 65) $intervalLabel.Size = New-Object System.Drawing.Size(130, 20) $settingsGroupBox.Controls.Add($intervalLabel) $intervalTextBox = New-Object System.Windows.Forms.TextBox $intervalTextBox.Text = "1000" $intervalTextBox.Location = New-Object System.Drawing.Point(150, 63) $intervalTextBox.Size = New-Object System.Drawing.Size(80, 20) $settingsGroupBox.Controls.Add($intervalTextBox) $intervalDescLabel = New-Object System.Windows.Forms.Label $intervalDescLabel.Text = "Time between movements (1000 = 1 second)" $intervalDescLabel.Location = New-Object System.Drawing.Point(240, 65) $intervalDescLabel.Size = New-Object System.Drawing.Size(250, 20) $intervalDescLabel.Font = New-Object System.Drawing.Font("Segoe UI", 7) $intervalDescLabel.ForeColor = [System.Drawing.Color]::Gray $settingsGroupBox.Controls.Add($intervalDescLabel) # Duration $durationLabel = New-Object System.Windows.Forms.Label $durationLabel.Text = "Duration (seconds):" $durationLabel.Location = New-Object System.Drawing.Point(15, 100) $durationLabel.Size = New-Object System.Drawing.Size(120, 20) $settingsGroupBox.Controls.Add($durationLabel) $durationTextBox = New-Object System.Windows.Forms.TextBox $durationTextBox.Text = "0" $durationTextBox.Location = New-Object System.Drawing.Point(150, 98) $durationTextBox.Size = New-Object System.Drawing.Size(80, 20) $settingsGroupBox.Controls.Add($durationTextBox) $durationDescLabel = New-Object System.Windows.Forms.Label $durationDescLabel.Text = "How long to run (0 = run until stopped)" $durationDescLabel.Location = New-Object System.Drawing.Point(240, 100) $durationDescLabel.Size = New-Object System.Drawing.Size(250, 20) $durationDescLabel.Font = New-Object System.Drawing.Font("Segoe UI", 7) $durationDescLabel.ForeColor = [System.Drawing.Color]::Gray $settingsGroupBox.Controls.Add($durationDescLabel) # Mouse Movement Type (linking basic to advanced mouse methods) $mouseTypeLabel = New-Object System.Windows.Forms.Label $mouseTypeLabel.Text = "Mouse Input Method:" $mouseTypeLabel.Location = New-Object System.Drawing.Point(15, 135) $mouseTypeLabel.Size = New-Object System.Drawing.Size(130, 20) $settingsGroupBox.Controls.Add($mouseTypeLabel) $mouseTypeComboBox = New-Object System.Windows.Forms.ComboBox $mouseTypeComboBox.Location = New-Object System.Drawing.Point(150, 133) $mouseTypeComboBox.Size = New-Object System.Drawing.Size(150, 20) $mouseTypeComboBox.DropDownStyle = "DropDownList" $mouseTypeComboBox.Items.AddRange(@("Software (Standard)", "Hardware (Low-level)", "Both (Redundant)")) $mouseTypeComboBox.SelectedIndex = 0 $settingsGroupBox.Controls.Add($mouseTypeComboBox) $mouseTypeDescLabel = New-Object System.Windows.Forms.Label $mouseTypeDescLabel.Text = "Standard method for most systems" $mouseTypeDescLabel.Location = New-Object System.Drawing.Point(310, 135) $mouseTypeDescLabel.Size = New-Object System.Drawing.Size(180, 20) $mouseTypeDescLabel.Font = New-Object System.Drawing.Font("Segoe UI", 7) $mouseTypeDescLabel.ForeColor = [System.Drawing.Color]::Gray $settingsGroupBox.Controls.Add($mouseTypeDescLabel) $mouseTypeComboBox.Add_SelectedIndexChanged({ switch ($mouseTypeComboBox.SelectedIndex) { 0 { $mouseTypeDescLabel.Text = "Standard method for most systems" } 1 { $mouseTypeDescLabel.Text = "Better for strict security policies" } 2 { $mouseTypeDescLabel.Text = "Maximum reliability (both methods)" } } }) # Strategy (overrides Mouse Input Method with a recommended method combination when not Manual) $strategyLabel = New-Object System.Windows.Forms.Label $strategyLabel.Text = "Strategy:" $strategyLabel.Location = New-Object System.Drawing.Point(15, 165) $strategyLabel.Size = New-Object System.Drawing.Size(130, 20) $settingsGroupBox.Controls.Add($strategyLabel) $strategyComboBox = New-Object System.Windows.Forms.ComboBox $strategyComboBox.Location = New-Object System.Drawing.Point(150, 163) $strategyComboBox.Size = New-Object System.Drawing.Size(150, 20) $strategyComboBox.DropDownStyle = "DropDownList" $strategyComboBox.Items.AddRange(@("Manual", "Adaptive", "LowImpact", "Compatibility", "AppKeepAlive")) $strategyComboBox.SelectedIndex = 0 $settingsGroupBox.Controls.Add($strategyComboBox) $strategyDescLabel = New-Object System.Windows.Forms.Label $strategyDescLabel.Text = "Manual: use Mouse Input Method below" $strategyDescLabel.Location = New-Object System.Drawing.Point(310, 165) $strategyDescLabel.Size = New-Object System.Drawing.Size(180, 20) $strategyDescLabel.Font = New-Object System.Drawing.Font("Segoe UI", 7) $strategyDescLabel.ForeColor = [System.Drawing.Color]::Gray $settingsGroupBox.Controls.Add($strategyDescLabel) $strategyComboBox.Add_SelectedIndexChanged({ switch ($strategyComboBox.SelectedItem.ToString()) { 'Manual' { $strategyDescLabel.Text = "Manual: use Mouse Input Method below" } 'Adaptive' { $strategyDescLabel.Text = "SystemAPI + Hardware mouse (Windows)" } 'LowImpact' { $strategyDescLabel.Text = "SystemAPI only, no mouse movement" } 'Compatibility' { $strategyDescLabel.Text = "Software mouse only, most compatible" } 'AppKeepAlive' { $strategyDescLabel.Text = "SystemAPI + Keyboard, keeps apps active" } } # A strategy other than Manual overrides Mouse Input Method, so grey it out $mouseTypeComboBox.Enabled = ($strategyComboBox.SelectedItem.ToString() -eq 'Manual') }) # Incognito mode checkbox $incognitoCheckbox = New-Object System.Windows.Forms.CheckBox $incognitoCheckbox.Text = "Incognito Mode (minimize window & clear console)" $incognitoCheckbox.Location = New-Object System.Drawing.Point(15, 195) $incognitoCheckbox.Size = New-Object System.Drawing.Size(350, 20) $settingsGroupBox.Controls.Add($incognitoCheckbox) #endregion #region Advanced Tab $advancedTab = New-Object System.Windows.Forms.TabPage $advancedTab.Text = "Advanced Mode" $advancedTab.BackColor = [System.Drawing.Color]::White $tabControl.Controls.Add($advancedTab) # Advanced Info Label $advancedInfoLabel = New-Object System.Windows.Forms.Label $advancedInfoLabel.Text = "Advanced mode combines multiple methods to prevent sleep/screensaver activation" $advancedInfoLabel.Location = New-Object System.Drawing.Point(20, 20) $advancedInfoLabel.Size = New-Object System.Drawing.Size(500, 30) $advancedInfoLabel.Font = New-Object System.Drawing.Font("Segoe UI", 9) $advancedInfoLabel.ForeColor = [System.Drawing.Color]::FromArgb(0, 102, 204) $advancedTab.Controls.Add($advancedInfoLabel) # Method Selection GroupBox $methodsGroupBox = New-Object System.Windows.Forms.GroupBox $methodsGroupBox.Text = "Keep-Awake Methods (Select one or more)" $methodsGroupBox.Location = New-Object System.Drawing.Point(20, 60) $methodsGroupBox.Size = New-Object System.Drawing.Size(500, 180) $advancedTab.Controls.Add($methodsGroupBox) # Mouse Software Checkbox $mouseSoftwareCheckbox = New-Object System.Windows.Forms.CheckBox $mouseSoftwareCheckbox.Text = "Software Mouse Movements" $mouseSoftwareCheckbox.Location = New-Object System.Drawing.Point(15, 25) $mouseSoftwareCheckbox.Size = New-Object System.Drawing.Size(220, 20) $mouseSoftwareCheckbox.Checked = $true $methodsGroupBox.Controls.Add($mouseSoftwareCheckbox) $mouseSoftwareDesc = New-Object System.Windows.Forms.Label $mouseSoftwareDesc.Text = "Standard cursor position changes" $mouseSoftwareDesc.Location = New-Object System.Drawing.Point(240, 25) $mouseSoftwareDesc.Size = New-Object System.Drawing.Size(250, 20) $mouseSoftwareDesc.Font = New-Object System.Drawing.Font("Segoe UI", 7) $mouseSoftwareDesc.ForeColor = [System.Drawing.Color]::Gray $methodsGroupBox.Controls.Add($mouseSoftwareDesc) # Mouse Hardware Checkbox $mouseHardwareCheckbox = New-Object System.Windows.Forms.CheckBox $mouseHardwareCheckbox.Text = "Hardware Mouse Input" $mouseHardwareCheckbox.Location = New-Object System.Drawing.Point(15, 55) $mouseHardwareCheckbox.Size = New-Object System.Drawing.Size(220, 20) $mouseHardwareCheckbox.Checked = $true $methodsGroupBox.Controls.Add($mouseHardwareCheckbox) $mouseHardwareDesc = New-Object System.Windows.Forms.Label $mouseHardwareDesc.Text = "Low-level input simulation (SendInput API)" $mouseHardwareDesc.Location = New-Object System.Drawing.Point(240, 55) $mouseHardwareDesc.Size = New-Object System.Drawing.Size(250, 20) $mouseHardwareDesc.Font = New-Object System.Drawing.Font("Segoe UI", 7) $mouseHardwareDesc.ForeColor = [System.Drawing.Color]::Gray $methodsGroupBox.Controls.Add($mouseHardwareDesc) # Keyboard Checkbox $keyboardCheckbox = New-Object System.Windows.Forms.CheckBox $keyboardCheckbox.Text = "Keyboard Input (F15 key)" $keyboardCheckbox.Location = New-Object System.Drawing.Point(15, 85) $keyboardCheckbox.Size = New-Object System.Drawing.Size(220, 20) $keyboardCheckbox.Checked = $true $methodsGroupBox.Controls.Add($keyboardCheckbox) $keyboardDesc = New-Object System.Windows.Forms.Label $keyboardDesc.Text = "Sends non-disruptive key press" $keyboardDesc.Location = New-Object System.Drawing.Point(240, 85) $keyboardDesc.Size = New-Object System.Drawing.Size(250, 20) $keyboardDesc.Font = New-Object System.Drawing.Font("Segoe UI", 7) $keyboardDesc.ForeColor = [System.Drawing.Color]::Gray $methodsGroupBox.Controls.Add($keyboardDesc) # System API Checkbox $systemApiCheckbox = New-Object System.Windows.Forms.CheckBox $systemApiCheckbox.Text = "System API (SetThreadExecutionState)" $systemApiCheckbox.Location = New-Object System.Drawing.Point(15, 115) $systemApiCheckbox.Size = New-Object System.Drawing.Size(250, 20) $systemApiCheckbox.Checked = $true $methodsGroupBox.Controls.Add($systemApiCheckbox) $systemApiDesc = New-Object System.Windows.Forms.Label $systemApiDesc.Text = "Directly prevents Windows power management" $systemApiDesc.Location = New-Object System.Drawing.Point(270, 115) $systemApiDesc.Size = New-Object System.Drawing.Size(220, 20) $systemApiDesc.Font = New-Object System.Drawing.Font("Segoe UI", 7) $systemApiDesc.ForeColor = [System.Drawing.Color]::Gray $methodsGroupBox.Controls.Add($systemApiDesc) # Advanced Interval $advIntervalLabel = New-Object System.Windows.Forms.Label $advIntervalLabel.Text = "Interval (milliseconds):" $advIntervalLabel.Location = New-Object System.Drawing.Point(15, 145) $advIntervalLabel.Size = New-Object System.Drawing.Size(130, 20) $methodsGroupBox.Controls.Add($advIntervalLabel) $advIntervalTextBox = New-Object System.Windows.Forms.TextBox $advIntervalTextBox.Text = "30000" $advIntervalTextBox.Location = New-Object System.Drawing.Point(150, 143) $advIntervalTextBox.Size = New-Object System.Drawing.Size(80, 20) $methodsGroupBox.Controls.Add($advIntervalTextBox) $advIntervalDesc = New-Object System.Windows.Forms.Label $advIntervalDesc.Text = "30000 = 30 seconds (recommended for keep-awake)" $advIntervalDesc.Location = New-Object System.Drawing.Point(240, 145) $advIntervalDesc.Size = New-Object System.Drawing.Size(250, 20) $advIntervalDesc.Font = New-Object System.Drawing.Font("Segoe UI", 7) $advIntervalDesc.ForeColor = [System.Drawing.Color]::Gray $methodsGroupBox.Controls.Add($advIntervalDesc) # Advanced Duration $advDurationGroupBox = New-Object System.Windows.Forms.GroupBox $advDurationGroupBox.Text = "Duration Settings" $advDurationGroupBox.Location = New-Object System.Drawing.Point(20, 250) $advDurationGroupBox.Size = New-Object System.Drawing.Size(500, 80) $advancedTab.Controls.Add($advDurationGroupBox) $advDurationLabel = New-Object System.Windows.Forms.Label $advDurationLabel.Text = "Duration (seconds):" $advDurationLabel.Location = New-Object System.Drawing.Point(15, 25) $advDurationLabel.Size = New-Object System.Drawing.Size(120, 20) $advDurationGroupBox.Controls.Add($advDurationLabel) $advDurationTextBox = New-Object System.Windows.Forms.TextBox $advDurationTextBox.Text = "0" $advDurationTextBox.Location = New-Object System.Drawing.Point(150, 23) $advDurationTextBox.Size = New-Object System.Drawing.Size(80, 20) $advDurationGroupBox.Controls.Add($advDurationTextBox) $advDurationDesc = New-Object System.Windows.Forms.Label $advDurationDesc.Text = "0 = run until manually stopped" $advDurationDesc.Location = New-Object System.Drawing.Point(240, 25) $advDurationDesc.Size = New-Object System.Drawing.Size(250, 20) $advDurationDesc.Font = New-Object System.Drawing.Font("Segoe UI", 7) $advDurationDesc.ForeColor = [System.Drawing.Color]::Gray $advDurationGroupBox.Controls.Add($advDurationDesc) # Advanced Incognito $advIncognitoCheckbox = New-Object System.Windows.Forms.CheckBox $advIncognitoCheckbox.Text = "Incognito Mode" $advIncognitoCheckbox.Location = New-Object System.Drawing.Point(15, 50) $advIncognitoCheckbox.Size = New-Object System.Drawing.Size(200, 20) $advDurationGroupBox.Controls.Add($advIncognitoCheckbox) #endregion #region Favorites Tab $favoritesTab = New-Object System.Windows.Forms.TabPage $favoritesTab.Text = "Quick Launch" $favoritesTab.BackColor = [System.Drawing.Color]::White $tabControl.Controls.Add($favoritesTab) # Favorites Info $favInfoLabel = New-Object System.Windows.Forms.Label $favInfoLabel.Text = "Quick launch pre-configured profiles for common scenarios" $favInfoLabel.Location = New-Object System.Drawing.Point(20, 20) $favInfoLabel.Size = New-Object System.Drawing.Size(500, 20) $favInfoLabel.Font = New-Object System.Drawing.Font("Segoe UI", 9) $favInfoLabel.ForeColor = [System.Drawing.Color]::FromArgb(0, 102, 204) $favoritesTab.Controls.Add($favInfoLabel) # Quick Launch Buttons $quickLaunchGroupBox = New-Object System.Windows.Forms.GroupBox $quickLaunchGroupBox.Text = "Quick Launch Profiles" $quickLaunchGroupBox.Location = New-Object System.Drawing.Point(20, 50) $quickLaunchGroupBox.Size = New-Object System.Drawing.Size(500, 320) $favoritesTab.Controls.Add($quickLaunchGroupBox) # Profile 1: Basic Discrete $profile1Button = New-Object System.Windows.Forms.Button $profile1Button.Text = "[Mouse] Basic Discrete" $profile1Button.Location = New-Object System.Drawing.Point(20, 30) $profile1Button.Size = New-Object System.Drawing.Size(220, 50) $profile1Button.BackColor = [System.Drawing.Color]::FromArgb(230, 240, 255) $quickLaunchGroupBox.Controls.Add($profile1Button) $profile1Desc = New-Object System.Windows.Forms.Label $profile1Desc.Text = "Random mouse movements every 1 second`nSoftware method, incognito mode" $profile1Desc.Location = New-Object System.Drawing.Point(250, 30) $profile1Desc.Size = New-Object System.Drawing.Size(230, 40) $profile1Desc.Font = New-Object System.Drawing.Font("Segoe UI", 8) $quickLaunchGroupBox.Controls.Add($profile1Desc) $profile1Button.Add_Click({ try { Start-PSMouseJiggler -Interval 1000 -MovementPattern 'Random' -Duration 0 -Incognito $statusLabel.Text = "Status: Running (Basic Discrete)" $statusLabel.ForeColor = [System.Drawing.Color]::DarkGreen $statusDetailsLabel.Text = "Profile: Basic Discrete | Random movement, 1s interval" $startButton.Enabled = $false $stopButton.Enabled = $true $tabControl.SelectedIndex = 0 $form.WindowState = [System.Windows.Forms.FormWindowState]::Minimized $form.ShowInTaskbar = $false $notifyIcon.Visible = $true } catch { [System.Windows.Forms.MessageBox]::Show("Error: $($_.Exception.Message)", "Error", "OK", "Error") } }) # Profile 2: Maximum Security $profile2Button = New-Object System.Windows.Forms.Button $profile2Button.Text = "[Lock] Maximum Security" $profile2Button.Location = New-Object System.Drawing.Point(20, 95) $profile2Button.Size = New-Object System.Drawing.Size(220, 50) $profile2Button.BackColor = [System.Drawing.Color]::FromArgb(255, 240, 230) $quickLaunchGroupBox.Controls.Add($profile2Button) $profile2Desc = New-Object System.Windows.Forms.Label $profile2Desc.Text = "Hardware mouse + System API`nBest for strict security policies, 30s interval" $profile2Desc.Location = New-Object System.Drawing.Point(250, 95) $profile2Desc.Size = New-Object System.Drawing.Size(230, 40) $profile2Desc.Font = New-Object System.Drawing.Font("Segoe UI", 8) $quickLaunchGroupBox.Controls.Add($profile2Desc) $profile2Button.Add_Click({ try { Start-PSMouseJiggler -Methods @('MouseHardware', 'SystemAPI') -Interval 30000 -Duration 0 -Incognito $statusLabel.Text = "Status: Running (Maximum Security)" $statusLabel.ForeColor = [System.Drawing.Color]::DarkGreen $statusDetailsLabel.Text = "Profile: Maximum Security | Hardware + System API, 30s interval" $startButton.Enabled = $false $stopButton.Enabled = $true $tabControl.SelectedIndex = 0 $form.WindowState = [System.Windows.Forms.FormWindowState]::Minimized $form.ShowInTaskbar = $false $notifyIcon.Visible = $true } catch { [System.Windows.Forms.MessageBox]::Show("Error: $($_.Exception.Message)", "Error", "OK", "Error") } }) # Profile 3: Keyboard Only $profile3Button = New-Object System.Windows.Forms.Button $profile3Button.Text = "[Key] Keyboard Only" $profile3Button.Location = New-Object System.Drawing.Point(20, 160) $profile3Button.Size = New-Object System.Drawing.Size(220, 50) $profile3Button.BackColor = [System.Drawing.Color]::FromArgb(240, 255, 240) $quickLaunchGroupBox.Controls.Add($profile3Button) $profile3Desc = New-Object System.Windows.Forms.Label $profile3Desc.Text = "Keyboard input only (F15 key)`nNo mouse movement, 30s interval" $profile3Desc.Location = New-Object System.Drawing.Point(250, 160) $profile3Desc.Size = New-Object System.Drawing.Size(230, 40) $profile3Desc.Font = New-Object System.Drawing.Font("Segoe UI", 8) $quickLaunchGroupBox.Controls.Add($profile3Desc) $profile3Button.Add_Click({ try { Start-PSMouseJiggler -Methods @('Keyboard') -Interval 30000 -Duration 0 -Incognito $statusLabel.Text = "Status: Running (Keyboard Only)" $statusLabel.ForeColor = [System.Drawing.Color]::DarkGreen $statusDetailsLabel.Text = "Profile: Keyboard Only | F15 key press, 30s interval" $startButton.Enabled = $false $stopButton.Enabled = $true $tabControl.SelectedIndex = 0 $form.WindowState = [System.Windows.Forms.FormWindowState]::Minimized $form.ShowInTaskbar = $false $notifyIcon.Visible = $true } catch { [System.Windows.Forms.MessageBox]::Show("Error: $($_.Exception.Message)", "Error", "OK", "Error") } }) # Profile 4: System API Only $profile4Button = New-Object System.Windows.Forms.Button $profile4Button.Text = "[API] System API Only" $profile4Button.Location = New-Object System.Drawing.Point(20, 225) $profile4Button.Size = New-Object System.Drawing.Size(220, 50) $profile4Button.BackColor = [System.Drawing.Color]::FromArgb(255, 250, 230) $quickLaunchGroupBox.Controls.Add($profile4Button) $profile4Desc = New-Object System.Windows.Forms.Label $profile4Desc.Text = "System API only (SetThreadExecutionState)`nDirect Windows power management control" $profile4Desc.Location = New-Object System.Drawing.Point(250, 225) $profile4Desc.Size = New-Object System.Drawing.Size(230, 40) $profile4Desc.Font = New-Object System.Drawing.Font("Segoe UI", 8) $quickLaunchGroupBox.Controls.Add($profile4Desc) $profile4Button.Add_Click({ try { Start-PSMouseJiggler -Methods @('SystemAPI') -Interval 30000 -Duration 0 -Incognito $statusLabel.Text = "Status: Running (System API Only)" $statusLabel.ForeColor = [System.Drawing.Color]::DarkGreen $statusDetailsLabel.Text = "Profile: System API | Direct power management control" $startButton.Enabled = $false $stopButton.Enabled = $true $tabControl.SelectedIndex = 0 $form.WindowState = [System.Windows.Forms.FormWindowState]::Minimized $form.ShowInTaskbar = $false $notifyIcon.Visible = $true } catch { [System.Windows.Forms.MessageBox]::Show("Error: $($_.Exception.Message)", "Error", "OK", "Error") } }) # Profile 5: All Methods $profile5Button = New-Object System.Windows.Forms.Button $profile5Button.Text = "[MAX] All Methods (Maximum)" $profile5Button.Location = New-Object System.Drawing.Point(20, 285) $profile5Button.Size = New-Object System.Drawing.Size(460, 25) $profile5Button.BackColor = [System.Drawing.Color]::FromArgb(255, 220, 220) $profile5Button.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold) $quickLaunchGroupBox.Controls.Add($profile5Button) $profile5Button.Add_Click({ try { Start-PSMouseJiggler -Methods @('MouseSoftware', 'MouseHardware', 'Keyboard', 'SystemAPI') -Interval 30000 -Duration 0 -Incognito $statusLabel.Text = "Status: Running (All Methods)" $statusLabel.ForeColor = [System.Drawing.Color]::DarkGreen $statusDetailsLabel.Text = "Profile: All Methods | Maximum reliability with all techniques" $startButton.Enabled = $false $stopButton.Enabled = $true $tabControl.SelectedIndex = 0 $form.WindowState = [System.Windows.Forms.FormWindowState]::Minimized $form.ShowInTaskbar = $false $notifyIcon.Visible = $true } catch { [System.Windows.Forms.MessageBox]::Show("Error: $($_.Exception.Message)", "Error", "OK", "Error") } }) #endregion # System tray icon lets the user restore or stop jiggling once the window is minimized/hidden by Incognito mode $notifyIcon = New-Object System.Windows.Forms.NotifyIcon $notifyIcon.Icon = [System.Drawing.SystemIcons]::Application $notifyIcon.Text = "PSMouseJiggler" $notifyIcon.Visible = $false $trayMenu = New-Object System.Windows.Forms.ContextMenuStrip $trayShowItem = $trayMenu.Items.Add("Show Window") $trayStopItem = $trayMenu.Items.Add("Stop Jiggling") $notifyIcon.ContextMenuStrip = $trayMenu $restoreFormAction = { $form.WindowState = [System.Windows.Forms.FormWindowState]::Normal $form.ShowInTaskbar = $true $form.Activate() } $stopJigglingAction = { Stop-PSMouseJiggler $statusLabel.Text = "Status: Stopped" $statusLabel.ForeColor = [System.Drawing.Color]::DarkRed $statusDetailsLabel.Text = "Ready to start mouse jiggling" $startButton.Enabled = $true $stopButton.Enabled = $false $notifyIcon.Visible = $false & $restoreFormAction } $trayShowItem.Add_Click($restoreFormAction) $trayStopItem.Add_Click($stopJigglingAction) $notifyIcon.Add_DoubleClick($restoreFormAction) #region Control Buttons # Start button $startButton = New-Object System.Windows.Forms.Button $startButton.Text = "> Start Jiggling" $startButton.Location = New-Object System.Drawing.Point(50, 445) $startButton.Size = New-Object System.Drawing.Size(150, 40) $startButton.BackColor = [System.Drawing.Color]::FromArgb(76, 175, 80) $startButton.ForeColor = [System.Drawing.Color]::White $startButton.Font = New-Object System.Drawing.Font("Segoe UI", 10, [System.Drawing.FontStyle]::Bold) $startButton.FlatStyle = "Flat" $startButton.Add_Click({ try { if ($tabControl.SelectedIndex -eq 0) { # Basic mode $interval = [int]$intervalTextBox.Text $duration = [int]$durationTextBox.Text $pattern = $patternComboBox.SelectedItem.ToString() $incognito = $incognitoCheckbox.Checked $strategy = $strategyComboBox.SelectedItem.ToString() if ($strategy -ne 'Manual') { # Strategy overrides Mouse Input Method with a recommended method combination Start-PSMouseJiggler -Strategy $strategy -Interval $interval -Duration $duration -Incognito:$incognito $statusDetailsLabel.Text = "Mode: Basic | Strategy: $strategy" } # Determine mouse methods based on selection elseif ($mouseTypeComboBox.SelectedIndex -eq 1) { # Hardware only Start-PSMouseJiggler -Methods @('MouseHardware') -Interval $interval -Duration $duration -Incognito:$incognito $statusDetailsLabel.Text = "Mode: Basic | Pattern: $pattern | Method: Hardware Mouse" } elseif ($mouseTypeComboBox.SelectedIndex -eq 2) { # Both methods Start-PSMouseJiggler -Methods @('MouseSoftware', 'MouseHardware') -Interval $interval -Duration $duration -Incognito:$incognito $statusDetailsLabel.Text = "Mode: Basic | Pattern: $pattern | Method: Both (Software + Hardware)" } else { # Software (standard) Start-PSMouseJiggler -Interval $interval -MovementPattern $pattern -Duration $duration -Incognito:$incognito $statusDetailsLabel.Text = "Mode: Basic | Pattern: $pattern | Method: Software Mouse" } $statusLabel.Text = "Status: Running" $statusLabel.ForeColor = [System.Drawing.Color]::DarkGreen } else { # Advanced mode $methods = @() if ($mouseSoftwareCheckbox.Checked) { $methods += 'MouseSoftware' } if ($mouseHardwareCheckbox.Checked) { $methods += 'MouseHardware' } if ($keyboardCheckbox.Checked) { $methods += 'Keyboard' } if ($systemApiCheckbox.Checked) { $methods += 'SystemAPI' } if ($methods.Count -eq 0) { [System.Windows.Forms.MessageBox]::Show("Please select at least one keep-awake method.", "Error", "OK", "Error") return } $interval = [int]$advIntervalTextBox.Text $duration = [int]$advDurationTextBox.Text $incognito = $advIncognitoCheckbox.Checked Start-PSMouseJiggler -Methods $methods -Interval $interval -Duration $duration -Incognito:$incognito $statusLabel.Text = "Status: Running (Advanced)" $statusLabel.ForeColor = [System.Drawing.Color]::DarkGreen $statusDetailsLabel.Text = "Mode: Advanced | Methods: $($methods -join ', ')" } $startButton.Enabled = $false $stopButton.Enabled = $true # If incognito mode is enabled, minimize the form if ($incognito -or $advIncognitoCheckbox.Checked) { $form.WindowState = [System.Windows.Forms.FormWindowState]::Minimized $form.ShowInTaskbar = $false $notifyIcon.Visible = $true } } catch { [System.Windows.Forms.MessageBox]::Show("Error: $($_.Exception.Message)", "Error", "OK", "Error") } }) $form.Controls.Add($startButton) # Stop button $stopButton = New-Object System.Windows.Forms.Button $stopButton.Text = "[] Stop Jiggling" $stopButton.Location = New-Object System.Drawing.Point(230, 445) $stopButton.Size = New-Object System.Drawing.Size(150, 40) $stopButton.Enabled = $false $stopButton.BackColor = [System.Drawing.Color]::FromArgb(244, 67, 54) $stopButton.ForeColor = [System.Drawing.Color]::White $stopButton.Font = New-Object System.Drawing.Font("Segoe UI", 10, [System.Drawing.FontStyle]::Bold) $stopButton.FlatStyle = "Flat" $stopButton.Add_Click($stopJigglingAction) $form.Controls.Add($stopButton) # Help button $helpButton = New-Object System.Windows.Forms.Button $helpButton.Text = "? Help" $helpButton.Location = New-Object System.Drawing.Point(410, 445) $helpButton.Size = New-Object System.Drawing.Size(100, 40) $helpButton.BackColor = [System.Drawing.Color]::FromArgb(158, 158, 158) $helpButton.ForeColor = [System.Drawing.Color]::White $helpButton.Font = New-Object System.Drawing.Font("Segoe UI", 10, [System.Drawing.FontStyle]::Bold) $helpButton.FlatStyle = "Flat" $helpButton.Add_Click({ $helpMessage = @" PSMouseJiggler Help BASIC MODE: - Choose movement pattern (Random recommended) - Set interval between movements (1000ms = 1 second) - Set duration (0 = run until stopped) - Choose mouse input method: * Software: Standard method (most compatible) * Hardware: Low-level input (better for strict policies) * Both: Maximum reliability - Or pick a Strategy to override Mouse Input Method with a recommended combination: * Adaptive: SystemAPI + Hardware mouse (Windows) * LowImpact: SystemAPI only, no mouse movement * Compatibility: Software mouse only, most compatible * AppKeepAlive: SystemAPI + Keyboard, keeps apps active without moving the mouse ADVANCED MODE: - Select multiple methods for maximum effectiveness - Mouse methods prevent screen timeout - Keyboard sends non-disruptive F15 key - System API directly controls Windows power settings - Longer interval recommended (30 seconds) QUICK LAUNCH: - Pre-configured profiles for common scenarios - One-click start with optimal settings - All profiles use incognito mode INCOGNITO MODE: - Minimizes window and hides it from the taskbar - Clears console output - Runs discreetly in background - Use the system tray icon (right-click for Show/Stop, or double-click to restore) to regain control "@ [System.Windows.Forms.MessageBox]::Show($helpMessage, "PSMouseJiggler Help", "OK", "Information") }) $form.Controls.Add($helpButton) #endregion # Timer to update status $timer = New-Object System.Windows.Forms.Timer $timer.Interval = 1000 $timer.Add_Tick({ if (-not $script:JigglingActive -and $stopButton.Enabled) { $statusLabel.Text = "Status: Stopped" $statusLabel.ForeColor = [System.Drawing.Color]::DarkRed $statusDetailsLabel.Text = "Ready to start mouse jiggling" $startButton.Enabled = $true $stopButton.Enabled = $false $notifyIcon.Visible = $false # Restore form if it was minimized if ($form.WindowState -eq [System.Windows.Forms.FormWindowState]::Minimized) { & $restoreFormAction } } }) $timer.Start() # Add form shown event to check initial state $form.Add_Shown({ # Check if jiggling is already active when GUI opens if ($script:JigglingActive) { $statusLabel.Text = "Status: Running (Started from Console)" $statusLabel.ForeColor = [System.Drawing.Color]::DarkGreen $statusDetailsLabel.Text = "Started from command line - use Stop button to halt" $startButton.Enabled = $false $stopButton.Enabled = $true # Restore form if it was minimized if ($form.WindowState -eq [System.Windows.Forms.FormWindowState]::Minimized) { $form.WindowState = [System.Drawing.Color]::Normal $form.ShowInTaskbar = $true } } $form.Activate() }) $form.Add_FormClosed({ $timer.Stop(); $notifyIcon.Visible = $false; $notifyIcon.Dispose() }) [void]$form.ShowDialog() } #endregion #region Configuration Functions function Assert-Configuration { [CmdletBinding()] param ( [Parameter(Mandatory)] [PSCustomObject]$Configuration ) $movementPattern = $Configuration.PSObject.Properties['MovementPattern'] if ($null -ne $movementPattern -and $movementPattern.Value -notin @('Random', 'Horizontal', 'Vertical', 'Circular')) { throw "MovementPattern must be Random, Horizontal, Vertical, or Circular." } foreach ($propertyName in @('MovementSpeed', 'JiggleInterval')) { $property = $Configuration.PSObject.Properties[$propertyName] if ($null -ne $property -and ($property.Value -isnot [int] -and $property.Value -isnot [long] -and $property.Value -isnot [double])) { throw "$propertyName must be a number." } if ($null -ne $property -and $property.Value -lt 1) { throw "$propertyName must be greater than zero." } } $duration = $Configuration.PSObject.Properties['Duration'] if ($null -ne $duration -and ($duration.Value -isnot [int] -and $duration.Value -isnot [long] -and $duration.Value -isnot [double])) { throw "Duration must be a number." } if ($null -ne $duration -and $duration.Value -lt 0) { throw "Duration cannot be negative." } return $Configuration } <# .SYNOPSIS Gets configuration settings from the config file. .DESCRIPTION Loads configuration from the default.json file or creates default settings. .PARAMETER ConfigFilePath Path to the configuration file. If not specified, uses the default location. .EXAMPLE $config = Get-Configuration Gets the current configuration. #> function Get-Configuration { [CmdletBinding()] param ( [Parameter()] [string]$ConfigFilePath ) if (-not $ConfigFilePath) { $moduleRoot = Split-Path -Parent $PSScriptRoot $ConfigFilePath = Join-Path $moduleRoot "config\default.json" } if (Test-Path $ConfigFilePath) { try { $config = Get-Content -Path $ConfigFilePath -Raw | ConvertFrom-Json Assert-Configuration -Configuration $config | Out-Null Write-Verbose "Configuration loaded from $ConfigFilePath" return $config } catch { Write-Warning "Error loading configuration: $($_.Exception.Message)" return Get-DefaultConfiguration } } else { Write-Verbose "Configuration file not found, using defaults" return Get-DefaultConfiguration } } <# .SYNOPSIS Saves configuration settings to the config file. .DESCRIPTION Saves the provided configuration object to the JSON config file. .PARAMETER Configuration The configuration object to save. .PARAMETER ConfigFilePath Path to save the configuration file. .EXAMPLE Save-Configuration -Configuration $config Saves the configuration to the default location. #> function Save-Configuration { [CmdletBinding()] param ( [Parameter(Mandatory)] [PSCustomObject]$Configuration, [Parameter()] [string]$ConfigFilePath ) if (-not $ConfigFilePath) { $moduleRoot = Split-Path -Parent $PSScriptRoot $ConfigFilePath = Join-Path $moduleRoot "config\default.json" } try { Assert-Configuration -Configuration $Configuration | Out-Null $configDir = Split-Path -Parent $ConfigFilePath if (-not (Test-Path $configDir)) { New-Item -ItemType Directory -Path $configDir -Force | Out-Null } $Configuration | ConvertTo-Json -Depth 10 | Set-Content -Path $ConfigFilePath -Force Write-Verbose "Configuration saved to $ConfigFilePath" } catch { Write-Error "Failed to save configuration: $($_.Exception.Message)" } } <# .SYNOPSIS Updates a specific configuration setting. .DESCRIPTION Updates a single configuration key with a new value. .PARAMETER Key The configuration key to update. .PARAMETER Value The new value for the key. .PARAMETER ConfigFilePath Path to the configuration file. .EXAMPLE Update-Configuration -Key "MovementSpeed" -Value 150 Updates the MovementSpeed setting to 150. #> function Update-Configuration { [CmdletBinding()] param ( [Parameter(Mandatory)] [string]$Key, [Parameter(Mandatory)] [object]$Value, [Parameter()] [string]$ConfigFilePath ) $config = Get-Configuration -ConfigFilePath $ConfigFilePath $config | Add-Member -MemberType NoteProperty -Name $Key -Value $Value -Force Save-Configuration -Configuration $config -ConfigFilePath $ConfigFilePath } function Get-PSMJProfile { [CmdletBinding()] param ( [Parameter()] [string]$Name, [Parameter()] [string]$ConfigFilePath ) $config = Get-Configuration -ConfigFilePath $ConfigFilePath $profilesProperty = $config.PSObject.Properties['Profiles'] if ($null -eq $profilesProperty -or $null -eq $profilesProperty.Value) { return @() } if ($Name) { $profileProperty = $profilesProperty.Value.PSObject.Properties[$Name] if ($null -eq $profileProperty) { Write-Error "Profile '$Name' was not found." return } $profile = [PSCustomObject]@{ Name = $Name } foreach ($property in $profileProperty.Value.PSObject.Properties) { $profile | Add-Member -MemberType NoteProperty -Name $property.Name -Value $property.Value } return $profile } foreach ($profileProperty in $profilesProperty.Value.PSObject.Properties) { $profile = [PSCustomObject]@{ Name = $profileProperty.Name } foreach ($property in $profileProperty.Value.PSObject.Properties) { $profile | Add-Member -MemberType NoteProperty -Name $property.Name -Value $property.Value } $profile } } function Save-PSMJProfile { [CmdletBinding()] param ( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter(Mandatory)] [PSCustomObject]$Profile, [Parameter()] [string]$ConfigFilePath ) $methods = $Profile.PSObject.Properties['Methods'] if ($null -ne $methods) { foreach ($method in @($methods.Value)) { if ($method -notin @('MouseSoftware', 'MouseHardware', 'Keyboard', 'SystemAPI', 'All')) { throw "Profile Methods entries must be MouseSoftware, MouseHardware, Keyboard, SystemAPI, or All." } } } $interval = $Profile.PSObject.Properties['Interval'] if ($null -ne $interval) { if ($interval.Value -isnot [int] -and $interval.Value -isnot [long] -and $interval.Value -isnot [double]) { throw "Profile Interval must be a number." } if ([double]$interval.Value -lt 1) { throw "Profile Interval must be greater than zero." } } $duration = $Profile.PSObject.Properties['Duration'] if ($null -ne $duration) { if ($duration.Value -isnot [int] -and $duration.Value -isnot [long] -and $duration.Value -isnot [double]) { throw "Profile Duration must be a number." } if ([double]$duration.Value -lt 0) { throw "Profile Duration cannot be negative." } } $config = Get-Configuration -ConfigFilePath $ConfigFilePath $profilesProperty = $config.PSObject.Properties['Profiles'] if ($null -eq $profilesProperty -or $null -eq $profilesProperty.Value) { $config | Add-Member -MemberType NoteProperty -Name Profiles -Value ([PSCustomObject]@{}) -Force } $profileCopy = [PSCustomObject]@{} foreach ($property in $Profile.PSObject.Properties) { if ($property.Name -ne 'Name') { $profileCopy | Add-Member -MemberType NoteProperty -Name $property.Name -Value $property.Value } } $config.Profiles | Add-Member -MemberType NoteProperty -Name $Name -Value $profileCopy -Force Save-Configuration -Configuration $config -ConfigFilePath $ConfigFilePath Get-PSMJProfile -Name $Name -ConfigFilePath $ConfigFilePath } function Remove-PSMJProfile { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter()] [string]$ConfigFilePath ) $config = Get-Configuration -ConfigFilePath $ConfigFilePath $profilesProperty = $config.PSObject.Properties['Profiles'] $profileProperty = if ($null -ne $profilesProperty) { $profilesProperty.Value.PSObject.Properties[$Name] } if ($null -eq $profileProperty) { Write-Error "Profile '$Name' was not found." return } if ($PSCmdlet.ShouldProcess($Name, 'Remove saved profile')) { $profilesProperty.Value.PSObject.Properties.Remove($Name) Save-Configuration -Configuration $config -ConfigFilePath $ConfigFilePath } } function Start-PSMJProfile { [CmdletBinding()] param ( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter()] [string]$ConfigFilePath ) $profile = Get-PSMJProfile -Name $Name -ConfigFilePath $ConfigFilePath -ErrorAction Stop $incognito = [bool]$profile.Incognito $parameters = @{ Interval = if ($null -ne $profile.Interval) { [int]$profile.Interval } else { 1000 } MovementPattern = if ($profile.MovementPattern) { [string]$profile.MovementPattern } else { 'Random' } Duration = if ($null -ne $profile.Duration) { [int]$profile.Duration } else { 0 } Incognito = $incognito } if ($null -ne $profile.Methods) { $parameters.Methods = @($profile.Methods) } if ($profile.Strategy) { $parameters.Strategy = [string]$profile.Strategy } Start-PSMouseJiggler @parameters } function Get-PSMJDiagnostics { [CmdletBinding()] param ( [Parameter()] [string]$ConfigFilePath ) Sync-PSMJState $configurationValid = $true $configuration = $null try { $configuration = Get-Configuration -ConfigFilePath $ConfigFilePath -WarningAction Stop } catch { $configurationValid = $false } $jobState = 'NotStarted' if ($null -ne $script:JigglingJob) { $jobState = [string]$script:JigglingJob.State } $profileCount = 0 if ($null -ne $configuration) { $profilesProperty = $configuration.PSObject.Properties['Profiles'] if ($null -ne $profilesProperty -and $null -ne $profilesProperty.Value) { $profileCount = @($profilesProperty.Value.PSObject.Properties).Count } } $module = Get-Module -Name PSMouseJiggler | Select-Object -First 1 [PSCustomObject]@{ ModuleName = 'PSMouseJiggler' ModuleVersion = if ($null -ne $module) { [string]$module.Version } else { $null } PowerShellVersion = [string]$PSVersionTable.PSVersion OperatingSystem = [string]$PSVersionTable.OS IsRunning = [bool]$script:JigglingActive JobState = $jobState ConfigurationValid = $configurationValid ProfileCount = $profileCount ConfigFilePath = if ($ConfigFilePath) { $ConfigFilePath } else { $null } } } <# .SYNOPSIS Resets configuration to default values. .DESCRIPTION Creates a new configuration file with default settings. .PARAMETER ConfigFilePath Path to the configuration file. .EXAMPLE Reset-Configuration Resets configuration to defaults. #> function Reset-Configuration { [CmdletBinding()] param ( [Parameter()] [string]$ConfigFilePath ) $defaultConfig = Get-DefaultConfiguration Save-Configuration -Configuration $defaultConfig -ConfigFilePath $ConfigFilePath Write-Host "Configuration reset to defaults" -ForegroundColor Green } function Get-DefaultConfiguration { return [PSCustomObject]@{ MovementSpeed = 1000 MovementPattern = "Random" JiggleInterval = 1000 EnableScheduledTasks = $false ScheduledTimes = @() AutoJiggle = $false Duration = 0 Profiles = [PSCustomObject]@{} GuiSettings = @{ WindowPosition = @{ X = 0 Y = 0 } RememberSettings = $true } } } #endregion #region Movement Pattern Functions <# .SYNOPSIS Gets a random movement pattern function. .DESCRIPTION Returns a scriptblock that represents a random movement pattern. .EXAMPLE $pattern = Get-RandomMovementPattern & $pattern #> function Get-RandomMovementPattern { [CmdletBinding()] param() $patterns = @( { $xOffset = Get-Random -Minimum -10 -Maximum 11 $yOffset = Get-Random -Minimum -10 -Maximum 11 Move-Mouse -X $xOffset -Y $yOffset }, { $xOffset = if ((Get-Random -Minimum 0 -Maximum 2) -eq 0) { -5 } else { 5 } Move-Mouse -X $xOffset -Y 0 }, { $yOffset = if ((Get-Random -Minimum 0 -Maximum 2) -eq 0) { -5 } else { 5 } Move-Mouse -X 0 -Y $yOffset }, { $angle = (Get-Date).Millisecond / 1000 * 2 * [Math]::PI $xOffset = [Math]::Round([Math]::Sin($angle) * 10) $yOffset = [Math]::Round([Math]::Cos($angle) * 10) Move-Mouse -X $xOffset -Y $yOffset } ) return Get-Random -InputObject $patterns } <# .SYNOPSIS Moves the mouse cursor by relative coordinates. .DESCRIPTION Moves the mouse cursor by the specified X and Y offsets. .PARAMETER X Horizontal offset in pixels. .PARAMETER Y Vertical offset in pixels. .EXAMPLE Move-Mouse -X 10 -Y -5 Moves the mouse 10 pixels right and 5 pixels up. #> function Move-Mouse { [CmdletBinding()] param ( [Parameter(Mandatory)] [int]$X, [Parameter(Mandatory)] [int]$Y ) $currentPos = [System.Windows.Forms.Cursor]::Position $newPos = [System.Drawing.Point]::new($currentPos.X + $X, $currentPos.Y + $Y) [System.Windows.Forms.Cursor]::Position = $newPos } <# .SYNOPSIS Starts a movement pattern for a specified duration. .DESCRIPTION Executes random movement patterns for the specified duration. .PARAMETER DurationInSeconds Duration to run movement patterns in seconds. .EXAMPLE Start-MovementPattern -DurationInSeconds 60 Runs movement patterns for 60 seconds. #> function Start-MovementPattern { [CmdletBinding()] param ( [Parameter(Mandatory)] [ValidateRange(0, 2147483647)] [int]$DurationInSeconds ) $endTime = (Get-Date).AddSeconds($DurationInSeconds) Write-Host "Starting movement pattern for $DurationInSeconds seconds" -ForegroundColor Green while ((Get-Date) -lt $endTime) { $pattern = Get-RandomMovementPattern & $pattern Start-Sleep -Milliseconds 1000 } Write-Host "Movement pattern completed" -ForegroundColor Green } <# .SYNOPSIS Stops the movement pattern. .DESCRIPTION Placeholder function for stopping movement patterns. .EXAMPLE Stop-MovementPattern #> function Stop-MovementPattern { [CmdletBinding()] param() Write-Host "Movement pattern stopped." -ForegroundColor Yellow } #endregion #region Scheduled Task Functions <# .SYNOPSIS Gets scheduled tasks related to PSMouseJiggler. .DESCRIPTION Retrieves scheduled tasks that match the specified task name pattern. .PARAMETER TaskName Name or pattern to search for in task names. .EXAMPLE Get-PSMJScheduledTasks -TaskName "PSMouseJiggler" Gets all tasks with "PSMouseJiggler" in the name. #> function Get-PSMJScheduledTasks { [CmdletBinding()] param ( [Parameter()] [string]$TaskName = "PSMouseJiggler*" ) try { $tasks = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue return $tasks } catch { Write-Warning "Error getting scheduled tasks: $($_.Exception.Message)" return @() } } function Get-PSMJScheduledTaskStatus { [CmdletBinding()] param ( [Parameter()] [string]$TaskName = 'PSMouseJiggler*' ) try { $tasks = @(Get-ScheduledTask -TaskName $TaskName -ErrorAction Stop) foreach ($task in $tasks) { $taskInfo = Get-ScheduledTaskInfo -TaskName $task.TaskName -TaskPath $task.TaskPath -ErrorAction Stop [PSCustomObject]@{ TaskName = $task.TaskName TaskPath = $task.TaskPath State = [string]$task.State Enabled = [bool]$task.Settings.Enabled NextRunTime = $taskInfo.NextRunTime LastRunTime = $taskInfo.LastRunTime LastTaskResult = $taskInfo.LastTaskResult } } } catch { Write-Warning "Error getting scheduled task status: $($_.Exception.Message)" return @() } } function New-PSMJScheduledProfileTask { [CmdletBinding()] param ( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$TaskName, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$ProfileName, [Parameter(Mandatory)] [DateTime]$StartTime, [Parameter()] [ValidateRange(0, 2147483647)] [int]$RepeatIntervalMinutes = 0, [Parameter()] [string]$ConfigFilePath ) Get-PSMJProfile -Name $ProfileName -ConfigFilePath $ConfigFilePath -ErrorAction Stop | Out-Null $escapedProfileName = $ProfileName.Replace("'", "''") $action = "Import-Module PSMouseJiggler -ErrorAction Stop; Start-PSMJProfile -Name '$escapedProfileName'" if ($ConfigFilePath) { $escapedConfigPath = $ConfigFilePath.Replace("'", "''") $action += " -ConfigFilePath '$escapedConfigPath'" } New-PSMJScheduledTask ` -TaskName $TaskName ` -Action $action ` -StartTime $StartTime ` -RepeatIntervalMinutes $RepeatIntervalMinutes } <# .SYNOPSIS Creates a new scheduled task for PSMouseJiggler. .DESCRIPTION Creates a scheduled task to run PSMouseJiggler at specified times. .PARAMETER TaskName Name for the scheduled task. .PARAMETER Action Command or script to execute. .PARAMETER StartTime When to start the task. .PARAMETER RepeatIntervalMinutes How often to repeat the task in minutes. .EXAMPLE New-PSMJScheduledTask -TaskName "MyJiggler" -Action "powershell.exe -Command 'Start-PSMouseJiggler'" -StartTime (Get-Date).AddMinutes(5) Creates a task to start jiggling in 5 minutes. #> function New-PSMJScheduledTask { [CmdletBinding()] param ( [Parameter(Mandatory)] [string]$TaskName, [Parameter(Mandatory)] [string]$Action, [Parameter(Mandatory)] [DateTime]$StartTime, [Parameter()] [ValidateRange(0, 2147483647)] [int]$RepeatIntervalMinutes = 0 ) try { $action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-NoProfile -WindowStyle Hidden -Command `"$Action`"" if ($RepeatIntervalMinutes -gt 0) { $trigger = New-ScheduledTaskTrigger -Once -At $StartTime -RepetitionInterval (New-TimeSpan -Minutes $RepeatIntervalMinutes) } else { $trigger = New-ScheduledTaskTrigger -Once -At $StartTime } $settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable Register-ScheduledTask -TaskName $TaskName -Action $action -Trigger $trigger -Settings $settings -Force Write-Host "Scheduled task '$TaskName' created successfully" -ForegroundColor Green } catch { Write-Error "Failed to create scheduled task: $($_.Exception.Message)" } } <# .SYNOPSIS Removes a scheduled task. .DESCRIPTION Removes the specified scheduled task. .PARAMETER TaskName Name of the task to remove. .EXAMPLE Remove-PSMJScheduledTask -TaskName "MyJiggler" Removes the MyJiggler scheduled task. #> function Remove-PSMJScheduledTask { [CmdletBinding()] param ( [Parameter(Mandatory)] [string]$TaskName ) try { Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false Write-Host "Scheduled task '$TaskName' removed" -ForegroundColor Green } catch { Write-Error "Failed to remove scheduled task: $($_.Exception.Message)" } } <# .SYNOPSIS Starts a scheduled task. .DESCRIPTION Manually starts the specified scheduled task. .PARAMETER TaskName Name of the task to start. .EXAMPLE Start-PSMJScheduledTask -TaskName "MyJiggler" Starts the MyJiggler scheduled task. #> function Start-PSMJScheduledTask { [CmdletBinding()] param ( [Parameter(Mandatory)] [string]$TaskName ) try { Start-ScheduledTask -TaskName $TaskName Write-Host "Scheduled task '$TaskName' started" -ForegroundColor Green } catch { Write-Error "Failed to start scheduled task: $($_.Exception.Message)" } } <# .SYNOPSIS Stops a scheduled task. .DESCRIPTION Stops the specified running scheduled task. .PARAMETER TaskName Name of the task to stop. .EXAMPLE Stop-PSMJScheduledTask -TaskName "MyJiggler" Stops the MyJiggler scheduled task. #> function Stop-PSMJScheduledTask { [CmdletBinding()] param ( [Parameter(Mandatory)] [string]$TaskName ) try { Stop-ScheduledTask -TaskName $TaskName Write-Host "Scheduled task '$TaskName' stopped" -ForegroundColor Green } catch { Write-Error "Failed to stop scheduled task: $($_.Exception.Message)" } } #endregion #region Advanced Wake Prevention Functions <# .SYNOPSIS Prevents system idle using Windows SetThreadExecutionState API. .DESCRIPTION Uses P/Invoke to call the SetThreadExecutionState Windows API to prevent the system from sleeping. .PARAMETER Duration Duration in seconds to prevent idle. Default is 0 (continuous). .EXAMPLE Prevent-SystemIdle -Duration 3600 Prevents the system from going idle for 1 hour. #> function Prevent-SystemIdle { [CmdletBinding()] param ( [Parameter()] [int]$Duration = 0 ) # Define the P/Invoke signature for SetThreadExecutionState Add-Type -TypeDefinition @" using System; using System.Runtime.InteropServices; public static class DisplayState { [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern uint SetThreadExecutionState(uint esFlags); public const uint ES_CONTINUOUS = 0x80000000; public const uint ES_SYSTEM_REQUIRED = 0x00000001; public const uint ES_DISPLAY_REQUIRED = 0x00000002; public const uint ES_AWAYMODE_REQUIRED = 0x00000040; } "@ # Prevent system sleep and display sleep [DisplayState]::SetThreadExecutionState( [DisplayState]::ES_CONTINUOUS -bor [DisplayState]::ES_SYSTEM_REQUIRED -bor [DisplayState]::ES_DISPLAY_REQUIRED) | Out-Null Write-Verbose "System idle prevention activated" if ($Duration -gt 0) { Start-Sleep -Seconds $Duration # Reset to normal state [DisplayState]::SetThreadExecutionState([DisplayState]::ES_CONTINUOUS) | Out-Null Write-Verbose "System idle prevention deactivated after $Duration seconds" } } <# .SYNOPSIS Simulates keyboard input using hardware-level API. .DESCRIPTION Uses SendInput Windows API to simulate hardware-level keyboard events. .PARAMETER Key The key to simulate. Defaults to a non-disruptive key (F15). .EXAMPLE Send-KeyboardInput Sends a function key press that's typically not mapped to any action. #> function Send-KeyboardInput { [CmdletBinding()] param ( [Parameter()] [string]$Key = "{F15}" ) Add-Type -AssemblyName System.Windows.Forms [System.Windows.Forms.SendKeys]::SendWait($Key) Write-Verbose "Sent keyboard input: $Key" } <# .SYNOPSIS Simulates mouse input using hardware-level API. .DESCRIPTION Uses SendInput Windows API to simulate hardware-level mouse events. .PARAMETER XOffset Horizontal movement offset. .PARAMETER YOffset Vertical movement offset. .EXAMPLE Send-MouseInput -XOffset 5 -YOffset -5 Simulates mouse movement using hardware-level API. #> function Send-MouseInput { [CmdletBinding()] param ( [Parameter()] [int]$XOffset = 0, [Parameter()] [int]$YOffset = 0 ) # Define the SendInput API Add-Type -TypeDefinition @" using System; using System.Runtime.InteropServices; public static class MouseSimulator { [StructLayout(LayoutKind.Sequential)] public struct MOUSEINPUT { public int dx; public int dy; public uint mouseData; public uint dwFlags; public uint time; public IntPtr dwExtraInfo; } [StructLayout(LayoutKind.Sequential)] public struct INPUT { public uint type; public MOUSEINPUT mi; } [DllImport("user32.dll", SetLastError = true)] public static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize); public const int INPUT_MOUSE = 0; public const int MOUSEEVENTF_MOVE = 0x0001; public const int MOUSEEVENTF_ABSOLUTE = 0x8000; } "@ $mouseInputStructure = New-Object MouseSimulator+INPUT $mouseInputStructure.type = [MouseSimulator]::INPUT_MOUSE $mouseInputStructure.mi.dx = $XOffset $mouseInputStructure.mi.dy = $YOffset $mouseInputStructure.mi.dwFlags = [MouseSimulator]::MOUSEEVENTF_MOVE $mouseInputStructure.mi.time = 0 $mouseInputStructure.mi.dwExtraInfo = [IntPtr]::Zero $inputArray = @($mouseInputStructure) $result = [MouseSimulator]::SendInput(1, $inputArray, [System.Runtime.InteropServices.Marshal]::SizeOf([type][MouseSimulator+INPUT])) if ($result -eq 0) { Write-Error "SendInput failed to send mouse event. Win32 error: $([System.Runtime.InteropServices.Marshal]::GetLastWin32Error())" } Write-Verbose "Sent hardware-level mouse movement: X=$XOffset, Y=$YOffset" } function Get-PSMJRecommendedMethods { [CmdletBinding()] param ( [Parameter()] [ValidateSet('Adaptive', 'LowImpact', 'Compatibility', 'AppKeepAlive')] [string]$Strategy = 'Adaptive' ) if ($Strategy -eq 'LowImpact') { return @('SystemAPI') } if ($Strategy -eq 'Compatibility') { return @('MouseSoftware') } if ($Strategy -eq 'AppKeepAlive') { return @('SystemAPI', 'Keyboard') } if ($env:OS -eq 'Windows_NT') { return @('SystemAPI', 'MouseHardware') } return @('MouseSoftware') } #endregion # Module cleanup when module is removed $MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = { if ($script:JigglingActive) { if ($script:JigglingJob) { # Add type checking to handle both real jobs and mock objects used in testing if ($script:JigglingJob -is [System.Management.Automation.Job]) { Stop-Job -Job $script:JigglingJob -ErrorAction SilentlyContinue Remove-Job -Job $script:JigglingJob -ErrorAction SilentlyContinue } else { Write-Verbose "Cleaning up non-Job object (likely a test mock)" } $script:JigglingJob = $null } $script:JigglingActive = $false } } # Export module members (this is also defined in the manifest for best practice) Export-ModuleMember -Function @( 'Start-PSMouseJiggler', 'Stop-PSMouseJiggler', 'Get-NewMousePosition', 'Show-PSMouseJigglerGUI', 'Get-Configuration', 'Save-Configuration', 'Update-Configuration', 'Reset-Configuration', 'Get-PSMJProfile', 'Save-PSMJProfile', 'Remove-PSMJProfile', 'Start-PSMJProfile', 'Get-PSMJDiagnostics', 'Get-RandomMovementPattern', 'Move-Mouse', 'Start-MovementPattern', 'Stop-MovementPattern', 'Get-PSMJScheduledTasks', # Updated name 'Get-PSMJScheduledTaskStatus', 'New-PSMJScheduledProfileTask', 'New-PSMJScheduledTask', # Updated name 'Remove-PSMJScheduledTask', # Updated name 'Start-PSMJScheduledTask', # Updated name 'Stop-PSMJScheduledTask', # Updated name # New functions 'Prevent-SystemIdle', 'Send-KeyboardInput', 'Send-MouseInput', 'Get-PSMJRecommendedMethods' ) |