desktopentry

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 7, 2026 License: BSD-3-Clause Imports: 11 Imported by: 0

README

desktopentry — go-freedesktop

ci Go Reference License Go Coverage

The freedesktop Desktop Entry layer for a launcher — the piece a dock, application menu, or Spotlight-style finder needs: enumerate the installed applications and expand their launch commands. Pure Go, CGO-free, no runtime dependencies beyond two small libraries it deliberately reuses.

Scope — what this adds, and what it reuses

This module does not reinvent the low-level parsers. It stands on:

  • github.com/rkoesters/xdg (BSD-3) — the base .desktop / keyfile parse, including localized values and the [Desktop Action …] groups.
  • github.com/adrg/xdg (MIT) — XDG base-directory resolution (applications/, config dirs).

On top of those it builds the launcher-grade gap:

  • a clean Entry type exposing exactly the fields a launcher needs (Name + per-locale Names, GenericName, Comment, Exec, TryExec, Icon, Categories, Keywords, MimeType, NoDisplay, Hidden, Terminal, StartupWMClass, Actions);
  • ExpandExec — correct Exec field-code expansion (%f %F %u %U %i %c %k %%, deprecated codes dropped) with spec-compliant quote/escape tokenizing — the crux of actually launching an app;
  • Scan — walk every applications/ dir, de-duplicate by desktop-file id (subdir → -, higher-precedence dir wins, Hidden tombstones), drop NoDisplay/Hidden → the dock / finder index;
  • Autostart — the autostart/ entries honoring Hidden and OnlyShowIn/NotShowIn.

Install

go get github.com/go-freedesktop/desktopentry

Quickstart

package main

import (
	"fmt"
	"os/exec"

	"github.com/go-freedesktop/desktopentry"
)

func main() {
	// The launcher / Spotlight index: every visible installed app.
	for _, e := range desktopentry.Scan() {
		fmt.Printf("%-30s %s  (icon: %s)\n", e.ID, e.Name, e.Icon)
	}

	// Launch one, opening a file.
	e, _ := desktopentry.ParseFile("/usr/share/applications/org.gnome.gedit.desktop")
	argv, err := e.ExpandExec([]string{"/tmp/notes.txt"}, "")
	if err != nil {
		panic(err)
	}
	_ = exec.Command(argv[0], argv[1:]...).Start()

	// Offer an entry's actions ("New Window", …) — already parsed.
	for _, a := range e.Actions {
		fmt.Println("action:", a.Name, "→", a.Exec)
	}
}

Public API

Symbol Purpose
Parse(r) (*Entry, error) parse a .desktop from a reader, default locale
ParseWithLocale(r, locale) (*Entry, error) parse resolving localized strings for locale
ParseFile(path) (*Entry, error) parse a file, recording Entry.Path
Entry launcher-facing fields + Names map + Actions
Action a [Desktop Action …] group (ID, Name, Exec, Icon)
(*Entry).ExpandExec(files, url) ([]string, error) / ExpandExec(e, files, url) expand Exec field codes into argv
(*Entry).ShouldShowIn(current) bool honor OnlyShowIn/NotShowIn
Scan() []*Entry index every visible installed application
ScanDirs(dirs) []*Entry injectable form (dirs = increasing precedence)
Autostart() []*Entry autostart entries for $XDG_CURRENT_DESKTOP
AutostartDirs(dirs, current) []*Entry injectable form
ErrNoExec, ErrBadExec Exec-expansion error sentinels
Exec field codes
Code Expands to
%f a single file path (first of files)
%F the list of file paths (one argv element each)
%u a single URL (url, or the first file when url is empty)
%U the list of URLs ([url] when set, else the files)
%i --icon <Icon> (two elements; nothing if Icon is empty)
%c the localized Name
%k the desktop-file path (Entry.Path)
%% a literal %
%d %D %n %N %v %m deprecated — dropped

wasmdesk integration

  • Scan() → the Spotlight / dock index (id, Name, per-locale Names for cross-language search, Categories).
  • ExpandExec() → turns a clicked entry (+ optional file/URL) into the exact argv to launch.
  • Icon → feeds the future icontheme lib to resolve a real image.

