netfetch/internal/collector/theme.go

352 lines
7.4 KiB
Go

package collector
import (
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
)
func (c *Collector) collectTheme() {
c.mutex.Lock()
defer c.mutex.Unlock()
c.info.Theme = getCurrentTheme()
}
func (c *Collector) collectIcons() {
c.mutex.Lock()
defer c.mutex.Unlock()
c.info.Icons = getCurrentIcons()
}
func (c *Collector) collectFont() {
c.mutex.Lock()
defer c.mutex.Unlock()
c.info.Font = getCurrentFont()
}
func (c *Collector) collectCursor() {
c.mutex.Lock()
defer c.mutex.Unlock()
c.info.Cursor = getCurrentCursor()
}
func getCurrentTheme() string {
if runtime.GOOS == "darwin" {
return getMacOSTheme()
}
if runtime.GOOS == "windows" {
return getWindowsTheme()
}
if theme := getGSettingsTheme("gtk-theme"); theme != "" {
return theme
}
homeDir, err := os.UserHomeDir()
if err != nil {
return "Unknown"
}
gtk4Theme := parseGTKSetting(filepath.Join(homeDir, ".config", "gtk-4.0", "settings.ini"), "gtk-theme-name")
gtk3Theme := parseGTKSetting(filepath.Join(homeDir, ".config", "gtk-3.0", "settings.ini"), "gtk-theme-name")
gtk2Theme := parseGTKSetting(filepath.Join(homeDir, ".gtkrc-2.0"), "gtk-theme-name")
if gtk4Theme != "" {
return gtk4Theme
}
if gtk3Theme != "" {
return gtk3Theme
}
if gtk2Theme != "" {
return gtk2Theme
}
kdeglobals := filepath.Join(homeDir, ".config", "kdeglobals")
if theme := parseINISetting(kdeglobals, "General", "ColorScheme"); theme != "" {
return theme
}
if theme := parseINISetting(kdeglobals, "KDE", "LookAndFeelPackage"); theme != "" {
parts := strings.Split(theme, ".")
if len(parts) > 0 {
return parts[len(parts)-1]
}
return theme
}
return "Unknown"
}
func getGSettingsTheme(key string) string {
schemas := []string{
"org.gnome.desktop.interface",
"org.cinnamon.desktop.interface",
"org.mate.interface",
"org.x.apps.portal",
}
for _, schema := range schemas {
out, err := exec.Command("gsettings", "get", schema, key).Output()
if err == nil {
theme := strings.TrimSpace(string(out))
theme = strings.Trim(theme, "'\"")
if theme != "" {
return theme
}
}
}
return ""
}
func getCurrentIcons() string {
if runtime.GOOS == "darwin" {
return "macOS Icons"
}
if runtime.GOOS == "windows" {
return "Windows Icons"
}
if icons := getGSettingsTheme("icon-theme"); icons != "" {
return icons
}
homeDir, err := os.UserHomeDir()
if err != nil {
return "Unknown"
}
gtk4Icons := parseGTKSetting(filepath.Join(homeDir, ".config", "gtk-4.0", "settings.ini"), "gtk-icon-theme-name")
gtk3Icons := parseGTKSetting(filepath.Join(homeDir, ".config", "gtk-3.0", "settings.ini"), "gtk-icon-theme-name")
gtk2Icons := parseGTKSetting(filepath.Join(homeDir, ".gtkrc-2.0"), "gtk-icon-theme-name")
if gtk4Icons != "" {
return gtk4Icons
}
if gtk3Icons != "" {
return gtk3Icons
}
if gtk2Icons != "" {
return gtk2Icons
}
kdeglobals := filepath.Join(homeDir, ".config", "kdeglobals")
if icons := parseINISetting(kdeglobals, "Icons", "Theme"); icons != "" {
return icons
}
return "Unknown"
}
func getCurrentFont() string {
if runtime.GOOS == "darwin" {
return getMacOSFont()
}
if runtime.GOOS == "windows" {
return getWindowsFont()
}
if font := getGSettingsTheme("font-name"); font != "" {
return font
}
homeDir, err := os.UserHomeDir()
if err != nil {
return "Unknown"
}
gtk4Font := parseGTKSetting(filepath.Join(homeDir, ".config", "gtk-4.0", "settings.ini"), "gtk-font-name")
gtk3Font := parseGTKSetting(filepath.Join(homeDir, ".config", "gtk-3.0", "settings.ini"), "gtk-font-name")
gtk2Font := parseGTKSetting(filepath.Join(homeDir, ".gtkrc-2.0"), "gtk-font-name")
if gtk4Font != "" {
return gtk4Font
}
if gtk3Font != "" {
return gtk3Font
}
if gtk2Font != "" {
return gtk2Font
}
kdeglobals := filepath.Join(homeDir, ".config", "kdeglobals")
if font := parseINISetting(kdeglobals, "General", "font"); font != "" {
return font
}
return "Unknown"
}
func getCurrentCursor() string {
if runtime.GOOS == "darwin" {
return "macOS Cursor"
}
if runtime.GOOS == "windows" {
return "Windows Cursor"
}
if cursor := os.Getenv("XCURSOR_THEME"); cursor != "" {
return cursor
}
if cursor := getGSettingsTheme("cursor-theme"); cursor != "" {
return cursor
}
homeDir, err := os.UserHomeDir()
if err != nil {
return "Unknown"
}
gtkPaths := []string{
filepath.Join(homeDir, ".config", "gtk-4.0", "settings.ini"),
filepath.Join(homeDir, ".config", "gtk-3.0", "settings.ini"),
filepath.Join(homeDir, ".gtkrc-2.0"),
}
for _, path := range gtkPaths {
if cursor := parseGTKSetting(path, "gtk-cursor-theme-name"); cursor != "" {
return cursor
}
}
xresources := filepath.Join(homeDir, ".Xresources")
if cursor := parseXResourcesSetting(xresources, "Xcursor.theme"); cursor != "" {
return cursor
}
iconsIndex := filepath.Join(homeDir, ".icons", "default", "index.theme")
if cursor := parseINISetting(iconsIndex, "Icon Theme", "Inherits"); cursor != "" {
return cursor
}
kcminputrc := filepath.Join(homeDir, ".config", "kcminputrc")
if cursor := parseINISetting(kcminputrc, "Mouse", "cursorTheme"); cursor != "" {
return cursor
}
return "Unknown"
}
func parseGTKSetting(path, key string) string {
content, err := os.ReadFile(path)
if err != nil {
return ""
}
lines := strings.Split(string(content), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, key+"=") || strings.HasPrefix(line, key+" =") {
parts := strings.SplitN(line, "=", 2)
if len(parts) == 2 {
value := strings.TrimSpace(parts[1])
value = strings.Trim(value, `"'`)
return value
}
}
}
return ""
}
func parseINISetting(path, section, key string) string {
content, err := os.ReadFile(path)
if err != nil {
return ""
}
lines := strings.Split(string(content), "\n")
inSection := false
for _, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
currentSection := strings.Trim(line, "[]")
inSection = strings.EqualFold(currentSection, section)
continue
}
if inSection && strings.Contains(line, "=") {
parts := strings.SplitN(line, "=", 2)
if len(parts) == 2 && strings.TrimSpace(parts[0]) == key {
value := strings.TrimSpace(parts[1])
value = strings.Trim(value, `"'`)
return value
}
}
}
return ""
}
func parseXResourcesSetting(path, key string) string {
content, err := os.ReadFile(path)
if err != nil {
return ""
}
lines := strings.Split(string(content), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "!") || strings.HasPrefix(line, "#") {
continue
}
if strings.HasPrefix(line, key+":") || strings.HasPrefix(line, "*"+key+":") {
parts := strings.SplitN(line, ":", 2)
if len(parts) == 2 {
return strings.TrimSpace(parts[1])
}
}
}
return ""
}
func getMacOSTheme() string {
out, err := exec.Command("defaults", "read", "-g", "AppleInterfaceStyle").Output()
if err != nil {
return "Light"
}
if strings.Contains(strings.ToLower(string(out)), "dark") {
return "Dark"
}
return "Light"
}
func getWindowsTheme() string {
out, err := exec.Command("reg", "query",
`HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize`,
"/v", "AppsUseLightTheme").Output()
if err != nil {
return "Windows"
}
if strings.Contains(string(out), "0x0") {
return "Dark"
}
return "Light"
}
func getMacOSFont() string {
return "SF Pro"
}
func getWindowsFont() string {
return "Segoe UI"
}