I’ve always been jealous of XTerm’s uber-geeky console mode screensaver, CMatrix. It was written shortly after the Matrix came out about ten years ago, and when I first saw it I thought it was really cool. I accidentally ran into it again recently and thought: “Hey, I could write that for PowerShell.” So I did.
The Matrix animation code uses the RawUI interface and is probably not all that interesting. It uses a simple “game loop” routine, repeatedly calling a “Step” function on multiple instances of a custom object, each representing an animating vertical column of letters. Each column is a dynamic module instantiated using the “-ascustomobject” parameter to allow encapsulation of state. I had originally intended to just prototype it using immediate writes to the console buffer, then moving onto a “frame buffer” implementation whereby the entire screen would be rendered into a BufferCell[,] array with a single SetBufferContents each cycle. It turns out though that the simpler implementation runs fast enough so there’s no need to get fancy pants. The idle detection was a little trickier. It uses PowerShell 2.0’s Eventing feature to start an instance of System.Timers.Timer running in the background. Instead of starting and stopping the timer during idle detection, which is actually quite a sluggish operation, the timer’s Elapsed handler increments a global variable every second. Each time the prompt function executes, it resets the counter back to 0. If this variable reaches the timeout value (default 3 minutes) it will invoke the screen saver from within the event handler. This allows the screen saver to activate without any user interaction. When running in an Action handler like this, you have to use CTRL+C to exit it; for some reason the $host KeyAvailable property doesn’t work correctly in this context. If you manually invoke the screen saver with Start-ScreenSaver, you can hit any key to exit. There is also some extra state being kept to temporarily disable the timer when the screen saver is active, or if the user has issued Disable-ScreenSaver. The implementation is a PowerShell 2.0 Module named “ScreenSaver.” To set it up on your console (sorry, it doesn’t work in graphical shells like ISE, PowerGUI or PoshConsole) just put in the following three lines in your $profile after you have installed the module.
set-screensavertimeout (new-timespan -minutes 5)
There are a couple of self explanatory functions exported from the module: Enable-ScreenSaver, Disable-ScreenSaver, Set-ScreenSaverTimeout <timespan | int>, Get-ScreenSaverTimeout and Start-ScreenSaver. Remove-Module ScreenSaver will do the right thing and clean up, disposing the timer. By the way, it is possible to have implemented this without the user of global variables but it would have added more complexity for little gain.
View the source
on PoshCode. Normally I would paste the code inline, but there’s a bit more than my normal posts would have. Feel free to clone the PoshCode script and add your own screen saver patterns. Btw, you can tweak the number of columns and the animation speed by examining the Start-ScreenSaver function.