functions/ConsoleClock.ps1

#functions for displaying a clock in a PowerShell console session

#region Exported functions

function Start-ConsoleClock {
    [cmdletbinding(SupportsShouldProcess)]
    [OutputType('none')]
    [alias('scc')]
    param(
        [Parameter(
            Position = 0,
            HelpMessage = 'Specify the date time formatting.'
        )]
        [PSDefaultValue(Help = 'F')]
        [ValidateNotNullOrEmpty()]
        [string]$Format = 'F',

        [Parameter(HelpMessage = 'Specify an ANSI style or console color for the clock display.')]
        [PSDefaultValue(Help = 'Yellow')]
        [ValidateNotNullOrEmpty()]
        [string]$DisplayColor = 'Yellow',

        [Parameter(HelpMessage = 'Add a line border to the display.')]
        [switch]$Border,

        [Parameter(HelpMessage = 'Specify an ANSI or console color for the border.')]
        [ValidateNotNullOrEmpty()]
        [string]$BorderColor = 'Green'
    )

    begin {
        _verbose ($strings.Starting -f $MyInvocation.MyCommand)
        if ($MyInvocation.CommandOrigin -eq 'Runspace') {
            _verbose ($strings.Running -f $PSVersionTable.PSVersion)
            _verbose ($strings.UsingModule -f $modVersion)
            _verbose ($strings.Detected -f $Host.Name)
        }
        Write-Information $PSBoundParameters -Tags runtime
    } #begin

    process {
        if ($host.Name -ne 'ConsoleHost') {
            Write-Warning $strings.RequiresConsole
            return
        }
        _verbose ($strings.UsingDateFormat -f $Format)

        #create $global:consoleClockSettings
        $svParams = @{
            Name        = "consoleClockSettings"
            Scope       = "global"
            Description = "This is used by the PSClock module. Do not manually remove."
            Value       = [ordered]@{
                            Format       = $Format
                            DisplayColor = ConvertTo-AnsiColor $DisplayColor
                            Border       = $Border
                            BorderColor  = ConvertTo-AnsiColor $BorderColor
                            Note         = "This is used by the PSClock module. Do not manually remove."
                           }
        }
        Set-Variable @svParams
        Write-Information $global:consoleClockSettings -Tags runtime
        if ($script:clockEvent) {
            Write-Warning $strings.ExistingConsoleClock
        }
        else {
            #the action scriptblock needs to be self-contained or able to reference globally scoped items
            $action = {
                #if the global settings hashtable is removed, do not run and stop the clock
                if (-Not $global:consoleClockSettings.Format) {
                    Stop-ConsoleClock
                    return
                }

                #dot source the helper functions from the PSClock module if required
                $modPath = (Get-Module PSClock).path | Split-Path -Parent
                $funPath = Join-Path -Path $modPath -ChildPath 'functions\ConsoleClockHelpers.ps1'
                . $funPath
                if ($global:consoleClockSettings.Border) {
                    #add an offset if there is a Border
                    $offSet = 5
                }
                else {
                    $offset = 1
                }

                [string]$dt = Get-Date -Format $global:consoleClockSettings.Format
                $fmtDt = "{0}$dt{1}" -f (ConvertTo-AnsiColor $global:consoleClockSettings.DisplayColor), "$([char]27)[0m"

                #define the cursor position in console
                $here = $host.ui.RawUI.CursorPosition
                $x = $Host.UI.RawUI.WindowSize.Width - $dt.length - $offSet
                $consolePosition = [System.Management.Automation.Host.Coordinates]::new($x, 0)
                $host.ui.RawUI.CursorPosition = $consolePosition
                if ($global:consoleClockSettings.Border) {
                    Format-BorderBox -text $fmtDt -BorderColor (ConvertTo-AnsiColor $global:consoleClockSettings.BorderColor) -Position $consolePosition
                }
                else {
                    Write-Host $fmtDt -NoNewline
                }
                #reset the cursor position
                $host.ui.RawUI.CursorPosition = $here
                Start-Sleep -Milliseconds 100
            } #close action

            Write-Information $action
            _verbose $strings.RegisterConsoleClock
            #Register-EngineEvent doesn't natively support -WhatIf so I have to do it.
            if ($PSCmdlet.ShouldProcess("Get-Date -format $format")) {
                #hide the subscription
                Register-EngineEvent -SourceIdentifier PowerShell.OnIdle -Action $action -SupportEvent
                $script:clockEvent = Get-EventSubscriber -Force | Sort-Object SubscriptionId | Select-Object -Last 1
                _verbose ($strings.UsingSubscription -f $script:clockEvent.SubscriptionID)
            } #WhatIf
        }
    } #process

    end {
        _verbose ($strings.Ending -f $MyInvocation.MyCommand)
    } #end

} #close Start-ConsoleClock

function Stop-ConsoleClock {
    [cmdletbinding(SupportsShouldProcess)]
    [OutputType('none')]
    [alias('stcc')]
    param( )

    begin {
        _verbose ($strings.Starting -f $MyInvocation.MyCommand)
        if ($MyInvocation.CommandOrigin -eq 'Runspace') {
            _verbose ($strings.Running -f $PSVersionTable.PSVersion)
            _verbose ($strings.UsingModule -f $modVersion)
            _verbose ($strings.Detected -f $Host.Name)
        }
    } #begin

    process {
        _verbose $strings.StoppingConsole
        if ($script:clockEvent) {
            _verbose ($strings.Unregister -f $($script:clockEvent.SubscriptionId))
            Unregister-Event -SubscriptionId $script:clockEvent.SubscriptionId -Force
            Remove-Variable -Name clockEvent -Scope Script -Force -ErrorAction SilentlyContinue
            Remove-Variable -Name consoleClockSettings -Scope Global -Force -ErrorAction SilentlyContinue
        }
        else {
            Write-Warning $strings.NoConsoleClock
        }
    } #process

    end {
        _verbose ($strings.Ending -f $MyInvocation.MyCommand)
    } #end

} #close Stop-ConsoleClock

#endregion