1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-05-14 16:22:46 -04:00
Files
DankMaterialShell/core/internal/desktop/entries.go
T
bbedward 018795125e app picker: extend App Picker to integrate with mime overrides
- Adds "DMS Opener" as an option (dms-open.desktop)
- Add mime type GO utils
- Add rememberance to App Picker modal
2026-05-14 13:06:22 -04:00

204 lines
4.0 KiB
Go

package desktop
import (
"os"
"path/filepath"
"strings"
"sync"
"time"
"github.com/AvengeMedia/DankMaterialShell/core/internal/utils"
)
type Entry struct {
ID string
Path string
Name string
Exec string
Icon string
Categories []string
MimeTypes []string
NoDisplay bool
Hidden bool
Terminal bool
}
type cachedEntry struct {
entry *Entry
modTime time.Time
size int64
}
var (
entryCache = make(map[string]cachedEntry)
entryCacheMu sync.Mutex
listingCache []*Entry
listingExpires time.Time
listingCacheMu sync.Mutex
)
const listingTTL = 5 * time.Second
func applicationDirs() []string {
seen := make(map[string]bool)
var dirs []string
add := func(path string) {
if path == "" {
return
}
abs, err := filepath.Abs(path)
if err != nil {
abs = path
}
if seen[abs] {
return
}
seen[abs] = true
dirs = append(dirs, abs)
}
add(filepath.Join(utils.XDGDataHome(), "applications"))
if env := os.Getenv("XDG_DATA_DIRS"); env != "" {
for d := range strings.SplitSeq(env, ":") {
add(filepath.Join(strings.TrimSpace(d), "applications"))
}
} else {
add("/usr/local/share/applications")
add("/usr/share/applications")
}
home, _ := os.UserHomeDir()
if home != "" {
add(filepath.Join(home, ".local", "share", "flatpak", "exports", "share", "applications"))
}
add("/var/lib/flatpak/exports/share/applications")
add("/var/lib/snapd/desktop/applications")
return dirs
}
func parseEntry(path string, id string) (*Entry, error) {
info, err := os.Stat(path)
if err != nil {
return nil, err
}
entryCacheMu.Lock()
if c, ok := entryCache[path]; ok && c.modTime.Equal(info.ModTime()) && c.size == info.Size() {
entryCacheMu.Unlock()
return c.entry, nil
}
entryCacheMu.Unlock()
data, err := os.ReadFile(path)
if err != nil {
return nil, err
}
groups := parseGroups(data)
g, ok := groups["Desktop Entry"]
if !ok {
return nil, nil
}
entry := &Entry{
ID: id,
Path: path,
Name: g.keys["Name"],
Exec: g.keys["Exec"],
Icon: g.keys["Icon"],
Categories: splitList(g.keys["Categories"]),
MimeTypes: splitList(g.keys["MimeType"]),
NoDisplay: parseBool(g.keys["NoDisplay"]),
Hidden: parseBool(g.keys["Hidden"]),
Terminal: parseBool(g.keys["Terminal"]),
}
if t := g.keys["Type"]; t != "" && t != "Application" {
return nil, nil
}
entryCacheMu.Lock()
entryCache[path] = cachedEntry{entry: entry, modTime: info.ModTime(), size: info.Size()}
entryCacheMu.Unlock()
return entry, nil
}
func relativeID(root, path string) string {
rel, err := filepath.Rel(root, path)
if err != nil {
return filepath.Base(path)
}
return strings.ReplaceAll(rel, string(filepath.Separator), "-")
}
func AllEntries() []*Entry {
listingCacheMu.Lock()
if time.Now().Before(listingExpires) && listingCache != nil {
out := listingCache
listingCacheMu.Unlock()
return out
}
listingCacheMu.Unlock()
seen := make(map[string]bool)
var entries []*Entry
for _, dir := range applicationDirs() {
_ = filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error {
if err != nil || d.IsDir() {
return nil
}
if !strings.HasSuffix(path, ".desktop") {
return nil
}
id := relativeID(dir, path)
if seen[id] {
return nil
}
seen[id] = true
entry, err := parseEntry(path, id)
if err != nil || entry == nil {
return nil
}
entries = append(entries, entry)
return nil
})
}
listingCacheMu.Lock()
listingCache = entries
listingExpires = time.Now().Add(listingTTL)
listingCacheMu.Unlock()
return entries
}
func EntryByID(id string) *Entry {
if !strings.HasSuffix(id, ".desktop") {
id += ".desktop"
}
for _, entry := range AllEntries() {
if entry.ID == id {
return entry
}
}
return nil
}
func InvalidateCache() {
entryCacheMu.Lock()
entryCache = make(map[string]cachedEntry)
entryCacheMu.Unlock()
listingCacheMu.Lock()
listingCache = nil
listingExpires = time.Time{}
listingCacheMu.Unlock()
}