Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add _NET_WM_PID atom to Linux X11 window #17470

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions src/Avalonia.X11/UtsName.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
using System;
using System.Runtime.InteropServices;
using System.Text;

namespace Avalonia.X11;

internal struct UtsName : IDisposable
{
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/utsname.h
/*
#define __NEW_UTS_LEN 64

struct new_utsname
{
char sysname[__NEW_UTS_LEN + 1];
char nodename[__NEW_UTS_LEN + 1];
char release[__NEW_UTS_LEN + 1];
char version[__NEW_UTS_LEN + 1];
char machine[__NEW_UTS_LEN + 1];
char domainname[__NEW_UTS_LEN + 1];
};
*/

private UtsName(IntPtr buffer)
{
_buffer = buffer;
}

public static UtsName GetUtsName()
{
var ntsNameStructSize = (UtsLength + 1) * FieldCount;

IntPtr buffer = Marshal.AllocHGlobal(ntsNameStructSize);
try
{
if (uname(buffer) != 0)
{
throw new InvalidOperationException("uname failed");
}

return new UtsName(buffer);
}
catch
{
Marshal.FreeHGlobal(buffer);
throw;
}
}

private const int SystemNameFieldIndex = 0;
public ReadOnlySpan<byte> SystemNameSpan => GetValue(SystemNameFieldIndex);
public string SystemName => GetAsciiString(SystemNameSpan);

private const int NodeNameFieldIndex = 1;
public ReadOnlySpan<byte> NodeNameSpan => GetValue(NodeNameFieldIndex);
public string NodeName => GetAsciiString(NodeNameSpan);

private const int ReleaseFieldIndex = 2;
public ReadOnlySpan<byte> ReleaseSpan => GetValue(ReleaseFieldIndex);
public string Release => GetAsciiString(ReleaseSpan);

private const int VersionFieldIndex = 3;
public ReadOnlySpan<byte> VersionSpan => GetValue(VersionFieldIndex);
public string Version => GetAsciiString(VersionSpan);

private const int MachineFieldIndex = 4;
public ReadOnlySpan<byte> MachineSpan => GetValue(MachineFieldIndex);
public string Machine => GetAsciiString(MachineSpan);

private const int DomainNameFieldIndex = 5;
public ReadOnlySpan<byte> DomainNameSpan => GetValue(DomainNameFieldIndex);
public string DomainName => GetAsciiString(DomainNameSpan);

private const int UtsLength = 64;

private const int FieldCount = 6;

private static string GetAsciiString(ReadOnlySpan<byte> value)
{
#if NET6_0_OR_GREATER
return Encoding.ASCII.GetString(value);
#else
unsafe
{
fixed (byte* ptr = value)
{
return Encoding.ASCII.GetString(ptr, value.Length);
}
}
#endif
}

private ReadOnlySpan<byte> GetValue(int fieldIndex)
{
var startOffset = (UtsLength + 1) * fieldIndex;
var length = 0;
while (Marshal.ReadByte(_buffer, startOffset + length) != 0)
{
length++;
}

unsafe
{
return new ReadOnlySpan<byte>((byte*)_buffer + startOffset, length);
}
}

[DllImport("libc")]
private static extern int uname(IntPtr buf);

private readonly IntPtr _buffer;

public void Dispose()
{
Marshal.FreeHGlobal(_buffer);
}
}
49 changes: 49 additions & 0 deletions src/Avalonia.X11/X11Window.cs
Original file line number Diff line number Diff line change
Expand Up @@ -151,15 +151,21 @@ public X11Window(AvaloniaX11Platform platform, IWindowImpl? popupParent, bool ov
(int)CreateWindowArgs.InputOutput,
visual,
new UIntPtr((uint)valueMask), ref attr);
AppendPid(_handle);

if (_useRenderWindow)
{
_renderHandle = XCreateWindow(_x11.Display, _handle, 0, 0, defaultWidth, defaultHeight, 0, depth,
(int)CreateWindowArgs.InputOutput,
visual,
new UIntPtr((uint)(SetWindowValuemask.BorderPixel | SetWindowValuemask.BitGravity |
SetWindowValuemask.WinGravity | SetWindowValuemask.BackingStore)), ref attr);
AppendPid(_renderHandle);
lindexi marked this conversation as resolved.
Show resolved Hide resolved
}
else
{
_renderHandle = _handle;
}

Handle = new PlatformHandle(_handle, "XID");

Expand Down Expand Up @@ -323,6 +329,49 @@ private void UpdateMotifHints()
PropertyMode.Replace, ref hints, 5);
}

/// <summary>
/// Append `_NET_WM_PID` atom to X11 window
/// </summary>
/// <param name="windowXId"></param>
private void AppendPid(IntPtr windowXId)
{
// See https://github.com/AvaloniaUI/Avalonia/issues/17444
var pid = (uint)s_pid;
// The type of `_NET_WM_PID` is `CARDINAL` which is 32-bit unsigned integer, see https://specifications.freedesktop.org/wm-spec/1.3/ar01s05.html
XChangeProperty(_x11.Display, windowXId,
_x11.Atoms._NET_WM_PID, _x11.Atoms.XA_CARDINAL, 32,
PropertyMode.Replace, ref pid, 1);

// If _NET_WM_PID is set, the ICCCM-specified property WM_CLIENT_MACHINE MUST also be set.
// the hostname can change, so we can't cache it
// gethostname(3) on Linux just calls uname(2), so do it ourselves
lindexi marked this conversation as resolved.
Show resolved Hide resolved
// and avoid a memcpy
using var utsName = UtsName.GetUtsName();

var WM_CLIENT_MACHINE = XInternAtom(_x11.Display, "WM_CLIENT_MACHINE", false);
lindexi marked this conversation as resolved.
Show resolved Hide resolved

var nodeNameSpan = utsName.NodeNameSpan;
fixed (byte* pNodeName = &nodeNameSpan.GetPinnableReference())
{
XChangeProperty(_x11.Display, windowXId,
WM_CLIENT_MACHINE, _x11.Atoms.XA_STRING, 8,
PropertyMode.Replace, pNodeName, nodeNameSpan.Length);
}
}

private static readonly int s_pid = GetProcessId();

private static int GetProcessId()
{
#if NET6_0_OR_GREATER
var pid = Environment.ProcessId;
#else
using var currentProcess = Process.GetCurrentProcess();
var pid = currentProcess.Id;
#endif
return pid;
}

private void UpdateSizeHints(PixelSize? preResize, bool forceDisableResize = false)
{
if (_overrideRedirect)
Expand Down