Tests & coverage

CGO_ENABLED=0 go test ./...100% statement coverage, including every error branch, driven by fixtures under testdata/. CI additionally cross-builds and runs the suite on the six supported 64-bit targets (amd64/arm64 natively, riscv64/loong64/ppc64le/s390x under qemu-user).

License

BSD-3-Clause. Copyright (c) the go-freedesktop/desktopentry authors.


Note: the go-freedesktop org landing page and MkDocs site are deferred to the Wave-2 documentation sweep; this repo ships the README and .github profile for now.

Documentation

Overview

Package desktopentry is the freedesktop Desktop Entry layer for a launcher: it enumerates the installed applications and expands their launch commands.

It does not reinvent the low-level parsers. The base .desktop / keyfile parse is delegated to github.com/rkoesters/xdg/desktop and the XDG base-directory resolution to github.com/adrg/xdg. On top of those this package adds the launcher-grade gap:

  • a clean Entry type exposing exactly the fields a dock / launcher / Spotlight index needs, with per-locale names;
  • Entry.Actions, the [Desktop Action <name>] groups;
  • Entry.ExpandExec / ExpandExec, correct Exec field-code expansion (%f %F %u %U %i %c %k %%) per the Desktop Entry Specification;
  • Scan, which enumerates every applications/ directory, de-duplicates by desktop-file id and drops the entries a launcher must not show;
  • Autostart, the autostart/ entries honoring Hidden / OnlyShowIn.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrNoExec is returned by [ExpandExec] when the entry has no Exec key
	// (for example a Link or Directory entry, which cannot be launched).
	ErrNoExec = errors.New("desktopentry: entry has no Exec")

	// ErrBadExec is returned when the Exec value cannot be tokenized, e.g.
	// it contains an unterminated double quote.
	ErrBadExec = errors.New("desktopentry: malformed Exec value")
)

Errors returned by this package.

Functions

func ExpandExec

func ExpandExec(e *Entry, files []string, url string) ([]string, error)

ExpandExec is the package-level form of Entry.ExpandExec.

Example

ExampleExpandExec shows turning a clicked entry plus a file into the exact argv a launcher should exec.

package main

import (
	"fmt"
	"strings"

	"github.com/go-freedesktop/desktopentry"
)

