ggps.psm1

using namespace System.Drawing
using namespace System.Windows.Forms

# Preload assemblies for standalone execution outside manifest
Add-Type -AssemblyName System.Windows.Forms, System.Drawing -ErrorAction SilentlyContinue

# Nuke invalid SDK paths in LIB that break csc.exe compiler in Windows PowerShell
$env:LIB = ''

$m = @'
    [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    public static extern void mouse_event(long dwFlags, long dx, long dy, long cButtons, long dwExtraInfo);
'@


$SendMouseClick = Add-Type -MemberDefinition $m -Name 'Win32MouseEvent' -Namespace Win32Functions -PassThru

function gg {
    <#
    .SYNOPSIS
        Simulates subtle mouse clicks to prevent idle timeouts and session lockouts.

    .DESCRIPTION
        Monitors mouse cursor coordinates and sends left-click down/up events with randomized delays.
        Automatically stops if any key is pressed or if significant mouse movement is detected.

    .PARAMETER MinDelayMs
        Minimum delay in milliseconds between mouse down and up events. Default is 125 ms.

    .PARAMETER MaxDelayMs
        Maximum delay in milliseconds between mouse down and up events. Default is 501 ms.

    .PARAMETER MovementThreshold
        Maximum allowed mouse movement in pixels before the loop exits. Default is 5 pixels.

    .EXAMPLE
        gg
    #>

    [CmdletBinding()]
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '')]
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingEmptyCatchBlock', '')]
    param (
        [Parameter()]
        [int]$MinDelayMs = 125,

        [Parameter()]
        [int]$MaxDelayMs = 501,

        [Parameter()]
        [int]$MovementThreshold = 5
    )

    $iX = [Windows.Forms.Cursor]::Position.X
    $iY = [Windows.Forms.Cursor]::Position.Y

    Write-Host "Press any key or move the mouse excessively to stop."

    while ($true) {
        try {
            if (-not [Console]::IsInputRedirected -and [Console]::KeyAvailable) {
                $null = [Console]::ReadKey($true)
                Write-Host "`nstopped"
                break
            }
        } catch {
            # Stdin redirected or non-interactive console
        }

        $cMX = [Windows.Forms.Cursor]::Position.X
        $cMY = [Windows.Forms.Cursor]::Position.Y
        if ([Math]::Abs($cMX - $iX) -gt $MovementThreshold -or [Math]::Abs($cMY - $iY) -gt $MovementThreshold) {
            Write-Host "`nexcessive mouse movement"
            break
        }

        $SendMouseClick::mouse_event(0x00000002, 0, 0, 0, 0)
        Start-Sleep -Milliseconds (Get-Random -Minimum $MinDelayMs -Maximum $MaxDelayMs)
        $SendMouseClick::mouse_event(0x00000004, 0, 0, 0, 0)
        Start-Sleep -Milliseconds (Get-Random -Minimum $MinDelayMs -Maximum $MaxDelayMs)
    }
}

Export-ModuleMember -Function gg

# Run 'gg' automatically on interactive module import
if ([Environment]::UserInteractive -and -not [Console]::IsInputRedirected -and -not $env:CI -and -not $env:GGPS_SKIP_AUTO_RUN) {
    & (Get-Command -Name gg -Module ggps -ErrorAction SilentlyContinue)
}