~/.local/share/applications follows symlinks: a .desktop file can live in dotfiles and be linked in:
ln -s ~/dotfiles/desktop/myapp.desktop ~/.local/share/applications/myapp.desktop
xdg-desktop-menu resolves the link, update-desktop-database indexes it, gtk-launch, rofi and dmenu find it. Mode 644, no execute bit:
chmod 644 myapp.desktop
update-desktop-database ~/.local/share/applications
gtk-launch myapp
A broken symlink makes the launcher vanish silently; Exec= paths resolve literally, not via the symlink.
Exec and $HOME
Exec=$HOME/scripts/open-browser.sh %u
Works on GNOME, KDE and XFCE, but the Desktop Entry specification does not guarantee it — a stricter launcher may not expand the variable. An absolute path always works:
Exec=/home/daniel/scripts/open-browser.sh %u
GNOME on Fedora rejects the bare form: $ is reserved, needs quoting and escaping, and field codes may not sit inside a quoted argument. Portable workaround — %u as an unquoted positional parameter:
Exec=sh -c 'exec "$HOME/scripts/open-browser.sh" "$1"' sh %u
%u is a single URL, %U accepts several, and a script taking no URL should have neither.
The window that will not come forward
sh -c breaks GNOME’s window association: the shell looks like the launched application, so the window belongs to a process GNOME cannot match — no focus, no icon grouping, may open in the background.
StartupWMClass fixes it. Run the application and inspect its window:
wmctrl -lx
xprop | grep WM_CLASS
Take the second, case-sensitive value:
[Desktop Entry]
Name=My App
Exec=sh -c 'exec "$HOME/scripts/open-browser.sh" "$1"' sh %u
Type=Application
StartupWMClass=firefox
Cleaner: don’t wrap it. A script ending in exec firefox "$@" replaces its own process, so the launcher points straight at it and GNOME sees the real application:
#!/usr/bin/env bash
exec firefox "$@"
Exec=/home/daniel/scripts/open-browser.sh %u
StartupWMClass=firefox
Neither Terminal=false nor StartupNotify=true addresses the cause; nohup, a trailing & or disown make it worse. Validate and watch a launch:
desktop-file-validate ~/.local/share/applications/myapp.desktop
journalctl --user -f | grep -i -E 'gnome|shell|desktop|gio|gtk'