- Enhanced Windows 11 native installer with GPU detection and DirectX 12 support - Add cross-platform GUI detection utilities for improved platform handling - Implement localization infrastructure foundation with i18n support - Create comprehensive cross-platform deployment guide - Add Inuktitut and Cree syllabics support framework - Enhanced Arch Linux installation with desktop environment detection - Implement Windows 11 DPI awareness and display scaling support - Add automated cross-platform testing framework
376 lines
11 KiB
Go
376 lines
11 KiB
Go
package utils
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"fyne.io/fyne/v2"
|
|
)
|
|
|
|
// min returns the minimum of two float64 values
|
|
func min(a, b float64) float64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
// abs returns the absolute value of an int
|
|
func abs(x int) int {
|
|
if x < 0 {
|
|
return -x
|
|
}
|
|
return x
|
|
}
|
|
|
|
// GUIEnvironment contains information about the user's desktop environment
|
|
type GUIEnvironment struct {
|
|
DisplayServer string // "x11", "wayland", "windows", "darwin"
|
|
DesktopEnvironment string // "gnome", "kde", "xfce", "windows11", "macos"
|
|
ScaleFactor float64 // Display scaling factor
|
|
PrimaryMonitor MonitorInfo
|
|
HasCompositing bool
|
|
GPUInfo GPUInfo
|
|
}
|
|
|
|
// MonitorInfo contains display monitor information
|
|
type MonitorInfo struct {
|
|
Width int
|
|
Height int
|
|
ScaleFactor float64
|
|
IsPrimary bool
|
|
}
|
|
|
|
// GPUInfo contains graphics card information
|
|
type GPUInfo struct {
|
|
Vendor string // "nvidia", "amd", "intel", "unknown"
|
|
Model string
|
|
Driver string
|
|
Supported bool // GPU acceleration support detected
|
|
}
|
|
|
|
// DetectGUIEnvironment performs comprehensive GUI environment detection
|
|
func DetectGUIEnvironment() GUIEnvironment {
|
|
env := GUIEnvironment{
|
|
ScaleFactor: 1.0,
|
|
GPUInfo: GPUInfo{Vendor: "unknown"},
|
|
}
|
|
|
|
switch runtime.GOOS {
|
|
case "linux":
|
|
env.detectLinuxGUI()
|
|
case "windows":
|
|
env.detectWindowsGUI()
|
|
case "darwin":
|
|
env.detectMacGUI()
|
|
}
|
|
|
|
return env
|
|
}
|
|
|
|
// detectLinuxGUI handles Linux-specific GUI detection
|
|
func (env *GUIEnvironment) detectLinuxGUI() {
|
|
// Display server detection
|
|
if os.Getenv("WAYLAND_DISPLAY") != "" {
|
|
env.DisplayServer = "wayland"
|
|
} else if os.Getenv("DISPLAY") != "" {
|
|
env.DisplayServer = "x11"
|
|
} else {
|
|
env.DisplayServer = "headless"
|
|
}
|
|
|
|
// Desktop environment detection
|
|
if desktop := os.Getenv("XDG_CURRENT_DESKTOP"); desktop != "" {
|
|
desktops := strings.ToLower(desktop)
|
|
if strings.Contains(desktops, "gnome") {
|
|
env.DesktopEnvironment = "gnome"
|
|
} else if strings.Contains(desktops, "kde") {
|
|
env.DesktopEnvironment = "kde"
|
|
} else if strings.Contains(desktops, "xfce") {
|
|
env.DesktopEnvironment = "xfce"
|
|
} else if strings.Contains(desktops, "sway") {
|
|
env.DesktopEnvironment = "sway"
|
|
} else if strings.Contains(desktops, "i3") {
|
|
env.DesktopEnvironment = "i3"
|
|
} else {
|
|
env.DesktopEnvironment = "unknown"
|
|
}
|
|
}
|
|
|
|
// GPU detection
|
|
env.GPUInfo.detectLinuxGPU()
|
|
|
|
// Scale factor detection
|
|
env.detectLinuxScale()
|
|
}
|
|
|
|
// detectLinuxGPU performs GPU detection on Linux
|
|
func (env *GPUInfo) detectLinuxGPU() {
|
|
if cmd, err := exec.Command("lspci").Output(); err == nil {
|
|
gpuInfo := string(cmd)
|
|
lines := strings.Split(gpuInfo, "\n")
|
|
|
|
for _, line := range lines {
|
|
if strings.Contains(strings.ToLower(line), "vga") ||
|
|
strings.Contains(strings.ToLower(line), "3d") ||
|
|
strings.Contains(strings.ToLower(line), "display") {
|
|
|
|
line = strings.ToLower(line)
|
|
if strings.Contains(line, "nvidia") {
|
|
env.Vendor = "nvidia"
|
|
env.Supported = true
|
|
} else if strings.Contains(line, "amd") || strings.Contains(line, "radeon") || strings.Contains(line, "advanced micro devices") {
|
|
env.Vendor = "amd"
|
|
env.Supported = true
|
|
} else if strings.Contains(line, "intel") {
|
|
env.Vendor = "intel"
|
|
env.Supported = true
|
|
}
|
|
|
|
// Extract model (simplified)
|
|
if colonPos := strings.LastIndex(line, ":"); colonPos != -1 {
|
|
env.Model = strings.TrimSpace(line[colonPos+1:])
|
|
// Clean up common prefixes
|
|
env.Model = strings.Replace(env.Model, "controller", "", -1)
|
|
env.Model = strings.Replace(env.Model, "corporation", "", -1)
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// detectLinuxScale detects display scaling on Linux
|
|
func (env *GUIEnvironment) detectLinuxScale() {
|
|
// Try to get scale factor from various sources
|
|
scaleFactors := []float64{1.0}
|
|
|
|
// Try GDK_SCALE (GNOME/GTK)
|
|
if gdkScale := os.Getenv("GDK_SCALE"); gdkScale != "" {
|
|
if scale, err := strconv.ParseFloat(gdkScale, 64); err == nil {
|
|
scaleFactors = append(scaleFactors, scale)
|
|
}
|
|
}
|
|
|
|
// Try QT_SCALE_FACTOR (Qt applications)
|
|
if qtScale := os.Getenv("QT_SCALE_FACTOR"); qtScale != "" {
|
|
if scale, err := strconv.ParseFloat(qtScale, 64); err == nil {
|
|
scaleFactors = append(scaleFactors, scale)
|
|
}
|
|
}
|
|
|
|
// Try Xft.dpi (X11 DPI)
|
|
if xftDPI := os.Getenv("Xft.dpi"); xftDPI != "" {
|
|
if dpi, err := strconv.ParseFloat(xftDPI, 64); err == nil {
|
|
scale := dpi / 96.0
|
|
scaleFactors = append(scaleFactors, scale)
|
|
}
|
|
}
|
|
|
|
// Use the largest scale factor found (most likely to be correct)
|
|
env.ScaleFactor = scaleFactors[0]
|
|
for _, scale := range scaleFactors {
|
|
if scale > env.ScaleFactor {
|
|
env.ScaleFactor = scale
|
|
}
|
|
}
|
|
|
|
// Clamp to reasonable range
|
|
if env.ScaleFactor > 4.0 {
|
|
env.ScaleFactor = 4.0
|
|
} else if env.ScaleFactor < 0.5 {
|
|
env.ScaleFactor = 0.5
|
|
}
|
|
}
|
|
|
|
// detectWindowsGUI handles Windows-specific GUI detection
|
|
func (env *GUIEnvironment) detectWindowsGUI() {
|
|
env.DisplayServer = "windows"
|
|
|
|
// Get Windows version info
|
|
if cmd, err := exec.Command("cmd", "/c", "ver").Output(); err == nil {
|
|
version := string(cmd)
|
|
if strings.Contains(version, "10.0.") {
|
|
// Check build number to distinguish Windows 11
|
|
if buildCmd, err := exec.Command("powershell", "-Command", "(Get-CimInstance Win32_OperatingSystem).BuildNumber").Output(); err == nil {
|
|
buildStr := strings.TrimSpace(string(buildCmd))
|
|
if build, err := strconv.Atoi(buildStr); err == nil {
|
|
if build >= 22000 {
|
|
env.DesktopEnvironment = "windows11"
|
|
} else {
|
|
env.DesktopEnvironment = "windows10"
|
|
}
|
|
}
|
|
} else {
|
|
env.DesktopEnvironment = "windows10"
|
|
}
|
|
} else {
|
|
env.DesktopEnvironment = "windows_legacy"
|
|
}
|
|
}
|
|
|
|
// GPU detection for Windows
|
|
env.GPUInfo.detectWindowsGPU()
|
|
|
|
// Windows DPI detection
|
|
env.detectWindowsScale()
|
|
}
|
|
|
|
// detectWindowsGPU performs GPU detection on Windows
|
|
func (env *GPUInfo) detectWindowsGPU() {
|
|
if cmd, err := exec.Command("powershell", "-Command", "Get-WmiObject Win32_VideoController | Select-Object Name").Output(); err == nil {
|
|
gpuName := strings.TrimSpace(string(cmd))
|
|
env.Model = gpuName
|
|
|
|
gpuNameLower := strings.ToLower(gpuName)
|
|
if strings.Contains(gpuNameLower, "nvidia") {
|
|
env.Vendor = "nvidia"
|
|
env.Supported = true
|
|
} else if strings.Contains(gpuNameLower, "amd") || strings.Contains(gpuNameLower, "radeon") {
|
|
env.Vendor = "amd"
|
|
env.Supported = true
|
|
} else if strings.Contains(gpuNameLower, "intel") {
|
|
env.Vendor = "intel"
|
|
env.Supported = true
|
|
}
|
|
}
|
|
}
|
|
|
|
// detectWindowsScale detects display scaling on Windows
|
|
func (env *GUIEnvironment) detectWindowsScale() {
|
|
// Try to get DPI from PowerShell
|
|
if cmd, err := exec.Command("powershell", "-Command", "Add-Type -TypeDefinition 'using System; using System.Runtime.InteropServices; public class DPI { [DllImport(\"user32.dll\")] public static extern IntPtr GetDC(IntPtr ptr); [DllImport(\"gdi32.dll\")] public static extern int GetDeviceCaps(IntPtr hdc, int nIndex); [DllImport(\"user32.dll\")] public static extern int ReleaseDC(IntPtr ptr, IntPtr hdc); public const int LOGPIXELSX = 88; public static double GetScale() { IntPtr hdc = GetDC(IntPtr.Zero); int dpi = GetDeviceCaps(hdc, LOGPIXELSX); ReleaseDC(IntPtr.Zero, hdc); return dpi / 96.0; } }; [DPI]::GetScale()").Output(); err == nil {
|
|
if scaleStr := strings.TrimSpace(string(cmd)); scaleStr != "" {
|
|
if scale, err := strconv.ParseFloat(scaleStr, 64); err == nil {
|
|
env.ScaleFactor = scale
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fallback to registry if PowerShell method fails
|
|
if env.ScaleFactor == 1.0 {
|
|
if cmd, err := exec.Command("reg", "query", "HKCU\\Control Panel\\Desktop", "/v", "LogPixels").Output(); err == nil {
|
|
output := string(cmd)
|
|
if strings.Contains(output, "0x") {
|
|
// Parse hex value
|
|
parts := strings.Fields(output)
|
|
for _, part := range parts {
|
|
if strings.HasPrefix(part, "0x") {
|
|
if dpi, err := strconv.ParseInt(part, 0, 64); err == nil {
|
|
env.ScaleFactor = float64(dpi) / 96.0
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// detectMacGUI handles macOS-specific GUI detection
|
|
func (env *GUIEnvironment) detectMacGUI() {
|
|
env.DisplayServer = "darwin"
|
|
env.DesktopEnvironment = "macos"
|
|
|
|
// macOS has good built-in scaling
|
|
if cmd, err := exec.Command("system_profiler", "SPDisplaysDataType", "-json").Output(); err == nil {
|
|
// Parse macOS display info for retina/HiDPI detection
|
|
displayInfo := string(cmd)
|
|
if strings.Contains(displayInfo, "Retina") || strings.Contains(displayInfo, "HiDPI") {
|
|
env.ScaleFactor = 2.0
|
|
}
|
|
}
|
|
|
|
// GPU detection for macOS
|
|
env.GPUInfo.detectMacGPU()
|
|
}
|
|
|
|
// detectMacGPU performs GPU detection on macOS
|
|
func (env *GPUInfo) detectMacGPU() {
|
|
if cmd, err := exec.Command("system_profiler", "SPDisplaysDataType").Output(); err == nil {
|
|
gpuInfo := string(cmd)
|
|
if strings.Contains(strings.ToLower(gpuInfo), "amd") || strings.Contains(strings.ToLower(gpuInfo), "radeon") {
|
|
env.Vendor = "amd"
|
|
env.Supported = true
|
|
} else if strings.Contains(strings.ToLower(gpuInfo), "intel") {
|
|
env.Vendor = "intel"
|
|
env.Supported = true
|
|
} else if strings.Contains(strings.ToLower(gpuInfo), "nvidia") {
|
|
env.Vendor = "nvidia"
|
|
env.Supported = true
|
|
}
|
|
}
|
|
}
|
|
|
|
// GetOptimalWindowSize returns the optimal window size for the current environment
|
|
func (env GUIEnvironment) GetOptimalWindowSize(minWidth, minHeight int) fyne.Size {
|
|
// Apply scale factor to minimum size
|
|
scaledWidth := float64(minWidth) * env.ScaleFactor
|
|
scaledHeight := float64(minHeight) * env.ScaleFactor
|
|
|
|
// Apply platform-specific adjustments
|
|
switch env.DisplayServer {
|
|
case "windows":
|
|
if env.DesktopEnvironment == "windows11" {
|
|
// Windows 11 has modern UI scaling, but we should be reasonable
|
|
scaledWidth = min(scaledWidth, 1600)
|
|
scaledHeight = min(scaledHeight, 1200)
|
|
}
|
|
case "wayland":
|
|
// Wayland handles scaling well, but we should be reasonable
|
|
scaledWidth = min(scaledWidth, 1400)
|
|
scaledHeight = min(scaledHeight, 1000)
|
|
case "x11":
|
|
// Traditional X11 might need more consideration for HiDPI
|
|
if env.ScaleFactor > 1.5 {
|
|
scaledWidth = min(scaledWidth, 1200)
|
|
scaledHeight = min(scaledHeight, 900)
|
|
}
|
|
}
|
|
|
|
return fyne.NewSize(float32(scaledWidth), float32(scaledHeight))
|
|
}
|
|
|
|
// String returns a human-readable description of the GUI environment
|
|
func (env GUIEnvironment) String() string {
|
|
return fmt.Sprintf("Display: %s, Desktop: %s, Scale: %.1fx, GPU: %s %s",
|
|
env.DisplayServer, env.DesktopEnvironment, env.ScaleFactor,
|
|
env.GPUInfo.Vendor, env.GPUInfo.Model)
|
|
}
|
|
|
|
// SupportsGPUAcceleration returns true if GPU acceleration is likely available
|
|
func (env GUIEnvironment) SupportsGPUAcceleration() bool {
|
|
return env.GPUInfo.Supported && env.ScaleFactor <= 2.0 // Don't use GPU acceleration on very high DPI displays
|
|
}
|
|
|
|
// GetModuleSpecificSize returns optimal size for specific modules
|
|
func (env GUIEnvironment) GetModuleSpecificSize(moduleID string) fyne.Size {
|
|
baseMinWidth := 800
|
|
baseMinHeight := 600
|
|
|
|
switch moduleID {
|
|
case "player":
|
|
// Player needs more space for video preview
|
|
baseMinWidth = 1024
|
|
baseMinHeight = 768
|
|
case "author":
|
|
// Author module benefits from wider layout
|
|
baseMinWidth = 900
|
|
baseMinHeight = 700
|
|
case "queue":
|
|
// Queue can be more compact but needs vertical space
|
|
baseMinWidth = 700
|
|
baseMinHeight = 500
|
|
case "settings":
|
|
// Settings can be compact
|
|
baseMinWidth = 600
|
|
baseMinHeight = 500
|
|
}
|
|
|
|
return env.GetOptimalWindowSize(baseMinWidth, baseMinHeight)
|
|
}
|