Experimenting with Monad’s MshHostRawUserInterface Class

Thu, Nov 17, 2005 3-minute read

As your scripts become more complex, it’s possible that you will have to interact with the user in a manner more complex than write- and read-host.  For example, you might decide to implement a text-based menu system, or even a game of Aliens.

To support this low-level access to the console buffer, hosts (for example, ours,) extend the MshHostRawUserInterface class.  This class provides most of the common operations required by a console-mode raw user interface, including window management, blitting, scrolling, and colour control.

The following script demonstrates some of the ways in which you can work with Monad’s Raw UI.  It illustrates:

  • Getting the coordinates of the viewport in relation to the window buffer
  • Getting the current cursor location
  • Obtaining direct access to the console’s buffer contents
  • Scrolling the buffer’s contents
  • Directly writing to the console’s buffer
  • Working within the constraints of the console’s current width and height
## scroll-buffer.msh  
## Demonstrate some of the features of the MshHostRawUserInterface class  
## Simply get a big screen full of junk, and run it.  ^C exits.  
  
function main  
{  
    ## Get the current coordinates of the view-port within the  
    ## screen buffer.  
    $currentCoordinatesX = $host.UI.RawUI.WindowPosition.X  
    $currentCoordinatesY = $host.UI.RawUI.WindowPosition.Y  
    $currentCursorY = $host.UI.RawUI.CursorPosition.Y - 1  
      
    ## Initialize a random number generator  
    $random = new-object Random  
  
    ## Initialize the scrolling rectangles  
    . initializeVerticalScrollRectangles  
    . initializeHorizontalScrollRectangle  
      
    ## Continuously scroll the rectangles until the user breaks out  
    $counter = 0  
    while($true)   
    {  
        ## We let each of the vertical scrolling rectangles scroll twice,  
        ## and then pick new dimensions and locations  
        if($counter -eq ((2 \* $rectHeight) + 2))   
        {  
            . initializeVerticalScrollRectangles  
            $counter = 0  
        }  
  
        $counter++  
  
        ## Sleep a bit so that it looks animated  
        start-sleep -m 50  
  
        ## Store the top line of the vertical scrolling rectangle,  
        ## scroll the rectangle up,  
        ## and fill the emptied space with the old top line.  
        $oldBufferLine = $host.UI.RawUI.GetBufferContents(\`  
            $verticalScrollTopLineRectangle)  
        $host.UI.RawUI.ScrollBufferContents(\`  
            $verticalScrollRectangle,\`  
            $verticalScrollTargetCoordinates,\`  
            $verticalScrollRectangle,\`  
            $cellType)  
        $host.UI.RawUI.SetBufferContents(\`  
            $verticalScrollBottomCoordinates$oldBufferLine)  
  
        ## Store the left cell of the horizontally scrolling line,  
        ## scroll the line left,  
        ## and fill the emptied space at the right with the old left cell.  
        $oldCell = $host.UI.RawUI.GetBufferContents(\`  
            $horizontalScrollRectangle)\[0,0\]  
        $host.UI.RawUI.ScrollBufferContents(\`  
            $horizontalScrollRectangle,\`  
            $horizontalScrollTargetCoordinates,\`  
            $horizontalScrollRectangle,\`  
            $oldCell)  
    }  
}  
  
function initializeVerticalScrollRectangles  
{  
    $cellType = new-object System.Management.Automation.Host.BufferCell \`  
        ".","White","Black","Complete"  
  
    ## Pick constrained random widths and heights  
    $rectWidth = \[int\] ($random.NextDouble() \* 38) + 2  
    $rectHeight = \[int\] ($random.NextDouble() \* 28) + 2  
  
    ## Decide where we want to position the rectangles  
    $xMax = $host.UI.RawUI.WindowSize.Width - $rectWidth  
    $yMax = $host.UI.RawUI.WindowSize.Height - $rectHeight - 4  
    $xOffset = \[int\] ($random.NextDouble() \* $xMax)  
    $yOffset = \[int\] ($random.NextDouble() \* $yMax)  
  
    ## Pick the rectangle that indicates the large vertical scrolling  
    ## rectangle region  
    $verticalS crollRectangle = \`  
        new-object System.Management.Automation.Host.Rectangle \`  
        ($currentCoordinatesX + $xOffset),\`  
        ($currentCoordinatesY + $yOffset),\`  
        ($currentCoordinatesX + $xOffset + $rectWidth),\`  
        ($currentCoordinatesY + $yOffset + $rectHeight)  
  
    ## The rectangle that indicates the top line of the large vertical  
    ## scrolling rectangle region  
    $verticalScrollTopLineRectangle = \`  
        new-object System.Management.Automation.Host.Rectangle \`  
        ($currentCoordinatesX + $xOffset),\`  
        ($currentCoordinatesY + $yOffset),\`  
        ($currentCoordinatesX + $xOffset + $rectWidth),\`  
        ($currentCoordinatesY + $yOffset)  
  
    ## The target of the scroll of the large vertical scrolling region.  
    ## This is simply one line up  
    $verticalScrollTargetCoordinates = \`  
        new-object System.Management.Automation.Host.Coordinates \`  
        ($currentCoordinatesX + $xOffset),\`  
        ($currentCoordinatesY + $yOffset - 1)  
  
    ## The bottom of the large vertical scrolling region.  This will be  
    ## used to fill later with the contents from the top  
    $verticalScrollBottomCoordinates = \`  
        new-object System.Management.Automation.Host.Coordinates \`  
        ($currentCoordinatesX + $xOffset),\`  
        ($currentCoordinatesY + $yOffset + $rectHeight)  
}  
  
function initializeHorizontalScrollRectangle  
{  
    $horizontalScrollRectangle = \`  
        new-object System.Management.Automation.Host.Rectangle \`  
        0,$currentCursorY,$host.UI.RawUI.WindowSize.Width,$currentCursorY  
  
    $horizontalScrollTargetCoordinates = \`  
        new-object System.Management.Automation.Host.Coordinates \`  
        -1,$currentCursorY  
}  
  
. main

[Edit: Monad has now been renamed to Windows PowerShell. This script or discussion may require slight adjustments before it applies directly to newer builds.]