func main() {
	e, err := desktopentry.Parse(strings.NewReader(
		"[Desktop Entry]\nType=Application\nName=Gedit\nExec=gedit %U\nIcon=gedit\n",
	))
	if err != nil {
		panic(err)
	}

	argv, err := e.ExpandExec(nil, "file:///tmp/notes.txt")
	if err != nil {
		panic(err)
	}
	fmt.Println(argv)
}
Output:
[gedit file:///tmp/notes.txt]

Types

type Action

type Action struct {
	// ID is the action identifier as listed in the Actions key.
	ID string
	// Name is the (localized) user-visible label.
	Name string
	// Exec is the program to run for this action; expand it with
	// [ExpandExec].
	Exec string
	// Icon is an optional icon name / path for the action.
	Icon string
}

Action is a single [Desktop Action <name>] group: an extra launch verb a launcher can offer next to the application (for example "New Window").

type Entry

type Entry struct {
	// ID is the desktop-file id (path under an applications/ dir with the
	// .desktop suffix stripped and directory separators turned into '-',
	// e.g. "kde4-konsole"). It is empty for entries parsed directly with
	// [Parse] rather than discovered by [Scan].
	ID string
	// Path is the absolute path of the .desktop file, or empty when the
	// entry was parsed from an in-memory reader.
	Path string

	// Type is the raw Type value ("Application", "Link", "Directory", ...).
	Type string

	// Name is the localized application name (resolved for the locale used
	// at parse time).
	Name string
	// Names maps a locale key ("" for the default, otherwise "fr",
	// "sr_Latn", ...) to the raw Name value, for cross-locale search.
	Names map[string]string
	// GenericName is the localized generic name ("Web Browser").
	GenericName string
	// Comment is the localized tooltip / description.
	Comment string

	// Exec is the program command line with field codes; expand it with
	// [Entry.ExpandExec].
	Exec string
	// TryExec is a binary whose presence gates whether the entry is
	// installed.
	TryExec string

	// Icon is the icon name or absolute path (feeds an icon-theme lookup).
	Icon string

	// Categories are the menu categories.
	Categories []string
	// Keywords are the localized search keywords.
	Keywords []string
	// MimeType are the MIME types the application can open.
	MimeType []string

	// NoDisplay hides the entry from menus / launchers.
	NoDisplay bool
	// Hidden marks the entry as deleted (a tombstone); a launcher must
	// treat it as absent.
	Hidden bool
	// Terminal requests the program be run inside a terminal.
	Terminal bool

	// OnlyShowIn / NotShowIn gate visibility by desktop environment.
	OnlyShowIn []string
	NotShowIn  []string

	// StartupWMClass is the WM class the launched window will set, used to
	// map a window back to its entry.
	StartupWMClass string

	// Actions are the extra launch verbs.
	Actions []Action
}

Entry is a parsed desktop entry reduced to the fields a launcher needs.

func Autostart

func Autostart() []*Entry

Autostart lists the autostart entries for the current desktop environment (read from XDG_CURRENT_DESKTOP), honoring Hidden and OnlyShowIn/NotShowIn.

func AutostartDirs

func AutostartDirs(dirs []string, current string) []*Entry

AutostartDirs is the injectable form of Autostart. dirs are given in decreasing order of precedence (config home first); an entry present in an earlier directory shadows one with the same filename in a later directory. Entries that are Hidden or not visible in current (per OnlyShowIn / NotShowIn) are dropped.

func Parse

func Parse(r io.Reader) (*Entry, error)

Parse reads a desktop entry from r using the process default locale.

func ParseFile

func ParseFile(path string) (*Entry, error)

ParseFile reads and parses the desktop entry at path using the default locale, recording its Path.

func ParseWithLocale

func ParseWithLocale(r io.Reader, locale string) (*Entry, error)

ParseWithLocale reads a desktop entry from r, resolving localized strings (Name, GenericName, Comment, Keywords) for locale, e.g. "fr_FR.UTF-8" or "sr@latin". An empty locale selects the process default locale.

func Scan

func Scan() []*Entry

Scan enumerates every installed application as a launcher would: it walks all applications/ directories resolved by github.com/adrg/xdg, parses each *.desktop file, de-duplicates by desktop-file id (a higher-precedence directory wins) and drops the entries a launcher must not show (NoDisplay or Hidden). The result is the dock / Spotlight index.

func ScanDirs

func ScanDirs(dirs []string) []*Entry

ScanDirs is the directory-injectable form of Scan. The dirs are given in increasing order of precedence: when the same desktop-file id appears in several directories, the entry from a later directory overrides the one from an earlier directory (including a later Hidden tombstone, which removes it from the result). Missing directories are ignored.

func (*Entry) ExpandExec

func (e *Entry) ExpandExec(files []string, url string) ([]string, error)

ExpandExec expands the entry's Exec value into an argv slice ready to be handed to os/exec, substituting the Desktop Entry Specification field codes:

%f  a single file path        (the first element of files)
%F  the list of file paths    (one argv element each)
%u  a single URL              (url, or the first file when url is empty)
%U  the list of URLs          ([url] when set, otherwise the files)
%i  --icon <Icon>             (two argv elements; nothing if Icon is "")
%c  the localized Name
%k  the desktop-file path     (Entry.Path)
%%  a literal percent sign

The deprecated field codes %d %D %n %N %v %m are recognized and dropped. A field code that expands to nothing (for example %f with no files) does not leave behind an empty argument.

The Exec value is tokenized per the specification's quoting rules (double quotes with backslash escapes) before substitution. An entry without an Exec key yields ErrNoExec; a malformed value yields ErrBadExec.

func (*Entry) ShouldShowIn

func (e *Entry) ShouldShowIn(current string) bool

ShouldShowIn reports whether the entry should be shown in the desktop environment named current (as in XDG_CURRENT_DESKTOP), honoring the OnlyShowIn and NotShowIn keys. An empty current means "any environment", which ignores both keys.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL