Browse Source

[X11] Allow setting WM_CLASS and _NET_WM_WINDOW_TYPE (#14619)

* [X11] Allow setting WM_CLASS and _NET_WM_WINDOW_TYPE

* Renamed to X11Properties and moved to Avalonia.Controls

* [PrivateApi]

* Rename

* Suggested doc

Co-authored-by: Max Katz <maxkatz6@outlook.com>

---------

Co-authored-by: Max Katz <maxkatz6@outlook.com>
pull/14782/head
Nikita Tsukanov 2 years ago
committed by GitHub
parent
commit
8adb67a3d5
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 21
      src/Avalonia.Controls/Platform/IX11OptionsToplevelImplFeature.cs
  2. 38
      src/Avalonia.Controls/Platform/X11Properties.cs
  3. 39
      src/Avalonia.X11/X11Window.cs

21
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);
}

38
src/Avalonia.Controls/Platform/X11Properties.cs

@ -0,0 +1,38 @@
using Avalonia.Controls.Platform;
using Avalonia.Platform;
using Avalonia.Reactive;
namespace Avalonia.Controls;
/// <summary>
/// Set of X11 specific properties and events that allow deeper customization of the application per platform.
/// </summary>
public class X11Properties
{
public static readonly AttachedProperty<X11NetWmWindowType> NetWmWindowTypeProperty =
AvaloniaProperty.RegisterAttached<X11Properties, Window, X11NetWmWindowType>("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<string?> WmClassProperty =
AvaloniaProperty.RegisterAttached<X11Properties, Window, string?>("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<IX11OptionsToplevelImplFeature>();
private static void OnWmClassChanged(AvaloniaPropertyChangedEventArgs<string?> e) =>
TryGetFeature(e)?.SetWmClass(e.NewValue.GetValueOrDefault(null));
private static void OnNetWmWindowTypeChanged(AvaloniaPropertyChangedEventArgs<X11NetWmWindowType> e) =>
TryGetFeature(e)?.SetNetWmWindowType(e.NewValue.GetValueOrDefault());
}

39
src/Avalonia.X11/X11Window.cs

@ -33,7 +33,8 @@ using Avalonia.Platform.Storage.FileIO;
namespace Avalonia.X11 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 AvaloniaX11Platform _platform;
private readonly bool _popup; private readonly bool _popup;
@ -183,9 +184,8 @@ namespace Avalonia.X11
_x11.Atoms.WM_DELETE_WINDOW _x11.Atoms.WM_DELETE_WINDOW
}; };
XSetWMProtocols(_x11.Display, _handle, protocols, protocols.Length); XSetWMProtocols(_x11.Display, _handle, protocols, protocols.Length);
XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_WINDOW_TYPE, _x11.Atoms.XA_ATOM, SetNetWmWindowType(X11NetWmWindowType.Normal);
32, PropertyMode.Replace, new[] { _x11.Atoms._NET_WM_WINDOW_TYPE_NORMAL }, 1);
SetWmClass(_handle, _platform.Options.WmClass); SetWmClass(_handle, _platform.Options.WmClass);
} }
@ -913,6 +913,9 @@ namespace Avalonia.X11
return new BclLauncher(); return new BclLauncher();
} }
if (featureType == typeof(IX11OptionsToplevelImplFeature))
return this;
return null; return null;
} }
@ -1251,6 +1254,13 @@ namespace Avalonia.X11
XFree(hint); 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) public void SetMinMaxSize(Size minSize, Size maxSize)
{ {
@ -1412,5 +1422,26 @@ namespace Avalonia.X11
public IntPtr Handle => _owner._renderHandle; public IntPtr Handle => _owner._renderHandle;
public string HandleDescriptor => "XID"; 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);
}
} }
} }

Loading…
Cancel
Save