Skip to content

Commit

Permalink
Works now
Browse files Browse the repository at this point in the history
  • Loading branch information
Labfox committed Jan 26, 2025
1 parent 3a41921 commit bc18282
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 12 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@ A golang library to handle freedesktop specifications

Left To implement:
* Respect TryExec, StartupNotify
* Implement proper icon lookup on desktop files (Line 112 of main.go)
* Implement preferences for icon lookup

46 changes: 46 additions & 0 deletions baseDir/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package basedir

import (
"os"
"strings"
)

// GetXDGDirectory returns either a string or a slice of strings depending on the directory type.
func GetXDGDirectory(dirType string) interface{} {
switch dirType {
case "data":
return getEnvOrDefault("XDG_DATA_HOME", os.Getenv("HOME")+"/.local/share")
case "config":
return getEnvOrDefault("XDG_CONFIG_HOME", os.Getenv("HOME")+"/.config")
case "state":
return getEnvOrDefault("XDG_STATE_HOME", os.Getenv("HOME")+"/.local/state")
case "cache":
return getEnvOrDefault("XDG_CACHE_HOME", os.Getenv("HOME")+"/.cache")
case "runtime":
return getEnvOrDefault("XDG_RUNTIME_DIR", "")
case "dataDirs":
return getEnvOrDefaultList("XDG_DATA_DIRS", "/usr/local/share:/usr/share")
case "configDirs":
return getEnvOrDefaultList("XDG_CONFIG_DIRS", "/etc/xdg")
default:
return nil
}
}

// getEnvOrDefault returns the value of an environment variable or a default if not set or empty.
func getEnvOrDefault(envVar, defaultValue string) string {
value := os.Getenv(envVar)
if value == "" {
return defaultValue
}
return value
}

// getEnvOrDefaultList returns a slice of strings by splitting an environment variable or using a default.
func getEnvOrDefaultList(envVar, defaultValue string) []string {
value := os.Getenv(envVar)
if value == "" {
value = defaultValue
}
return strings.Split(value, ":")
}
35 changes: 28 additions & 7 deletions desktopFiles/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"regexp"
"strings"

basedir "github.com/MiracleOS-Team/libxdg-go/baseDir"
"github.com/MiracleOS-Team/libxdg-go/icons"
"gopkg.in/ini.v1"
)

Expand Down Expand Up @@ -108,14 +110,18 @@ func TranslateFieldWithLocale(key string, locale string, section *ini.Section) s
return key // Return the original key if no match
}

