Last active
December 22, 2024 08:33
-
-
Save amorphobia/e23169b30973954709c8f74efa49708f to your computer and use it in GitHub Desktop.
Desktop clock by PowerShell + .Net Framework + WPF
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# : | |
@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