In this article, let's review how to handle icons for Python or Java applications in Haiku.
The standard way to have an icon to an application is to attach the BEOS:ICON attribute like described in the "Attach icon to app" article.
When the application is launched, its icon is visible in the Deskbar menu, like Genio below:
The icon can also be useful for LaunchBox :
Let's suppose you have created a new Python application under Haiku, like the PyHelloHaiku reviewed in Use Python and Haiku API article.
Now let's attach to this application an icon :
seticon ./sun.hvif ./PyHelloHaiku
Note : seticon() is my user defined function (in profile):
seticon()
{
if [ $# -ne 2 ]; then
echo "Usage: seticon "
return 1
fi
addattr -f "$1" -t icon BEOS:ICON "$2"
}
Looking at the Tracker, the icon is correctly attached to the program :
However when you launch the python application, the Deskbar will display the generic icon :
And the application's name is overridden with "python3.10"
How to change that ?
You will need to launch the command :
addattr -t mime "BEOS:TYPE" application/x-vnd.be-elfexecutable /boot/home/Documents/Python/PyHelloHaiku/PyHelloHaiku
This command indicates that the program is an executable file. Now let's relaunch the program via the Tracker :
Nice it's working fine !
You can also add the application in LaunchBox, and it will work as expected :
Last point :
if you launch the application from the Terminal :
The "python3.10" with a generic icon will be displayed again :/
To avoid this, you can add the "open" command just before your application call :
In this case it's working as well :)
The java case was hard to resolve.
The solution proposed below is what I've found so far, as the best compromise.
Let's suppose, you're using the Squirrel SQL client - which is a java program.
The way to call the program is to have the launch script below :
As you might expect, the Tracker will display this application as "java" :
What about creating a C++ wrapper application, which will do the same than the bash script, ie calling the java program with the corresponding arguments needed to launch the Squirrel application ?
For that create the below C++ application with Pe editor :
lpe SquirrelSQL.cpp &
And paste the C++ code :
#include <Application.h>
#include <Alert.h>
#include <MessageRunner.h>
#include <unistd.h>
#include <iostream>
#include <csignal>
#include <sys/wait.h>
#include <filesystem>
#define APP_SIGNATURE "application/x-vnd.squirrel"
#define JAVA_JAR "/boot/home/config/non-packaged/apps/squirrelsql/squirrel-sql.jar"
class JavaLauncherApp : public BApplication {
public:
JavaLauncherApp()
: BApplication(APP_SIGNATURE), childPid(-1) {}
virtual void ReadyToRun() override {
childPid = fork();
if (childPid == 0) {
std::string JAVA_BIN = FindJava();
if (JAVA_BIN.empty()) {
std::cerr << "Error: Unable to find Java executable. Please ensure Java is installed " << std::endl;
exit(1);
}
// Execute child process corresponding to the JAR
if (execlp(JAVA_BIN.c_str(), JAVA_BIN.c_str(), "-jar", JAVA_JAR, nullptr) == -1) {
std::cerr << "Error: unable to execute the JAR file." << std::endl;
exit(1); // Terminate the child process if execlp fails
}
} else if (childPid < 0) {
std::cerr << "Error: unable to create child process." << std::endl;
exit(1);
} else {
// Start a MessageRunner to check the child's status
StartMessageRunner();
}
}
virtual void Quit() override {
if (childPid > 0) {
// Wait for child process to finish
int status;
pid_t result = waitpid(childPid, &status, WNOHANG); // Non-blocking wait
if (result == childPid) {
// Child process already exited
} else if (result == -1) {
// Error while waiting for child process
} else {
// Child is still running, send SIGTERM
kill(childPid, SIGTERM);
waitpid(childPid, &status, 0); // Block here to ensure proper termination
}
}
BApplication::Quit();
}
void StartMessageRunner() {
// Post a periodic message every 1s to check if the child is still running
BMessage* message = new BMessage('chdl');
messageRunner = new BMessageRunner(this, message, 1000000); // 1s interval
}
virtual void MessageReceived(BMessage* message) override {
if (message->what == 'chdl') {
int status;
pid_t result = waitpid(childPid, &status, WNOHANG); // Non-blocking check for child status
if (result == childPid) {
PostMessage(B_QUIT_REQUESTED); // Notify parent to quit
delete messageRunner;
}
} else {
BApplication::MessageReceived(message);
}
}
private:
pid_t childPid;
BMessageRunner* messageRunner;
// Search JAVA from JAVA_HOME environment variable
std::string FindJavaWithEnv() {
const char* javaHome = std::getenv("JAVA_HOME");
if (javaHome) {
std::string javaPath = std::string(javaHome) + "/bin/java";
if (std::filesystem::exists(javaPath)) {
return javaPath;
}
}
return "";
}
// Search JAVA in PATH
std::string FindJavaInPath() {
const char* pathEnv = std::getenv("PATH");
if (!pathEnv) {
return "";
}
std::string pathStr(pathEnv);
std::istringstream pathStream(pathStr);
std::string dir;
// Scan for directories in PATH
while (std::getline(pathStream, dir, ':')) {
std::filesystem::path javaPath = std::filesystem::path(dir) / "java";
// Check if java command
if (std::filesystem::exists(javaPath)) {
return javaPath.string();
}
}
return "";
}
// Search for JAVA
std::string FindJava() {
// JAVA_HOME method
std::string javaPath = FindJavaWithEnv();
if (!javaPath.empty()) {
return javaPath;
}
// PATH folders
javaPath = FindJavaInPath();
if (!javaPath.empty()) {
return javaPath;
}
return "";
}
};
int main() {
JavaLauncherApp app;
app.Run();
return 0;
}
This wrapper is doing the below :
I'm not sure the part to identify the java location is required, because when the "openjdkxxx_default" package is installed on Haiku, then the "/bin/java" symlink is created.
Now let's compile it and attach an icon :
g++ SquirrelSQL.cpp -o SquirrelSQL -lbe
seticon /boot/home/Documents/Java/squirrel.hvif /boot/home/Documents/Java/SquirrelSQL
Double click on the SquirrelSQL application :
As you can see, the SquirrelSQL application name and icon is visible in the Tracker !
The java part is still there also, but that's the current compromise to do.
When quitting the application, you can :
With this C++ wrapper, it makes Java applications a little bit more clearer in the Deskbar and you can also have dedicated icon for you application in LaunchBox :
Now let's do the same for DB Visualizer :
In the case of DB Visualizer, the arguments used by the "java" program is containing more entries like below :
Let's compile it and attach a nice icon :
g++ ./DBVisualizer.cpp -o DBVisualizer -lbe
seticon ./DBVisualizer.hvif ./DBVisualizer
In the Tracker, double click on "DBVisualizer" but also on "SquirrelSQL":
It's working fine !
Each application has its dedicated icon. The "java" part is now including the child for DBVisualizer but also SquirrelSQL :
If you close each java application via the close icon, it will close the parent application as well :)
And now you can add your favorite icons into LaunchBox :
I hope you find this article useful, and that in case of Java or Python applications, you should be able to associate now some nice icons.