func ParseIconString(value string) string {
func ParseIconString(value string) (string, error) {
if strings.HasPrefix(value, "/") {
return value
return value, nil
}
if strings.Contains(value, "/") {
return filepath.Join("/", value)
return filepath.Join("/", value), nil
}
return value
icon, err := icons.FindIconDefaults(value, 256, 1, "application-x-executable")
if err != nil {
panic(err)
}
return icon, err
}

// ReadDesktopFileWithLocale reads a .desktop file and prints key-value pairs with locale-based selection
Expand All @@ -132,7 +138,6 @@ func ReadDesktopFile(filePath string) (DesktopFile, error) {
// Iterate over sections and print key-value pairs with locale-based translation
for _, section := range cfg.SectionStrings() {
sectionObj := cfg.Section(section)
fmt.Printf("Section: [%s]\n", section)
for _, key := range sectionObj.KeyStrings() {
if !strings.HasSuffix(key, "]") {
if sectionObj.Name() == "Desktop Entry" {
Expand All @@ -150,7 +155,7 @@ func ReadDesktopFile(filePath string) (DesktopFile, error) {
case "Comment":
dfile.Comment = TranslateFieldWithLocale(key, locale, sectionObj)
case "Icon":
dfile.Icon = ParseIconString(sectionObj.Key(key).String())
dfile.Icon, _ = ParseIconString(sectionObj.Key(key).String())
case "Hidden":
dfile.Hidden, _ = sectionObj.Key(key).Bool()
case "OnlyShowIn":
Expand Down Expand Up @@ -187,7 +192,7 @@ func ReadDesktopFile(filePath string) (DesktopFile, error) {
dfile.ApplicationObject.SingleMainWindow, _ = sectionObj.Key(key).Bool()

}
fmt.Println(key, sectionObj.Key(key).String())

}

}
Expand All @@ -198,6 +203,22 @@ func ReadDesktopFile(filePath string) (DesktopFile, error) {
return dfile, nil
}

func ListAllApplications() ([]DesktopFile, error) {
apps := []DesktopFile{}
for _, dir := range basedir.GetXDGDirectory("dataDirs").([]string) {
if _, err := os.Stat(dir + "/applications"); os.IsNotExist(err) {
continue
}
app1, err := ListApplications(dir)
if err != nil {
return nil, err
}
apps = append(apps, app1...)
}

return apps, nil
}

// ListApplications traverses a directory and parses .desktop files to list applications
func ListApplications(directory string) ([]DesktopFile, error) {
var apps []DesktopFile
Expand Down
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@ module github.com/MiracleOS-Team/libxdg-go

go 1.22.2

require gopkg.in/ini.v1 v1.67.0 // indirect
require (
golang.org/x/text v0.21.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
100 changes: 98 additions & 2 deletions icons/main.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
package icons

import (
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"time"

basedir "github.com/MiracleOS-Team/libxdg-go/baseDir"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)

type Subdir struct {
Expand Down Expand Up @@ -107,7 +114,16 @@ func findIconHelper(icon string, size, scale int, theme Theme, themeMap map[stri
for _, parentName := range theme.Parents {
parentTheme, exists := themeMap[parentName]
if !exists {
continue
parentTheme, exists = themeMap[strings.ToLower(parentName)]
if !exists {
parentTheme, exists = themeMap[strings.ToUpper(parentName)]
if !exists {
parentTheme, exists = themeMap[cases.Title(language.English, cases.Compact).String(parentName)]
if !exists {
continue
}
}
}
}
filename, err = findIconHelper(icon, size, scale, parentTheme, themeMap)
if err == nil {
Expand All @@ -117,6 +133,83 @@ func findIconHelper(icon string, size, scale int, theme Theme, themeMap map[stri
return "", errors.New("icon not found in theme or parents")
}

func FindIconDefaults(icon string, size, scale int, fallback string) (string, error) {

themeMap, err := CacheThemeMap(fmt.Sprintf("%v", basedir.GetXDGDirectory("cache")) + "/libxdg-icons.json")
if err != nil {
panic(err)
}

iconp, err := FindIcon(icon, size, scale, themeMap["MiracleOS"], themeMap)
if err != nil {
if fallback == "" {
return "", err
} else {
iconp, err = FindIconDefaults(fallback, size, scale, "")
}
}

return iconp, err

}

// CacheThemeMap caches the themeMap in a predefined file and generates it if it does not exist or if the cache is older than 24 hours.
func CacheThemeMap(cacheFile string) (map[string]Theme, error) {
themeMap := make(map[string]Theme)

// Check if cache file exists and is not older than 24 hours
if fileExists(cacheFile) {
info, err := os.Stat(cacheFile)
if err != nil {
return nil, err
}
if time.Since(info.ModTime()) < 4*time.Hour {
file, err := os.Open(cacheFile)
if err != nil {
return nil, err
}
defer file.Close()

decoder := json.NewDecoder(file)
err = decoder.Decode(&themeMap)
if err != nil {
return nil, err
}
return themeMap, nil
}
}

// Generate themeMap if cache file does not exist or is older than 24 hours
for _, v := range basedir.GetXDGDirectory("dataDirs").([]string) {
if _, err := os.Stat(v + "/icons"); os.IsNotExist(err) {
continue
}
themeMapv, err := GenerateThemeMap(v + "/icons")
if err != nil {
return nil, err
}

for key, value := range themeMapv {
themeMap[key] = value
}
}

// Cache the generated themeMap
file, err := os.Create(cacheFile)
if err != nil {
return nil, err
}
defer file.Close()

encoder := json.NewEncoder(file)
err = encoder.Encode(themeMap)
if err != nil {
return nil, err
}

return themeMap, nil
}

// FindIcon implements the main logic to find an icon.
func FindIcon(icon string, size, scale int, theme Theme, themeMap map[string]Theme) (string, error) {
filename, err := findIconHelper(icon, size, scale, theme, themeMap)
Expand All @@ -125,7 +218,10 @@ func FindIcon(icon string, size, scale int, theme Theme, themeMap map[string]The
}
hicolorTheme, exists := themeMap["hicolor"]
if !exists {
return "", errors.New("hicolor theme not found")
hicolorTheme, exists = themeMap["Hicolor"]
if !exists {
return "", errors.New("hicolor theme not found")
}
}
filename, err = findIconHelper(icon, size, scale, hicolorTheme, themeMap)
if err == nil {
Expand Down
2 changes: 1 addition & 1 deletion icons/themeUtils.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func GenerateThemeMap(iconsDir string) (map[string]Theme, error) {
})

if err != nil {
return nil, fmt.Errorf("failed to generate theme map: %w", err)
return themeMap, fmt.Errorf("failed to generate theme map: %w", err)
}

return themeMap, nil
Expand Down

0 comments on commit bc18282

Please sign in to comment.