How to Specify an Icon with a Relative Path for a Linux Desktop Entry File

How can I specify an icon with a RELATIVE path for a Linux desktop entry file?

After doing some more research it doesn't look like it's possible to specify relative paths for an icon in a desktop entry file as far as I can see.

The workaround I used was to add the following code to the end of my launcher.sh script:

mv myapp.desktop myapp.desktop-bak
sed -e "s,Icon=.*,Icon=$PWD/app.svg,g" myapp.desktop-bak > myapp.desktop
rm myapp.desktop-bak

This will update the path of the icon each time the launcher script is run, and since the .desktop file points to the launcher script, clicking the .desktop file effectively updates its icon.

I know you could use cat or the -i option to shorten the above code but I've read that the solution I used is more reliable. If anyone has further information on that please do post a comment.

Installed .desktop file to have user's home directory path inserted

Alternatively, put ~/.local/share/applications in the premade .desktop file, and then sed 's/~/$HOME/g' appname.desktop > $HOME/.local/share/applications/appname.desktop in the installer script, or invoke that from your language interpreter as a shell exec.

It's a real shame that more projects with GUIs don't do this. These desktop files also need a 'wmClass' element in them in order to allow for the launcher to also be a window tab. I've manually fixed several applications for my convenience this way.

Embedding an icon in a Linux executable

Executables on Linux do not have icons embedded (try to point your file manager to /usr/bin). What you have seen is probably a Desktop Entry. That's a text file describing the icon, program name and the executable.

Programmatically set custom folder/directory icon in Linux

If you are using the KDE desktop, then simply create a .directory file inside the directory.

E.g. I have a custom icon for my directory named "dev.sparc". I created this using the KDE desktop. All it does is create a file which I can view.

$ cat dev.sparc/.directory
[Desktop Entry]
Icon=applications-engineering

Et voila. That's all there is to it. If you or your users are using Gnome, this will be similar but probably subtly different. Just create an empty directory, use Gnome to set an icon, then $ ls -al in the directory to see what file it created.

How do I get relative or absolute file path stored in hpp header files, that can be used in any system?

Cross-Platform Home Directory in C++

To get the HOME directory at runtime (meaning it cannot be determined at compile-time, so it cannot be stored as a constant in a header), you may use getenv (or on Windows, _wgetenv, since paths should be Unicode-aware and therefore use the wide API on Windows).

POSIX

You may assume the path is specified using the HOME environment variable.

#include <cstdlib>
#include <string>

std::string get_home()
{
char *dir = getenv("HOME");
if (dir != nullptr) {
return std::string(dir);
} else {
// home not defined, root user, maybe return "/root"
// this code path should generally **not** occur.
return std::string("/");
}
}

Windows

A simple solution, as suggested by Miles Budnek, is to use GetUserProfileDirectory function.

#include <windows.h>
#include <string>

std::wstring get_home()
{
DWORD size = 0;
HANDLE token = GetCurrentProcessToken();

// get the size needed for the buffer.
GetUserProfileDirectoryW(token, NULL, &size);
if (size == 0) {
throw std::runtime_error("Unable to get required size.\n");
}

// this won't work pre-C++11, since strings weren't guaranteed
// to be continuous
std::wstring home(size, 0);
if (!GetUserProfileDirectoryW(token, &home[0], &size)) {
throw std::runtime_error(("Unable to get home directory.\n");
}

return home;
}

If you would like to rely on environment variables, this is not so easy, but the best solution is to check USERPROFILE, then HOME, then HOMEDRIVE and HOMEPATH, and if none of those are set, then SystemDrive as a fallback. This works out to:

#include <cstdlib>
#include <stdexcept>
#include <string>

std::wstring get_home()
{
// check USERPROFILE
wchar_t *home = _wgetenv(L"USERPROFILE");
if (home != nullptr) {
return std::wstring(home);
}

// check HOME
home = _wgetenv(L"HOME");
if (home != nullptr) {
return std::wstring(home);
}

// combine HOMEDRIVE and HOMEPATH
wchar_t *drive = _wgetenv(L"HOMEDRIVE");
wchar_t *path = _wgetenv(L"HOMEPATH");
if (drive != nullptr && path != nullptr) {
// "c:", "\users\{user}"
return std::wstring(drive) + std::wstring(path);
}

// try SystemDrive
home = _wgetenv(L"SystemDrive");
if (home != nullptr) {
return std::wstring(home);
} else {
return std::wstring(L"c:");
}
}

Why not WordExp?

wordexp is not guaranteed to be a part of Windows compilers, and won't work well on Windows. Also, HOME is not guaranteed to be set on Windows. You should use (_w)getenv. Also, wordexp does shell expansion, which means many other symbols (including *, character sets, and other environment variables) will be expanded, which may not be desired. This is simple, cross-platform, and limited in scope.



Related Topics



Leave a reply



Submit