2024-03-17 18:37:06 -07:00
using System ;
2024-02-05 23:08:37 -08:00
using System.Diagnostics ;
using System.IO ;
using System.Runtime.InteropServices ;
using System.Runtime.Versioning ;
using System.Text ;
2024-03-17 18:37:06 -07:00
using Avalonia ;
2024-03-27 21:08:04 -07:00
using Avalonia.Controls ;
2024-03-17 18:37:06 -07:00
using Avalonia.Media ;
namespace SourceGit.Native
{
2024-02-05 23:08:37 -08:00
[SupportedOSPlatform("windows")]
2024-03-17 18:37:06 -07:00
internal class Windows : OS . IBackend
{
2024-03-27 21:08:04 -07:00
[StructLayout(LayoutKind.Sequential)]
internal struct RTL_OSVERSIONINFOEX
{
internal uint dwOSVersionInfoSize ;
internal uint dwMajorVersion ;
internal uint dwMinorVersion ;
internal uint dwBuildNumber ;
internal uint dwPlatformId ;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
internal string szCSDVersion ;
}
[StructLayout(LayoutKind.Sequential)]
internal struct MARGINS
{
public int cxLeftWidth ;
public int cxRightWidth ;
public int cyTopHeight ;
public int cyBottomHeight ;
}
2024-02-05 23:08:37 -08:00
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode, SetLastError = false)]
private static extern bool PathFindOnPath ( [ In , Out ] StringBuilder pszFile , [ In ] string [ ] ppszOtherDirs ) ;
2024-03-27 21:08:04 -07:00
[DllImport("ntdll")]
private static extern int RtlGetVersion ( ref RTL_OSVERSIONINFOEX lpVersionInformation ) ;
[DllImport("dwmapi.dll")]
private static extern int DwmExtendFrameIntoClientArea ( IntPtr hwnd , ref MARGINS margins ) ;
2024-03-28 01:02:39 -07:00
[DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = false)]
private static extern IntPtr ILCreateFromPathW ( string pszPath ) ;
[DllImport("shell32.dll", SetLastError = false)]
private static extern void ILFree ( IntPtr pidl ) ;
[DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = false)]
private static extern int SHOpenFolderAndSelectItems ( IntPtr pidlFolder , int cild , IntPtr apidl , int dwFlags ) ;
2024-03-17 18:37:06 -07:00
public void SetupApp ( AppBuilder builder )
{
builder . With ( new FontManagerOptions ( )
{
2024-03-07 20:22:22 -08:00
DefaultFamilyName = "Microsoft YaHei UI" ,
2024-03-24 19:39:36 -07:00
FontFallbacks = [ new FontFallback { FontFamily = "Microsoft YaHei" } ] ,
2024-03-07 20:22:22 -08:00
} ) ;
2024-03-27 21:08:04 -07:00
// Fix drop shadow issue on Windows 10
RTL_OSVERSIONINFOEX v = new RTL_OSVERSIONINFOEX ( ) ;
v . dwOSVersionInfoSize = ( uint ) Marshal . SizeOf < RTL_OSVERSIONINFOEX > ( ) ;
if ( RtlGetVersion ( ref v ) = = 0 & & ( v . dwMajorVersion < 10 | | v . dwBuildNumber < 22000 ) )
{
Window . WindowStateProperty . Changed . AddClassHandler < Window > ( ( w , e ) = >
{
if ( w . WindowState ! = WindowState . Maximized )
{
var margins = new MARGINS { cxLeftWidth = 1 , cxRightWidth = 1 , cyTopHeight = 1 , cyBottomHeight = 1 } ;
DwmExtendFrameIntoClientArea ( w . TryGetPlatformHandle ( ) . Handle , ref margins ) ;
}
} ) ;
Window . LoadedEvent . AddClassHandler < Window > ( ( w , e ) = >
{
if ( w . WindowState ! = WindowState . Maximized )
{
var margins = new MARGINS { cxLeftWidth = 1 , cxRightWidth = 1 , cyTopHeight = 1 , cyBottomHeight = 1 } ;
DwmExtendFrameIntoClientArea ( w . TryGetPlatformHandle ( ) . Handle , ref margins ) ;
}
} ) ;
}
2024-03-07 20:22:22 -08:00
}
2024-03-17 18:37:06 -07:00
public string FindGitExecutable ( )
{
2024-02-05 23:08:37 -08:00
var reg = Microsoft . Win32 . RegistryKey . OpenBaseKey (
Microsoft . Win32 . RegistryHive . LocalMachine ,
Microsoft . Win32 . RegistryView . Registry64 ) ;
var git = reg . OpenSubKey ( "SOFTWARE\\GitForWindows" ) ;
2024-03-17 18:37:06 -07:00
if ( git ! = null )
{
2024-02-20 19:29:28 -08:00
return Path . Combine ( git . GetValue ( "InstallPath" ) as string , "bin" , "git.exe" ) ;
2024-02-05 23:08:37 -08:00
}
var builder = new StringBuilder ( "git.exe" , 259 ) ;
2024-03-17 18:37:06 -07:00
if ( ! PathFindOnPath ( builder , null ) )
{
2024-02-05 23:08:37 -08:00
return null ;
}
var exePath = builder . ToString ( ) ;
if ( string . IsNullOrEmpty ( exePath ) ) return null ;
2024-02-20 19:29:28 -08:00
return exePath ;
2024-02-05 23:08:37 -08:00
}
2024-03-17 18:37:06 -07:00
public string FindVSCode ( )
{
2024-02-05 23:08:37 -08:00
var root = Microsoft . Win32 . RegistryKey . OpenBaseKey (
Microsoft . Win32 . RegistryHive . LocalMachine ,
Environment . Is64BitOperatingSystem ? Microsoft . Win32 . RegistryView . Registry64 : Microsoft . Win32 . RegistryView . Registry32 ) ;
2024-03-28 02:42:13 -07:00
var vscode = root . OpenSubKey ( @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{EA457B21-F73E-494C-ACAB-524FDE069978}_is1" ) ;
2024-03-17 18:37:06 -07:00
if ( vscode ! = null )
{
2024-02-05 23:08:37 -08:00
return vscode . GetValue ( "DisplayIcon" ) as string ;
}
2024-03-27 23:48:20 -07:00
var toolPath = Environment . ExpandEnvironmentVariables ( $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}\\AppData\\Local\\Programs\\Microsoft VS Code\\Code.exe" ) ;
if ( File . Exists ( toolPath ) ) return toolPath ;
2024-03-27 22:49:32 -07:00
return string . Empty ;
}
2024-02-05 23:08:37 -08:00
2024-03-27 22:49:32 -07:00
public string FindFleet ( )
{
2024-03-27 23:48:20 -07:00
var toolPath = Environment . ExpandEnvironmentVariables ( $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}\\AppData\\Local\\Programs\\Fleet\\Fleet.exe" ) ;
if ( File . Exists ( toolPath ) ) return toolPath ;
2024-02-05 23:08:37 -08:00
return string . Empty ;
}
2024-03-17 18:37:06 -07:00
public void OpenBrowser ( string url )
{
2024-02-05 23:08:37 -08:00
var info = new ProcessStartInfo ( "cmd" , $"/c start {url}" ) ;
info . CreateNoWindow = true ;
Process . Start ( info ) ;
}
2024-03-17 18:37:06 -07:00
public void OpenTerminal ( string workdir )
{
2024-02-20 19:29:28 -08:00
var bash = Path . Combine ( Path . GetDirectoryName ( OS . GitInstallPath ) , "bash.exe" ) ;
2024-03-17 18:37:06 -07:00
if ( ! File . Exists ( bash ) )
{
2024-02-20 19:29:28 -08:00
App . RaiseException ( string . IsNullOrEmpty ( workdir ) ? "" : workdir , $"Can NOT found bash.exe under '{Path.GetDirectoryName(OS.GitInstallPath)}'" ) ;
2024-02-05 23:08:37 -08:00
return ;
}
var startInfo = new ProcessStartInfo ( ) ;
startInfo . UseShellExecute = true ;
startInfo . FileName = bash ;
if ( ! string . IsNullOrEmpty ( workdir ) & & Path . Exists ( workdir ) ) startInfo . WorkingDirectory = workdir ;
Process . Start ( startInfo ) ;
}
2024-03-17 18:37:06 -07:00
public void OpenInFileManager ( string path , bool select )
{
2024-03-01 01:40:17 -08:00
var fullpath = string . Empty ;
2024-03-17 18:37:06 -07:00
if ( File . Exists ( path ) )
{
2024-03-01 01:40:17 -08:00
fullpath = new FileInfo ( path ) . FullName ;
2024-03-28 01:02:39 -07:00
// For security reason, we never execute a file.
// Instead, we open the folder and select it.
select = true ;
2024-03-17 18:37:06 -07:00
}
else
{
2024-03-01 01:40:17 -08:00
fullpath = new DirectoryInfo ( path ) . FullName ;
}
2024-03-17 18:37:06 -07:00
if ( select )
{
2024-03-28 01:02:39 -07:00
// The fullpath here may be a file or a folder.
OpenFolderAndSelectFile ( fullpath ) ;
2024-03-17 18:37:06 -07:00
}
else
{
2024-03-28 01:02:39 -07:00
// The fullpath here is always a folder.
Process . Start ( new ProcessStartInfo ( fullpath )
{
UseShellExecute = true ,
CreateNoWindow = true ,
} ) ;
}
}
private static void OpenFolderAndSelectFile ( string folderPath )
{
var pidl = ILCreateFromPathW ( folderPath ) ;
try
{
SHOpenFolderAndSelectItems ( pidl , 0 , 0 , 0 ) ;
}
finally
{
ILFree ( pidl ) ;
2024-02-05 23:08:37 -08:00
}
}
2024-03-17 18:37:06 -07:00
public void OpenWithDefaultEditor ( string file )
{
2024-03-01 01:40:17 -08:00
var info = new FileInfo ( file ) ;
var start = new ProcessStartInfo ( "cmd" , $"/c start {info.FullName}" ) ;
start . CreateNoWindow = true ;
Process . Start ( start ) ;
2024-02-05 23:08:37 -08:00
}
}
2024-03-17 18:37:06 -07:00
}