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 #region Core Mouse Jiggling Functions <# .SYNOPSIS Starts the PSMouseJiggler to simulate mouse movements. .DESCRIPTION Starts mouse jiggling with specified interval and movement pattern to prevent the system from going idle. .PARAMETER Interval Time in milliseconds between mouse movements. Default is 1000ms. .PARAMETER MovementPattern The pattern for mouse movement. Valid values: 'Random', 'Horizontal', 'Vertical', 'Circular'. Default is 'Random'. .PARAMETER Duration Duration in seconds to run the jiggler. If not specified, runs indefinitely until stopped. .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. #> function Start-PSMouseJiggler { [CmdletBinding()] param ( [Parameter()] [int]$Interval = 1000, [Parameter()] [ValidateSet('Random', 'Horizontal', 'Vertical', 'Circular')] [string]$MovementPattern = 'Random', [Parameter()] [int]$Duration = 0 ) if ($script:JigglingActive) { Write-Warning "PSMouseJiggler is already running. Use Stop-PSMouseJiggler to stop it first." return } Write-Host "Starting PSMouseJiggler with $MovementPattern pattern, interval: $Interval ms" -ForegroundColor Green $script:JigglingActive = $true $startTime = Get-Date $script:JigglingJob = Start-Job -ScriptBlock { param($Interval, $MovementPattern, $Duration, $StartTime) Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing $endTime = if ($Duration -gt 0) { $StartTime.AddSeconds($Duration) } else { [DateTime]::MaxValue } while ((Get-Date) -lt $endTime) { # Get current mouse position $currentPos = [System.Windows.Forms.Cursor]::Position # Calculate new position based on pattern switch ($MovementPattern) { 'Random' { $xOffset = Get-Random -Minimum -10 -Maximum 11 $yOffset = Get-Random -Minimum -10 -Maximum 11 $newPos = [System.Drawing.Point]::new($currentPos.X + $xOffset, $currentPos.Y + $yOffset) } 'Horizontal' { $xOffset = if ((Get-Random -Minimum 0 -Maximum 2) -eq 0) { -5 } else { 5 } $newPos = [System.Drawing.Point]::new($currentPos.X + $xOffset, $currentPos.Y) } 'Vertical' { $yOffset = if ((Get-Random -Minimum 0 -Maximum 2) -eq 0) { -5 } else { 5 } $newPos = [System.Drawing.Point]::new($currentPos.X, $currentPos.Y + $yOffset) } 'Circular' { $angle = (Get-Date).Millisecond / 1000.0 * 2 * [Math]::PI $radius = 10 $xOffset = [Math]::Cos($angle) * $radius $yOffset = [Math]::Sin($angle) * $radius $newPos = [System.Drawing.Point]::new($currentPos.X + $xOffset, $currentPos.Y + $yOffset) } default { $newPos = $currentPos } } # Move mouse to new position [System.Windows.Forms.Cursor]::Position = $newPos # Wait for specified interval Start-Sleep -Milliseconds $Interval } } -ArgumentList $Interval, $MovementPattern, $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 } } <# .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() if (-not $script:JigglingActive) { Write-Warning "PSMouseJiggler is not currently running." return } if ($script:JigglingJob) { Stop-Job -Job $script:JigglingJob -ErrorAction SilentlyContinue Remove-Job -Job $script:JigglingJob -ErrorAction SilentlyContinue $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 [System.Drawing.Point]::new($CurrentPosition.X + $xOffset, $CurrentPosition.Y + $yOffset) } 'Horizontal' { $xOffset = if ((Get-Random -Minimum 0 -Maximum 2) -eq 0) { -5 } else { 5 } return [System.Drawing.Point]::new($CurrentPosition.X + $xOffset, $CurrentPosition.Y) } 'Vertical' { $yOffset = if ((Get-Random -Minimum 0 -Maximum 2) -eq 0) { -5 } else { 5 } return [System.Drawing.Point]::new($CurrentPosition.X, $CurrentPosition.Y + $yOffset) } 'Circular' { $angle = (Get-Date).Millisecond / 1000.0 * 2 * [Math]::PI $radius = 10 $xOffset = [Math]::Cos($angle) * $radius $yOffset = [Math]::Sin($angle) * $radius return [System.Drawing.Point]::new($CurrentPosition.X + $xOffset, $CurrentPosition.Y + $yOffset) } default { return $CurrentPosition } } } #endregion #region GUI Functions <# .SYNOPSIS Shows the PSMouseJiggler GUI interface. .DESCRIPTION Displays a graphical user interface for controlling the mouse jiggler. .EXAMPLE Show-PSMouseJigglerGUI Opens the GUI interface. #> function Show-PSMouseJigglerGUI { [CmdletBinding()] param() # Create the main form $form = New-Object System.Windows.Forms.Form $form.Text = "PSMouseJiggler" $form.Size = New-Object System.Drawing.Size(400, 410) # Increased height for new controls $form.StartPosition = "CenterScreen" $form.FormBorderStyle = "FixedDialog" $form.MaximizeBox = $false # Status label $statusLabel = New-Object System.Windows.Forms.Label $statusLabel.Text = "Status: Stopped" $statusLabel.Location = New-Object System.Drawing.Point(20, 20) $statusLabel.Size = New-Object System.Drawing.Size(200, 20) $form.Controls.Add($statusLabel) # Interval input $intervalLabel = New-Object System.Windows.Forms.Label $intervalLabel.Text = "Interval (ms):" $intervalLabel.Location = New-Object System.Drawing.Point(20, 60) $intervalLabel.Size = New-Object System.Drawing.Size(80, 20) $form.Controls.Add($intervalLabel) $intervalTextBox = New-Object System.Windows.Forms.TextBox $intervalTextBox.Text = "1000" $intervalTextBox.Location = New-Object System.Drawing.Point(110, 60) $intervalTextBox.Size = New-Object System.Drawing.Size(100, 20) $form.Controls.Add($intervalTextBox) # Pattern selection $patternLabel = New-Object System.Windows.Forms.Label $patternLabel.Text = "Movement Pattern:" $patternLabel.Location = New-Object System.Drawing.Point(20, 100) $patternLabel.Size = New-Object System.Drawing.Size(120, 20) $form.Controls.Add($patternLabel) $patternComboBox = New-Object System.Windows.Forms.ComboBox $patternComboBox.Location = New-Object System.Drawing.Point(150, 100) $patternComboBox.Size = New-Object System.Drawing.Size(120, 20) $patternComboBox.DropDownStyle = "DropDownList" $patternComboBox.Items.AddRange(@("Random", "Horizontal", "Vertical", "Circular")) $patternComboBox.SelectedIndex = 0 $form.Controls.Add($patternComboBox) # Duration input $durationLabel = New-Object System.Windows.Forms.Label $durationLabel.Text = "Duration (sec, 0=infinite):" $durationLabel.Location = New-Object System.Drawing.Point(20, 140) $durationLabel.Size = New-Object System.Drawing.Size(140, 20) $form.Controls.Add($durationLabel) $durationTextBox = New-Object System.Windows.Forms.TextBox $durationTextBox.Text = "0" $durationTextBox.Location = New-Object System.Drawing.Point(170, 140) $durationTextBox.Size = New-Object System.Drawing.Size(100, 20) $form.Controls.Add($durationTextBox) # Advanced mode checkbox $advancedModeCheckbox = New-Object System.Windows.Forms.CheckBox $advancedModeCheckbox.Text = "Use Advanced Keep-Awake Methods" $advancedModeCheckbox.Location = New-Object System.Drawing.Point(20, 180) $advancedModeCheckbox.Size = New-Object System.Drawing.Size(250, 20) $form.Controls.Add($advancedModeCheckbox) # Method selection group $methodsGroupBox = New-Object System.Windows.Forms.GroupBox $methodsGroupBox.Text = "Keep-Awake Methods" $methodsGroupBox.Location = New-Object System.Drawing.Point(20, 210) $methodsGroupBox.Size = New-Object System.Drawing.Size(350, 100) $methodsGroupBox.Enabled = $false $form.Controls.Add($methodsGroupBox) # Method checkboxes $mouseSoftwareCheckbox = New-Object System.Windows.Forms.CheckBox $mouseSoftwareCheckbox.Text = "Software Mouse Movements" $mouseSoftwareCheckbox.Location = New-Object System.Drawing.Point(10, 20) $mouseSoftwareCheckbox.Size = New-Object System.Drawing.Size(200, 20) $mouseSoftwareCheckbox.Checked = $true $methodsGroupBox.Controls.Add($mouseSoftwareCheckbox) $mouseHardwareCheckbox = New-Object System.Windows.Forms.CheckBox $mouseHardwareCheckbox.Text = "Hardware Mouse Movements" $mouseHardwareCheckbox.Location = New-Object System.Drawing.Point(10, 45) $mouseHardwareCheckbox.Size = New-Object System.Drawing.Size(200, 20) $mouseHardwareCheckbox.Checked = $true $methodsGroupBox.Controls.Add($mouseHardwareCheckbox) $keyboardCheckbox = New-Object System.Windows.Forms.CheckBox $keyboardCheckbox.Text = "Keyboard Input" $keyboardCheckbox.Location = New-Object System.Drawing.Point(10, 70) $keyboardCheckbox.Size = New-Object System.Drawing.Size(120, 20) $keyboardCheckbox.Checked = $true $methodsGroupBox.Controls.Add($keyboardCheckbox) $systemApiCheckbox = New-Object System.Windows.Forms.CheckBox $systemApiCheckbox.Text = "System API" $systemApiCheckbox.Location = New-Object System.Drawing.Point(180, 70) $systemApiCheckbox.Size = New-Object System.Drawing.Size(120, 20) $systemApiCheckbox.Checked = $true $methodsGroupBox.Controls.Add($systemApiCheckbox) # Enable/disable method selection based on advanced mode $advancedModeCheckbox.Add_CheckedChanged({ $methodsGroupBox.Enabled = $advancedModeCheckbox.Checked }) # Start button $startButton = New-Object System.Windows.Forms.Button $startButton.Text = "Start Jiggling" $startButton.Location = New-Object System.Drawing.Point(50, 320) $startButton.Size = New-Object System.Drawing.Size(100, 30) $startButton.Add_Click({ try { $interval = [int]$intervalTextBox.Text $duration = [int]$durationTextBox.Text if ($advancedModeCheckbox.Checked) { $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 } Start-KeepAwake -Methods $methods -Interval $interval -Duration $duration $statusLabel.Text = "Status: Running (Advanced Mode)" } else { $pattern = $patternComboBox.SelectedItem.ToString() Start-PSMouseJiggler -Interval $interval -MovementPattern $pattern -Duration $duration $statusLabel.Text = "Status: Running ($pattern)" } $startButton.Enabled = $false $stopButton.Enabled = $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(200, 320) $stopButton.Size = New-Object System.Drawing.Size(100, 30) $stopButton.Enabled = $false $stopButton.Add_Click({ Stop-PSMouseJiggler $statusLabel.Text = "Status: Stopped" $startButton.Enabled = $true $stopButton.Enabled = $false }) $form.Controls.Add($stopButton) # 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" $startButton.Enabled = $true $stopButton.Enabled = $false } }) $timer.Start() # Show the form $form.Add_Shown({ $form.Activate() }) $form.Add_FormClosed({ $timer.Stop() }) [void]$form.ShowDialog() } #endregion #region Configuration Functions <# .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 { $jsonContent = Get-Content -Path $ConfigFilePath -Raw | ConvertFrom-Json return $jsonContent } catch { Write-Warning "Error reading configuration file: $($_.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 { $configDir = Split-Path -Parent $ConfigFilePath if (-not (Test-Path $configDir)) { New-Item -ItemType Directory -Path $configDir -Force | Out-Null } $jsonContent = $Configuration | ConvertTo-Json -Depth 10 Set-Content -Path $ConfigFilePath -Value $jsonContent -Force Write-Verbose "Configuration saved to $ConfigFilePath" } catch { Write-Error "Error saving 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 } <# .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" AutoJiggle = $false Duration = 0 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 = @( { Move-Mouse -X 10 -Y 0; Start-Sleep -Milliseconds 100 }, { Move-Mouse -X -10 -Y 0; Start-Sleep -Milliseconds 100 }, { Move-Mouse -X 0 -Y 10; Start-Sleep -Milliseconds 100 }, { Move-Mouse -X 0 -Y -10; Start-Sleep -Milliseconds 100 }, { Move-Mouse -X 5 -Y 5; Start-Sleep -Milliseconds 100 }, { Move-Mouse -X -5 -Y -5; Start-Sleep -Milliseconds 100 } ) 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()] [int]$X = 0, [Parameter()] [int]$Y = 0 ) $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()] [int]$DurationInSeconds = 60 ) $endTime = (Get-Date).AddSeconds($DurationInSeconds) Write-Host "Starting movement pattern for $DurationInSeconds seconds" -ForegroundColor Green while ((Get-Date) -lt $endTime) { $movementPattern = Get-RandomMovementPattern & $movementPattern } 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-ScheduledTasks -TaskName "PSMouseJiggler" Gets all tasks with "PSMouseJiggler" in the name. #> function Get-ScheduledTasks { [CmdletBinding()] param ( [Parameter()] [string]$TaskName = "PSMouseJiggler" ) try { $tasks = Get-ScheduledTask | Where-Object { $_.TaskName -like "*$TaskName*" } return $tasks } catch { Write-Error "Error retrieving scheduled tasks: $($_.Exception.Message)" return @() } } <# .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-ScheduledTask -TaskName "MyJiggler" -Action "powershell.exe -Command 'Start-PSMouseJiggler'" -StartTime (Get-Date).AddMinutes(5) Creates a task to start jiggling in 5 minutes. #> function New-ScheduledTask { [CmdletBinding()] param ( [Parameter(Mandatory)] [string]$TaskName, [Parameter(Mandatory)] [string]$Action, [Parameter(Mandatory)] [datetime]$StartTime, [Parameter()] [int]$RepeatIntervalMinutes = 60 ) try { $action = New-ScheduledTaskAction -Execute $Action $trigger = New-ScheduledTaskTrigger -At $StartTime -Daily $trigger.RepeatInterval = (New-TimeSpan -Minutes $RepeatIntervalMinutes) Register-ScheduledTask -Action $action -Trigger $trigger -TaskName $TaskName -Description "PSMouseJiggler Task" Write-Host "Scheduled task '$TaskName' created successfully" -ForegroundColor Green } catch { Write-Error "Error creating 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-ScheduledTask -TaskName "MyJiggler" Removes the MyJiggler scheduled task. #> function Remove-ScheduledTask { [CmdletBinding()] param ( [Parameter(Mandatory)] [string]$TaskName ) try { Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false Write-Host "Scheduled task '$TaskName' removed successfully" -ForegroundColor Green } catch { Write-Error "Error removing 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-ScheduledTask -TaskName "MyJiggler" Starts the MyJiggler scheduled task. #> function Start-ScheduledTask { [CmdletBinding()] param ( [Parameter(Mandatory)] [string]$TaskName ) try { Start-ScheduledTask -TaskName $TaskName Write-Host "Scheduled task '$TaskName' started successfully" -ForegroundColor Green } catch { Write-Error "Error starting 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-ScheduledTask -TaskName "MyJiggler" Stops the MyJiggler scheduled task. #> function Stop-ScheduledTask { [CmdletBinding()] param ( [Parameter(Mandatory)] [string]$TaskName ) try { Stop-ScheduledTask -TaskName $TaskName Write-Host "Scheduled task '$TaskName' stopped successfully" -ForegroundColor Green } catch { Write-Error "Error stopping 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" } <# .SYNOPSIS Keeps the system awake using multiple methods. .DESCRIPTION Combines various techniques to prevent system sleep, including mouse movements, keyboard input, and Windows API calls. .PARAMETER Methods Array of methods to use. Default includes all available methods. .PARAMETER Interval Time in milliseconds between actions. Default is 30000 (30 seconds). .PARAMETER Duration Duration in seconds to run. Default is 0 (indefinite). .EXAMPLE Start-KeepAwake -Interval 60000 -Duration 3600 Keeps the system awake for 1 hour, performing actions every 60 seconds. #> function Start-KeepAwake { [CmdletBinding()] param ( [Parameter()] [ValidateSet('MouseSoftware', 'MouseHardware', 'Keyboard', 'SystemAPI', 'All')] [string[]]$Methods = @('All'), [Parameter()] [int]$Interval = 30000, [Parameter()] [int]$Duration = 0 ) if ($script:JigglingActive) { Write-Warning "PSMouseJiggler is already running. Use Stop-PSMouseJiggler to stop it first." return } Write-Host "Starting PSMouseJiggler KeepAwake with multiple methods, interval: $Interval ms" -ForegroundColor Green $script:JigglingActive = $true $startTime = Get-Date # If 'All' is specified, use all methods if ($Methods -contains 'All') { $Methods = @('MouseSoftware', 'MouseHardware', 'Keyboard', 'SystemAPI') } # Start the prevention immediately using the API if ($Methods -contains 'SystemAPI') { Prevent-SystemIdle } $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 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 $currentPos = [System.Windows.Forms.Cursor]::Position $xOffset = Get-Random -Minimum -10 -Maximum 11 $yOffset = Get-Random -Minimum -10 -Maximum 11 $newPos = [System.Drawing.Point]::new($currentPos.X + $xOffset, $currentPos.Y + $yOffset) [System.Windows.Forms.Cursor]::Position = $newPos # Move back to original position after a short delay to minimize disruption Start-Sleep -Milliseconds 100 [System.Windows.Forms.Cursor]::Position = $currentPos } '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) $result = [MouseSimulator]::SendInput(1, $inputArray, [System.Runtime.InteropServices.Marshal]::SizeOf([type][MouseSimulator+INPUT])) if ($result -eq 0) { Write-Warning "MouseSimulator::SendInput failed to send input event." } } '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 KeepAwake will run for $Duration seconds" -ForegroundColor Yellow } else { Write-Host "PSMouseJiggler KeepAwake is running indefinitely. Use Stop-PSMouseJiggler to stop." -ForegroundColor Yellow } } #endregion # Module cleanup when module is removed $MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = { if ($script:JigglingActive) { Stop-PSMouseJiggler } } # 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-RandomMovementPattern', 'Move-Mouse', 'Start-MovementPattern', 'Stop-MovementPattern', 'Get-ScheduledTasks', 'New-ScheduledTask', 'Remove-ScheduledTask', 'Start-ScheduledTask', 'Stop-ScheduledTask', # New functions 'Prevent-SystemIdle', 'Send-KeyboardInput', 'Send-MouseInput', 'Start-KeepAwake' ) |