Skip to content

Instantly share code, notes, and snippets.

@amorphobia
Last active December 22, 2024 08:33
Show Gist options
  • Save amorphobia/e23169b30973954709c8f74efa49708f to your computer and use it in GitHub Desktop.
Save amorphobia/e23169b30973954709c8f74efa49708f to your computer and use it in GitHub Desktop.
Desktop clock by PowerShell + .Net Framework + WPF
<# :
@echo off
start /b powershell /windowstyle hidden /nologo /noprofile /command ^
"&{[ScriptBlock]::Create((cat """%~f0""") -join [Char[]]10).Invoke(@(&{$args}%*))}"
exit /b
#>
param(
[Parameter(Mandatory=$false)]
[string]$CultureName,
[Parameter(Mandatory=$false)]
[string]$DarkMode
)
function Get-ClockXAML {
return [xml]@"
<Window xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Background = "#cceed0"
Title = "Clock" Width = "640" Height = "480">
<Window.TaskbarItemInfo>
<TaskbarItemInfo />
</Window.TaskbarItemInfo>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width = "Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height = "Auto" />
<RowDefinition Height = "Auto" />
<RowDefinition Height = "Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Name = "Label" FontSize = "30"
Margin = "16,0,0,0"
Grid.Row = "1" Grid.Column = "1"
Text = "Current time" />
<TextBlock Name = "Time" FontSize = "144"
FontWeight = "Bold"
HorizontalAlignment = "Center"
VerticalAlignment = "Center"
Margin = "5,0,0,0"
Grid.Row = "2" Grid.Column = "1"
Text = "00:00:00" />
<TextBlock Name = "Date" FontSize = "32"
HorizontalAlignment = "Right"
Margin = "0,0,16,0"
Grid.Row = "3" Grid.Column = "1"
Text = "1970-01-01" />
</Grid>
</Window>
"@
}
function Add-Dependancies {
$Drawing_Assembly = "System.Drawing"
if ($host.version -ge "7.0") {
$Drawing_Assembly += ".Common"
}
$Signature = @{
Namespace = "WinAPI"
Name = "Utils"
Language = "CSharp"
UsingNamespace = "System.Runtime", "System.IO", "System.Text", "System.Drawing", "System.Globalization"
ReferencedAssemblies = $Drawing_Assembly
ErrorAction = "Stop"
WarningAction = "Ignore"
MemberDefinition = @"
[DllImport("user32.dll")]
internal static extern bool ShowWindow(IntPtr handle, int state);
[DllImport("shell32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]
internal static extern int ExtractIconEx(string sFile, int iIndex, out IntPtr piLargeVersion, out IntPtr piSmallVersion, int amountIcons);
public static void HideConsole(IntPtr handle) {
ShowWindow(handle, 0);
}
public static Icon ExtractIcon(string file, int number, bool largeIcon) {
IntPtr large, small;
ExtractIconEx(file, number, out large, out small, 1);
try {
return Icon.FromHandle(largeIcon ? large : small);
} catch {
return null;
}
}
"@
}
if (!("WinAPI.Utils" -as [type])) {
Add-Type @Signature
}
Add-Type -AssemblyName PresentationFramework
}
function ConvertTo-ImageSource {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
[System.Drawing.Icon]$Icon
)
Process {
foreach ($i in $Icon) {
[System.Windows.Interop.Imaging]::CreateBitmapSourceFromHIcon(
$i.Handle,
(New-Object System.Windows.Int32Rect -Args 0,0,$i.Width, $i.Height),
[System.Windows.Media.Imaging.BitmapSizeOptions]::FromEmptyOptions()
)
}
}
}
function Set-ToFullScreen {
param (
[Parameter(Mandatory)][Object]$Window
)
$Window.ResizeMode = "NoResize"
$Window.WindowStyle = "None"
$Window.WindowState = "Maximized"
}
function Set-ToWindowed {
param (
[Parameter(Mandatory)][Object]$Window
)
$Window.ResizeMode = "CanResize"
$Window.WindowStyle = "SingleBorderWindow"
$Window.WindowState = "Normal"
}
function Start-Clock {
param (
[Parameter(Mandatory=$false)][Object]$Culture,
[Parameter(Mandatory=$false)][string]$DarkMode
)
try {
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
} catch {}
Add-Dependancies
# [WinAPI.Utils]::HideConsole(([System.Diagnostics.Process]::GetCurrentProcess() | Get-Process).MainWindowHandle)
$ClockXAML = Get-ClockXAML
$Clock = [Windows.Markup.XamlReader]::Load((New-Object System.Xml.XmlNodeReader $ClockXAML))
$ClockXAML.SelectNodes("//*[@Name]") | ForEach-Object {
Set-Variable -Name "Clock_$($_.Name)" -Value $Clock.FindName($_.Name) -Scope Script
}
$WinVer = [System.Environment]::OSVersion.Version
if ($Winver -ge [System.Version]"10.0.22000") {
$IconIndex = 249
} else {
$IconIndex = 265
}
$bitmap = [WinAPI.Utils]::ExtractIcon("shell32.dll", $IconIndex, $true) | ConvertTo-ImageSource
$Clock.Icon = $bitmap
$Clock.TaskbarItemInfo.Overlay = $bitmap
$Clock.TaskbarItemInfo.Description = $Clock.Title
$MouseDownPoint = $null
$DayFormat = " dddd"
Switch -Regex ($Culture.Name) {
"de|de-DE" {
$Clock_Label.Text = "Aktuelle zeit"
$DayFormat = ""
}
"en|en-GB|en-HK|en-MO|en-US" {
$Clock_Time.FontSize = "96"
$DayFormat = ""
}
"fr|fr-FR" {
$Clock_Label.Text = "Heure actuelle"
$DayFormat = ""
}
"ja|ja-JP" {
$Clock_Label.Text = "$([char]0x73fe)$([char]0x5728)$([char]0x6642)$([char]0x523b)"
}
"ru|ru-RU" {
$Clock_Label.Text = "$([char]0x0422)$([char]0x043e)$([char]0x0447)$([char]0x043d)$([char]0x043e)$([char]0x0435) $([char]0x0432)$([char]0x0440)$([char]0x0435)$([char]0x043c)$([char]0x044f)"
}
"zh|zh-CHS|zh-CN|zh-Hans|zh-Hans-HK|zh-Hans-MO|zh-SG" {
$Clock_Label.Text = "$([char]0x5f53)$([char]0x524d)$([char]0x65f6)$([char]0x95f4)"
}
"zh-CHT|zh-Hant|zh-HK|zh-MO|zh-TW" {
$Clock_Label.Text = "$([char]0x7576)$([char]0x524d)$([char]0x6642)$([char]0x9593)"
}
}
Switch -Regex ($DarkMode) {
"^[DTYdty]" {
$Clock.Background = "Black"
$Clock_Label.Foreground = "#cceed0"
$Clock_Time.Foreground = "#cceed0"
$Clock_Date.Foreground = "#cceed0"
}
}
$Clock.Add_KeyDown({
param (
[Parameter(Mandatory)][Object]$sender,
[Parameter(Mandatory)][Windows.Input.KeyEventArgs]$e
)
if ($e.Key -eq "Escape") {
if ($Clock.WindowStyle -ne "SingleBorderWindow") {
Set-ToWindowed $Clock
} else {
$Clock.Close()
}
}
if ($e.Key -eq "F11") {
if ($Clock.WindowStyle -ne "SingleBorderWindow") {
Set-ToWindowed $Clock
} else {
Set-ToFullScreen $Clock
}
}
})
$Clock.Add_MouseDoubleClick({
param (
[Parameter(Mandatory)][Object]$sender,
[Parameter(Mandatory)][Windows.Input.MouseEventArgs]$e
)
if ($e.LeftButton -ne 1) {
return
}
if ($Clock.WindowStyle -ne "SingleBorderWindow") {
Set-ToWindowed $Clock
} else {
Set-ToFullScreen $Clock
}
})
$Clock.Add_MouseLeftButtonDown({
param (
[Parameter(Mandatory)][Object]$sender,
[Parameter(Mandatory)][Windows.Input.MouseButtonEventArgs]$e
)
if ($e.ClickCount -ne 1) {
return
}
$MouseDownPoint = $Clock.PointToScreen($e.GetPosition($Clock))
})
$Clock.Add_MouseMove({
param (
[Parameter(Mandatory)][Object]$sender,
[Parameter(Mandatory)][Windows.Input.MouseEventArgs]$e
)
if ($e.LeftButton -ne [System.Windows.Input.MouseButtonState]::Pressed) {
return
}
$MouseCurrentPoint = $Clock.PointToScreen($e.GetPosition($Clock))
$xDrag = [System.Math]::Abs($MouseCurrentPoint.X - $MouseDownPoint.X) -ge [System.Windows.SystemParameters]::MinimumHorizontalDragDistance
$yDrag = [System.Math]::Abs($MouseCurrentPoint.Y - $MouseDownPoint.Y) -ge [System.Windows.SystemParameters]::MinimumVerticalDragDistance
if ($xDrag -or $yDrag) {
$Clock.DragMove()
}
})
$dispatcherTimer = [System.Windows.Threading.DispatcherTimer]::new()
$dispatcherTimer.Interval = [timespan]::FromSeconds(0.1)
$dispatcherTimer.Add_Tick({
$Clock.Title = Get-Date -Format $Culture.DateTimeFormat.ShortTimePattern
$Clock_Time.Text = Get-Date -Format $Culture.DateTimeFormat.LongTimePattern
$Clock_Date.Text = Get-Date -Format "$($Culture.DateTimeFormat.LongDatePattern)$DayFormat"
})
$Clock.Add_Loaded({
$Clock.Activate()
$dispatcherTimer.Start()
})
$Clock.ShowDialog() | Out-Null
Get-Variable Location
}
function Set-ThreadCulture {
param (
[Parameter(Mandatory)]
[System.Globalization.CultureInfo]$CultureInfo
)
[System.Threading.Thread]::CurrentThread.CurrentUICulture = $CultureInfo
[System.Threading.Thread]::CurrentThread.CurrentCulture = $CultureInfo
}
if ([string]::IsNullOrEmpty($CultureName)) {
$CultureName = (Get-Culture).Name
}
try {
$CultureInfo = [System.Globalization.CultureInfo]::GetCultureInfo($CultureName)
}
catch {
$DarkMode = $CultureName
$CultureName = (Get-Culture).Name
$CultureInfo = [System.Globalization.CultureInfo]::GetCultureInfo($CultureName)
}
Set-ThreadCulture $CultureInfo
Start-Clock -Culture $CultureInfo -DarkMode $DarkMode
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment