mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-05-14 16:22:46 -04:00
018795125e
- Adds "DMS Opener" as an option (dms-open.desktop) - Add mime type GO utils - Add rememberance to App Picker modal
204 lines
4.0 KiB
Go
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()
|
|
}
|