diff --git a/Interop/X11.cs b/Interop/X11.cs
index a414acc..6ef11da 100644
--- a/Interop/X11.cs
+++ b/Interop/X11.cs
@@ -88,6 +88,9 @@ internal static partial class X11
[LibraryImport(LibX11, StringMarshalling = StringMarshalling.Utf8)]
public static partial int XStoreName(IntPtr display, IntPtr window, string windowName);
+ [LibraryImport(LibX11)]
+ public static partial int XSetClassHint(IntPtr display, IntPtr window, ref XClassHint classHint);
+
[LibraryImport(LibX11)]
public static partial int XRaiseWindow(IntPtr display, IntPtr window);
diff --git a/Interop/XClassHint.cs b/Interop/XClassHint.cs
new file mode 100644
index 0000000..0d03728
--- /dev/null
+++ b/Interop/XClassHint.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.InteropServices;
+
+namespace Microsoft.Maui.Platform.Linux.Interop;
+
+[StructLayout(LayoutKind.Sequential)]
+public struct XClassHint
+{
+ public IntPtr res_name;
+ public IntPtr res_class;
+}
diff --git a/LinuxApplication.cs b/LinuxApplication.cs
index 6c8b4c9..8b126f2 100644
--- a/LinuxApplication.cs
+++ b/LinuxApplication.cs
@@ -265,6 +265,18 @@ public class LinuxApplication : IDisposable
Console.WriteLine("[LinuxApplication] GTK pre-initialized for WebView support");
}
+ // Set application name for desktop integration (taskbar, etc.)
+ // Try to get the name from environment or use executable name
+ string? appName = Environment.GetEnvironmentVariable("APPIMAGE_NAME");
+ if (string.IsNullOrEmpty(appName))
+ {
+ appName = Path.GetFileNameWithoutExtension(Environment.ProcessPath ?? "MauiApp");
+ }
+ string prgName = appName.Replace(" ", "");
+ GtkNative.g_set_prgname(prgName);
+ GtkNative.g_set_application_name(appName);
+ Console.WriteLine($"[LinuxApplication] Set application name: {appName} (prgname: {prgName})");
+
// Initialize dispatcher
LinuxDispatcher.Initialize();
DispatcherProvider.SetCurrent(LinuxDispatcherProvider.Instance);
diff --git a/Native/GtkNative.cs b/Native/GtkNative.cs
index 3e94d08..72672ce 100644
--- a/Native/GtkNative.cs
+++ b/Native/GtkNative.cs
@@ -42,6 +42,12 @@ internal static class GtkNative
[DllImport("libgtk-3.so.0")]
public static extern void gtk_window_set_title(IntPtr window, string title);
+ [DllImport("libglib-2.0.so.0")]
+ public static extern void g_set_prgname(string name);
+
+ [DllImport("libglib-2.0.so.0")]
+ public static extern void g_set_application_name(string name);
+
[DllImport("libgtk-3.so.0")]
public static extern void gtk_window_set_default_size(IntPtr window, int width, int height);
diff --git a/Window/X11Window.cs b/Window/X11Window.cs
index 5004e34..adf0367 100644
--- a/Window/X11Window.cs
+++ b/Window/X11Window.cs
@@ -150,6 +150,9 @@ public class X11Window : IDisposable
// Set window title
X11.XStoreName(_display, _window, title);
+ // Set WM_CLASS for desktop integration (taskbar icon matching)
+ SetWMClass(title.Replace(" ", ""), title.Replace(" ", ""));
+
// Select input events
X11.XSelectInput(_display, _window,
XEventMask.KeyPressMask |
@@ -196,6 +199,37 @@ public class X11Window : IDisposable
}
}
+ ///
+ /// Sets the WM_CLASS property for desktop integration.
+ /// This allows the desktop to match the window to its .desktop file.
+ ///
+ public void SetWMClass(string resName, string resClass)
+ {
+ IntPtr namePtr = IntPtr.Zero;
+ IntPtr classPtr = IntPtr.Zero;
+ try
+ {
+ namePtr = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(resName);
+ classPtr = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(resClass);
+
+ var classHint = new XClassHint
+ {
+ res_name = namePtr,
+ res_class = classPtr
+ };
+
+ X11.XSetClassHint(_display, _window, ref classHint);
+ Console.WriteLine($"[X11Window] Set WM_CLASS: {resName}, {resClass}");
+ }
+ finally
+ {
+ if (namePtr != IntPtr.Zero)
+ System.Runtime.InteropServices.Marshal.FreeHGlobal(namePtr);
+ if (classPtr != IntPtr.Zero)
+ System.Runtime.InteropServices.Marshal.FreeHGlobal(classPtr);
+ }
+ }
+
///
/// Sets the window icon from a file. Supports both raster images and SVG.
///