diff --git a/winfetch.ps1 b/winfetch.ps1 index 114f2dd..d7894df 100644 --- a/winfetch.ps1 +++ b/winfetch.ps1 @@ -63,6 +63,8 @@ .NOTES Run Winfetch without arguments to view core functionality. #> + +using namespace System.Collections.Generic; [CmdletBinding()] param( [string][alias('i')]$image, @@ -89,84 +91,6 @@ if (-not ($IsWindows -or $PSVersionTable.PSVersion.Major -eq 5)) { exit 1 } -$e = [char]0x1B -$ansiRegex = '([\u001B\u009B][[\]()#;?]*(?:(?:(?:[a-zA-Z\d]*(?:;[-a-zA-Z\d\/#&.:=?%@~_]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-ntqry=><~])))' -$chars = ' .,:;+iIH$@' - -if (-not $configPath) { - if ($env:WINFETCH_CONFIG_PATH) { - $configPath = $env:WINFETCH_CONFIG_PATH - } else { - $configPath = "${env:USERPROFILE}\.config\winfetch\config.ps1" - } -} - -# function to generate percentage bars -function get_percent_bar { - param ([Parameter(Mandatory)][ValidateRange(0, 100)][int]$percent) - - $x = [char]9632 - $bar = $null - - $bar += "$e[97m[ $e[0m" - for ($i = 1; $i -le ($barValue = ([math]::round($percent / 10))); $i++) { - if ($i -le 6) { $bar += "$e[32m$x$e[0m" } - elseif ($i -le 8) { $bar += "$e[93m$x$e[0m" } - else { $bar += "$e[91m$x$e[0m" } - } - for ($i = 1; $i -le (10 - $barValue); $i++) { $bar += "$e[97m-$e[0m" } - $bar += "$e[97m ]$e[0m" - - return $bar -} - -function get_level_info { - param ( - [string]$barprefix, - [string]$style, - [int]$percentage, - [string]$text, - [switch]$altstyle - ) - - switch ($style) { - 'bar' { return "$barprefix$(get_percent_bar $percentage)" } - 'textbar' { return "$text $(get_percent_bar $percentage)" } - 'bartext' { return "$barprefix$(get_percent_bar $percentage) $text" } - default { if ($altstyle) { return "$percentage% ($text)" } else { return "$text ($percentage%)" }} - } -} - -function truncate_line { - param ( - [string]$text, - [int]$maxLength - ) - $length = ($text -replace $ansiRegex, "").Length - if ($length -le $maxLength) { - return $text - } - $truncateAmt = $length - $maxLength - $trucatedOutput = "" - $parts = $text -split $ansiRegex - - for ($i = $parts.Length - 1; $i -ge 0; $i--) { - $part = $parts[$i] - if (-not $part.StartsWith([char]27) -and $truncateAmt -gt 0) { - $num = if ($truncateAmt -gt $part.Length) { - $part.Length - } else { - $truncateAmt - } - $truncateAmt -= $num - $part = $part.Substring(0, $part.Length - $num) - } - $trucatedOutput = "$part$trucatedOutput" - } - - return $trucatedOutput -} - # ===== DISPLAY HELP ===== if ($help) { if (Get-Command -Name less -ErrorAction Ignore) { @@ -178,80 +102,7 @@ if ($help) { } -# ===== VARIABLES ===== -$cimSession = New-CimSession -$buildVersion = "$([System.Environment]::OSVersion.Version)" -$os = Get-CimInstance -ClassName Win32_OperatingSystem -Property Caption,OSArchitecture -CimSession $cimSession -$GAP = 3 -Add-Type -TypeDefinition @' -using System; -using System.ComponentModel; -using System.Runtime.InteropServices; -using System.Text; - -namespace WinAPI -{ - public class DiskMethods - { - [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "GetLogicalDriveStringsW", SetLastError = true)] - private static extern int NativeGetLogicalDriveStringsW( - int nBufferLength, - char[] lpBuffer); - - // Wrapper around the native function for error handling - public static char[] GetLogicalDriveStringsW() - { - int length = NativeGetLogicalDriveStringsW(0, null); - if (length == 0) - throw new Win32Exception(); - - char[] buffer = new char[length]; - length = NativeGetLogicalDriveStringsW(length, buffer); - if (length == 0) - throw new Win32Exception(); - - return buffer; - } - - [DllImport("Kernel32.dll", SetLastError = true)] - public static extern bool GetDiskFreeSpaceEx( - string lpDirectoryName, - out ulong lpFreeBytesAvailable, - out ulong lpTotalNumberOfBytes, - out ulong lpTotalNumberOfFreeBytes); - } -} -'@ - -# ===== CONFIGURATION ===== -$baseConfig = @( - "title" - "dashes" - "os" - "computer" - "kernel" - "motherboard" - "uptime" - "resolution" - "ps_pkgs" - "pkgs" - "pwsh" - "terminal" - "theme" - "cpu" - "gpu" - "cpu_usage" - "memory" - "disk" - "battery" - "locale" - "weather" - "local_ip" - "public_ip" - "blank" - "colorbar" -) - +# ===== CONFIG MANAGEMENT ===== $defaultConfig = @' # ===== WINFETCH CONFIGURATION ===== @@ -346,6 +197,14 @@ $defaultConfig = @' '@ +if (-not $configPath) { + if ($env:WINFETCH_CONFIG_PATH) { + $configPath = $env:WINFETCH_CONFIG_PATH + } else { + $configPath = "${env:USERPROFILE}\.config\winfetch\config.ps1" + } +} + # generate default config if ($genconf -and (Test-Path $configPath)) { $choiceYes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", ` @@ -369,9 +228,34 @@ if (-not (Test-Path $configPath) -or [String]::IsNullOrWhiteSpace((Get-Content $ # load config file $config = . $configPath - if (-not $config -or $all) { - $config = $baseConfig + $config = @( + "title" + "dashes" + "os" + "computer" + "kernel" + "motherboard" + "uptime" + "resolution" + "ps_pkgs" + "pkgs" + "pwsh" + "terminal" + "theme" + "cpu" + "gpu" + "cpu_usage" + "memory" + "disk" + "battery" + "locale" + "weather" + "local_ip" + "public_ip" + "blank" + "colorbar" + ) } # prevent config from overriding specified parameters @@ -379,35 +263,100 @@ foreach ($param in $PSBoundParameters.Keys) { Set-Variable $param $PSBoundParameters[$param] } -# convert old config style -if ($config.GetType() -eq [string]) { - $oldConfig = $config.ToLower() - $config = $baseConfig | Where-Object { $oldConfig.Contains($PSItem) } - $config += @("blank", "colorbar") -} - +# ===== VARIABLES ===== +$e = [char]0x1B +$ansiRegex = '([\u001B\u009B][[\]()#;?]*(?:(?:(?:[a-zA-Z\d]*(?:;[-a-zA-Z\d\/#&.:=?%@~_]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-ntqry=><~])))' +$cimSession = New-CimSession +$os = Get-CimInstance -ClassName Win32_OperatingSystem -Property Caption,OSArchitecture -CimSession $cimSession $t = if ($blink) { "5" } else { "1" } $COLUMNS = $imgwidth +# ===== UTILITY FUNCTIONS ===== +function get_percent_bar { + param ([Parameter(Mandatory)][ValidateRange(0, 100)][int]$percent) + + $x = [char]9632 + $bar = $null + + $bar += "$e[97m[ $e[0m" + for ($i = 1; $i -le ($barValue = ([math]::round($percent / 10))); $i++) { + if ($i -le 6) { $bar += "$e[32m$x$e[0m" } + elseif ($i -le 8) { $bar += "$e[93m$x$e[0m" } + else { $bar += "$e[91m$x$e[0m" } + } + for ($i = 1; $i -le (10 - $barValue); $i++) { $bar += "$e[97m-$e[0m" } + $bar += "$e[97m ]$e[0m" + + return $bar +} + +function get_level_info { + param ( + [string]$barprefix, + [string]$style, + [int]$percentage, + [string]$text, + [switch]$altstyle + ) + + switch ($style) { + 'bar' { return "$barprefix$(get_percent_bar $percentage)" } + 'textbar' { return "$text $(get_percent_bar $percentage)" } + 'bartext' { return "$barprefix$(get_percent_bar $percentage) $text" } + default { if ($altstyle) { return "$percentage% ($text)" } else { return "$text ($percentage%)" }} + } +} + +function truncate_line { + param ( + [string]$text, + [int]$maxLength + ) + $length = ($text -replace $ansiRegex, "").Length + if ($length -le $maxLength) { + return $text + } + $truncateAmt = $length - $maxLength + $trucatedOutput = "" + $parts = $text -split $ansiRegex + + for ($i = $parts.Length - 1; $i -ge 0; $i--) { + $part = $parts[$i] + if (-not $part.StartsWith([char]27) -and $truncateAmt -gt 0) { + $num = if ($truncateAmt -gt $part.Length) { + $part.Length + } else { + $truncateAmt + } + $truncateAmt -= $num + $part = $part.Substring(0, $part.Length - $num) + } + $trucatedOutput = "$part$trucatedOutput" + } + + return $trucatedOutput +} + # ===== IMAGE ===== $img = if (-not $noimage) { if ($image) { if ($image -eq 'wallpaper') { $image = (Get-ItemProperty -Path 'HKCU:\Control Panel\Desktop' -Name Wallpaper).Wallpaper } - if (-not (Test-Path -path $image)) { - Write-Error 'Specified image or wallpaper does not exist.' - exit 1 - } Add-Type -AssemblyName 'System.Drawing' - $OldImage = New-Object System.Drawing.Bitmap -ArgumentList (Resolve-Path $image).Path + $OldImage = if (Test-Path $image -PathType Leaf) { + [Drawing.Bitmap]::FromFile((Resolve-Path $image)) + } else { + [Drawing.Bitmap]::FromStream((Invoke-WebRequest $image -UseBasicParsing).RawContentStream) + } # Divide scaled height by 2.2 to compensate for ASCII characters being taller than they are wide [int]$ROWS = $OldImage.Height / $OldImage.Width * $COLUMNS / $(if ($ascii) { 2.2 } else { 1 }) $Bitmap = New-Object System.Drawing.Bitmap @($OldImage, [Drawing.Size]"$COLUMNS,$ROWS") if ($ascii) { + $chars = ' .,:;+iIH$@' for ($i = 0; $i -lt $Bitmap.Height; $i++) { $currline = "" for ($j = 0; $j -lt $Bitmap.Width; $j++) { @@ -602,7 +551,7 @@ function info_computer { function info_kernel { return @{ title = "Kernel" - content = $buildVersion + content = "$([System.Environment]::OSVersion.Version)" } } @@ -625,14 +574,92 @@ function info_uptime { # ===== RESOLUTION ===== function info_resolution { - Add-Type -AssemblyName System.Windows.Forms - $displays = foreach ($monitor in [System.Windows.Forms.Screen]::AllScreens) { - "$($monitor.Bounds.Size.Width)x$($monitor.Bounds.Size.Height)" + Add-Type -AssemblyName System.Core + Add-Type -AssemblyName System.Runtime.InteropServices + $id = Get-Random + $code = @" +using System.Runtime.InteropServices; +using System.Collections.Generic; +using System; +using System.Linq; +public class Pinvoke$id +{ + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + struct MONITORINFOEX + { + public int Size; + public Rect Monitor; + public Rect WorkArea; + public uint Flags; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + public string DeviceName; + } + + [DllImport("Shcore.dll")] + static public extern bool SetProcessDpiAwareness(int value); + + [DllImport("user32.dll")] + static extern bool EnumDisplayMonitors( + IntPtr hdc, + IntPtr lprcClip, + EnumMonitorsDelegate lpfnEnum, + IntPtr dwData + ); + + [DllImport("user32.dll", CharSet = CharSet.Auto)] + static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFOEX lpmi); + + [StructLayout(LayoutKind.Sequential)] + public struct Rect + { + public int left; + public int top; + public int right; + public int bottom; + } + + delegate bool EnumMonitorsDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr dwData); + + static private List m_displays = new List(); + + static bool EnumProc(IntPtr hMonitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr dwData) + { + MONITORINFOEX mONITORINFOEX = new MONITORINFOEX(); + mONITORINFOEX.Size = Marshal.SizeOf(typeof(MONITORINFOEX)); + GetMonitorInfo(hMonitor, ref mONITORINFOEX); + + var displayInfo = new DisplayInfo(); + displayInfo.X = (int)((mONITORINFOEX.Monitor.right - mONITORINFOEX.Monitor.left)); + displayInfo.Y = (int)((mONITORINFOEX.Monitor.bottom - mONITORINFOEX.Monitor.top)); + + m_displays.Add(displayInfo); + return true; + } + + public class DisplayInfo + { + public int X; + public int Y; + } + + public static string GetResult() + { + SetProcessDpiAwareness(2); + EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, EnumProc, IntPtr.Zero); + return String.Join(", ", m_displays.Select(displayInfo => displayInfo.X.ToString() + " x " + displayInfo.Y.ToString())); } + public static void Main() + { + GetResult(); + } +} +"@ + Add-Type -TypeDefinition $code + $result = iex "[Pinvoke$id]::GetResult()" return @{ title = "Resolution" - content = $displays -join ', ' + content = $result } } @@ -737,6 +764,45 @@ function info_memory { # ===== DISK USAGE ===== function info_disk { [System.Collections.ArrayList]$lines = @() + Add-Type @' +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; +using System.Text; + +namespace WinAPI +{ + public class DiskMethods + { + [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "GetLogicalDriveStringsW", SetLastError = true)] + private static extern int NativeGetLogicalDriveStringsW( + int nBufferLength, + char[] lpBuffer); + + // Wrapper around the native function for error handling + public static char[] GetLogicalDriveStringsW() + { + int length = NativeGetLogicalDriveStringsW(0, null); + if (length == 0) + throw new Win32Exception(); + + char[] buffer = new char[length]; + length = NativeGetLogicalDriveStringsW(length, buffer); + if (length == 0) + throw new Win32Exception(); + + return buffer; + } + + [DllImport("Kernel32.dll", SetLastError = true)] + public static extern bool GetDiskFreeSpaceEx( + string lpDirectoryName, + out ulong lpFreeBytesAvailable, + out ulong lpTotalNumberOfBytes, + out ulong lpTotalNumberOfFreeBytes); + } +} +'@ function to_units($value) { if ($value -gt 1tb) { @@ -855,14 +921,9 @@ function info_pkgs { } if ("scoop" -in $ShowPkgs) { - if (Test-Path "~/scoop/apps") { - $scoopdir = "~/scoop/apps" - } elseif (Get-Command -Name scoop -ErrorAction Ignore) { - $scoop = & scoop which scoop.ps1 - $scoopdir = (Resolve-Path "$(Split-Path -Path $scoop)\..\..\..").Path - } + $scoopdir = if ($Env:SCOOP) { "$Env:SCOOP\apps" } else { "$Env:UserProfile\scoop\apps" } - if ($scoopdir) { + if (Test-Path $scoopdir) { $scooppkg = (Get-ChildItem -Path $scoopdir -Directory).Count - 1 } @@ -1005,6 +1066,7 @@ if (-not $stripansi) { } } +$GAP = 3 $writtenLines = 0 $freeSpace = $Host.UI.RawUI.WindowSize.Width - 1