Multiple OSs, one codebase, Part 1 - Application icons

September 26, 2026 Qt Software

In this series of posts, I'm going to talk about pain points in writing code for more than one OS with a single pile of source and how to fix them, if possible, or be aware of, if not.

First up - application icons

You know, Qt, for a programming framework that prides itself in being 'write the code once, compile it and run it on multiple OSs' this, frankly, just is awful.

Every OS handles the application icon differently. Even WASM (yes, I'll get there). Qt does nothing to help you here (had to stop myself from swearing there).

Windows: appicon.rc
Linux: Hidden folder for a file describing the application to the window manager(!)
WASM: favicon
Android: A directory full of confusing crap only one of which works (who designed this?)
MacOS: Don't know, but I'm positive it's different!

And yes, for WASM, favicon is an application icon, unless you don't want the little icon on the tab bar to reflect you application and just be the stupid 'I don't have an icon, (dur!)

... and you have to do it for each application. No help from Qt here. Nope.

So, here is how you do it for each OS:

Windows: Design an icon, save it as an image. In 'Greenfish Icon Editor Pro' (great program, btw!) convert it to a windows icon and save it as a .ico. in your CMakeLists.txt:

if (MSVC)
    qt_add_executable(<target> WIN32 appicon.rc)
endif()

Linux: For installers, I use the QtInstallerFramework since it gives me the most control, and I need it here. In `packages\com.vendor.product\meta':

'packages.xml':

<InstallScript>installScript.qs</InstallScript>

installscript.qs:

function Component()
{
    component.loaded.connect(this, Component.prototype.componentLoaded);
}

Component.prototype.componentLoaded = function()
{
    // runs when component loads
}

Component.prototype.createOperations = function()
{
    component.createOperations(); // run default operations first

    if (systemInfo.kernalType === "linux") {
        component.addOperation("CreateDesktopEntry",
            "@HomeDir@/.local/share/applications/<application_name>.desktop",
            "[Desktop Entry]\n" +
            "Type=<type of application>\n" +
            "Name=<application name>\n" +
            "Comment=<application description>\n" +
            "Exec=@TargetDir@/<application name>\n" +
            "Icon=@TargetDir@/application icon>\n" +
            "Terminal=<true or false>\n" +
            "Categories=<semi-colon list of mysterious categories>;\n" +
            "StartupNotify=true\n" +
            "StartupWMClass=<applicaiton name>\n" +
            "Keywords=<semi-colon list of mysterious keywords>;\n";
        );
    }
}

Just replace the stuff between the < and > and you are done-ish ...

WASM: Simple, just put a 48x48 image (favicon.png is this example) in the same directory as the application html directory and add the following line to the line to the <head> block:

    <link rel="icon" type="image/png" href="favicon.png">

If you want the favicon somewhere else, modify the line to suit.

Android: This one hurts. I'm not sure I even understand it entirely, or that is is all you need to do. I cut and pasted stuff from examples until it worked.

First step (and the easiest): create an image to use as the icon, leaving a 10% border around it in the icon background color for Android launcher icon decorations.

Next, add these lines to your CMakeLists.txt file:

if(ANDROID)
set_target_properties(<target> PROPERTIES
    QT_ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android"
    QT_ANDROID_APP_ICON "@mipmap/ic_launcher"
)
endif()

add this file (android/AndroidManifest.xml):

<?xml version='1.0'?>
<manifest android:installLocation="auto" android:versionCode="-- %%INSERT_VERSION_CODE%% --" android:versionName="-- %%INSERT_VERSION_NAME%% --" package="org.qtproject.example" xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- %%INSERT_PERMISSIONS -->
    <supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:smallScreens="true"/>
    <!--%%INSERT_FEATURES-->
    <application android:allowBackup="true" android:enableOnBackInvokedCallback="true" android:fullBackupOnly="false" android:hardwareAccelerated="true" android:icon="@drawable/icon" android:label="-- %%INSERT_APP_NAME%% --" android:name="org.qtproject.qt.android.bindings.QtApplication">
        <activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:exported="true" android:label="-- %%INSERT_APP_NAME%% --" android:launchMode="singleTop" android:name="org.qtproject.qt.android.bindings.QtActivity" android:screenOrientation="unspecified">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
            <meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
            <meta-data android:name="android.app.arguments" android:value="-- %%INSERT_APP_ARGUMENTS%% --"/>
        </activity>
        <provider android:authorities="${applicationId}.qtprovider" android:exported="false" android:grantUriPermissions="true" android:name="androidx.core.content.FileProvider">
            <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/qtprovider_paths"/>
        </provider>
    </application>
</manifest>

Now for the black magic. Open it in QtCreator. Magically, you get XML Source, Icon, Permissiona, and Splash Screen tabs. Select Icon, then pic Select Master Icon and pick your icon and it makes everything it needs to. Save it.

Yes, that's what you have to do. No, it's not automated any more than that. No, I haven't tried the bundled CMake Install step. This way works for me, and to be honest I don't want to have to slay anymore dragons.

Quoth!

Comments (0)

No comments yet. Be the first hero to speak up.

Leave a comment