diff --git a/src/Avalonia.Controls/Platform/IX11OptionsToplevelImplFeature.cs b/src/Avalonia.Controls/Platform/IX11OptionsToplevelImplFeature.cs
new file mode 100644
index 0000000000..b1ff618fbf
--- /dev/null
+++ b/src/Avalonia.Controls/Platform/IX11OptionsToplevelImplFeature.cs
@@ -0,0 +1,21 @@
+using Avalonia.Metadata;
+namespace Avalonia.Controls.Platform;
+
+public enum X11NetWmWindowType
+{
+ Normal,
+ Dialog,
+ Utility,
+ Menu,
+ Toolbar,
+ Splash,
+ Dock,
+ Desktop
+}
+
+[PrivateApi]
+public interface IX11OptionsToplevelImplFeature
+{
+ void SetNetWmWindowType(X11NetWmWindowType type);
+ void SetWmClass(string? className);
+}
diff --git a/src/Avalonia.Controls/Platform/X11Properties.cs b/src/Avalonia.Controls/Platform/X11Properties.cs
new file mode 100644
index 0000000000..fec88a427c
--- /dev/null
+++ b/src/Avalonia.Controls/Platform/X11Properties.cs
@@ -0,0 +1,38 @@
+using Avalonia.Controls.Platform;
+using Avalonia.Platform;
+using Avalonia.Reactive;
+
+namespace Avalonia.Controls;
+
+///
+/// Set of X11 specific properties and events that allow deeper customization of the application per platform.
+///
+public class X11Properties
+{
+ public static readonly AttachedProperty NetWmWindowTypeProperty =
+ AvaloniaProperty.RegisterAttached("NetWmWindowType");
+
+ public static void SetNetWmWindowType(Window obj, X11NetWmWindowType value) => obj.SetValue(NetWmWindowTypeProperty, value);
+ public static X11NetWmWindowType GetNetWmWindowType(Window obj) => obj.GetValue(NetWmWindowTypeProperty);
+
+ public static readonly AttachedProperty WmClassProperty =
+ AvaloniaProperty.RegisterAttached("WmClass");
+
+ public static void SetWmClass(Window obj, string? value) => obj.SetValue(WmClassProperty, value);
+ public static string? GetWmClass(Window obj) => obj.GetValue(WmClassProperty);
+
+ static X11Properties()
+ {
+ NetWmWindowTypeProperty.Changed.Subscribe(OnNetWmWindowTypeChanged);
+ WmClassProperty.Changed.Subscribe(OnWmClassChanged);
+ }
+
+ private static IX11OptionsToplevelImplFeature? TryGetFeature(AvaloniaPropertyChangedEventArgs e)
+ => (e.Sender as TopLevel)?.PlatformImpl?.TryGetFeature();
+
+ private static void OnWmClassChanged(AvaloniaPropertyChangedEventArgs e) =>
+ TryGetFeature(e)?.SetWmClass(e.NewValue.GetValueOrDefault(null));
+
+ private static void OnNetWmWindowTypeChanged(AvaloniaPropertyChangedEventArgs e) =>
+ TryGetFeature(e)?.SetNetWmWindowType(e.NewValue.GetValueOrDefault());
+}
\ No newline at end of file
diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs
index 669ecd585f..368a5a8e0f 100644
--- a/src/Avalonia.X11/X11Window.cs
+++ b/src/Avalonia.X11/X11Window.cs
@@ -33,7 +33,8 @@ using Avalonia.Platform.Storage.FileIO;
namespace Avalonia.X11
{
- internal unsafe partial class X11Window : IWindowImpl, IPopupImpl, IXI2Client
+ internal unsafe partial class X11Window : IWindowImpl, IPopupImpl, IXI2Client,
+ IX11OptionsToplevelImplFeature
{
private readonly AvaloniaX11Platform _platform;
private readonly bool _popup;
@@ -183,9 +184,8 @@ namespace Avalonia.X11
_x11.Atoms.WM_DELETE_WINDOW
};
XSetWMProtocols(_x11.Display, _handle, protocols, protocols.Length);
- XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_WINDOW_TYPE, _x11.Atoms.XA_ATOM,
- 32, PropertyMode.Replace, new[] { _x11.Atoms._NET_WM_WINDOW_TYPE_NORMAL }, 1);
-
+ SetNetWmWindowType(X11NetWmWindowType.Normal);
+
SetWmClass(_handle, _platform.Options.WmClass);
}
@@ -913,6 +913,9 @@ namespace Avalonia.X11
return new BclLauncher();
}
+ if (featureType == typeof(IX11OptionsToplevelImplFeature))
+ return this;
+
return null;
}
@@ -1251,6 +1254,13 @@ namespace Avalonia.X11
XFree(hint);
}
+
+ public void SetWmClass(string? className)
+ {
+ if (_handle == IntPtr.Zero)
+ return;
+ SetWmClass(_handle, className ?? _platform.Options.WmClass);
+ }
public void SetMinMaxSize(Size minSize, Size maxSize)
{
@@ -1412,5 +1422,26 @@ namespace Avalonia.X11
public IntPtr Handle => _owner._renderHandle;
public string HandleDescriptor => "XID";
}
+
+ public void SetNetWmWindowType(X11NetWmWindowType type)
+ {
+ if(_handle == IntPtr.Zero)
+ return;
+
+ var atom = type switch
+ {
+ X11NetWmWindowType.Dialog => _x11.Atoms._NET_WM_WINDOW_TYPE_DIALOG,
+ X11NetWmWindowType.Utility => _x11.Atoms._NET_WM_WINDOW_TYPE_UTILITY,
+ X11NetWmWindowType.Toolbar => _x11.Atoms._NET_WM_WINDOW_TYPE_TOOLBAR,
+ X11NetWmWindowType.Splash => _x11.Atoms._NET_WM_WINDOW_TYPE_SPLASH,
+ X11NetWmWindowType.Dock => _x11.Atoms._NET_WM_WINDOW_TYPE_DOCK,
+ X11NetWmWindowType.Desktop => _x11.Atoms._NET_WM_WINDOW_TYPE_DESKTOP,
+ _ => _x11.Atoms._NET_WM_WINDOW_TYPE_NORMAL
+ };
+
+ XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_WINDOW_TYPE, _x11.Atoms.XA_ATOM,
+ 32, PropertyMode.Replace, new[] { atom }, 1);
+
+ }
}
}