diff --git a/README.md b/README.md
index 8c4646f..6828521 100644
--- a/README.md
+++ b/README.md
@@ -24,14 +24,6 @@ Anything that can install .NET 8 should be able to run YMouseButtonControl
| Ubuntu | 20.04+ |
| macOS | 12.0+ |
-## Linux Recommended Software
-
-* xdotool (x11 only)
- * ```sudo apt install xdotool```
- * This is used to retrieve the foreground window name
-
-If you don't install xdotool or use wayland, every window will match when doing a mouse press.
-
## Build
### Requirements
diff --git a/YMouseButtonControl.Linux/Services/CurrentWindowServiceX11.cs b/YMouseButtonControl.Linux/Services/CurrentWindowServiceX11.cs
index 45e7c8d..2fa5d8a 100644
--- a/YMouseButtonControl.Linux/Services/CurrentWindowServiceX11.cs
+++ b/YMouseButtonControl.Linux/Services/CurrentWindowServiceX11.cs
@@ -1,4 +1,4 @@
-using System.Diagnostics;
+using System.Runtime.InteropServices;
using YMouseButtonControl.Core.Services.Processes;
namespace YMouseButtonControl.Linux.Services;
@@ -7,36 +7,123 @@ public class CurrentWindowServiceX11 : ICurrentWindowService
{
public string ForegroundWindow => GetForegroundWindow();
- private string GetForegroundWindow()
+ private static string GetForegroundWindow()
{
- var startInfo = new ProcessStartInfo
+ var display = X11.XOpenDisplay(nint.Zero);
+ if (display == nint.Zero)
{
- FileName = "/bin/bash",
- Arguments = "-c \"xdotool getwindowfocus getwindowpid\"",
- RedirectStandardOutput = true,
- };
- using var xdoProc = new Process();
- xdoProc.StartInfo = startInfo;
- xdoProc.Start();
- var pid = xdoProc.StandardOutput.ReadToEnd().TrimEnd();
- xdoProc.WaitForExit();
-
- if (string.IsNullOrWhiteSpace(pid))
+ throw new Exception("Error opening display");
+ }
+
+ try
{
- return "";
+ var pid = GetForegroundWindowPid(display);
+ if (pid is null)
+ {
+ return "";
+ }
+
+ return GetPathFromPid(pid) ?? "";
}
+ finally
+ {
+ X11.XCloseDisplay(display);
+ }
+ }
- startInfo = new ProcessStartInfo
+ private static string? GetPathFromPid(int? pid)
+ {
+ var fi = new FileInfo($"/proc/{pid}/exe");
+ return fi.LinkTarget;
+ }
+
+ private static unsafe int? GetForegroundWindowPid(nint display)
+ {
+ var root = X11.XDefaultRootWindow(display);
+ var prop = X11.XInternAtom(display, Marshal.StringToHGlobalAnsi("_NET_ACTIVE_WINDOW"), 0);
+ var pidProp = X11.XInternAtom(display, Marshal.StringToHGlobalAnsi("_NET_WM_PID"), 1);
+
+ if (
+ X11.XGetWindowProperty(
+ display,
+ root,
+ prop,
+ 0,
+ sizeof(ulong),
+ 0,
+ 0,
+ out _,
+ out _,
+ out _,
+ out _,
+ out var outProp
+ ) != 0
+ || outProp == nint.Zero
+ )
{
- FileName = "/bin/bash",
- Arguments = $"-c \"ls -l /proc/{pid}/exe\"",
- RedirectStandardOutput = true,
- };
- using var proc = new Process();
- proc.StartInfo = startInfo;
- proc.Start();
- var path = proc.StandardOutput.ReadToEnd().Split("-> ")[1].Trim();
- proc.WaitForExit();
- return path;
+ return null;
+ }
+
+ var activeWindow = *(nint*)outProp;
+ X11.XFree(outProp);
+
+ if (
+ X11.XGetWindowProperty(
+ display,
+ activeWindow,
+ pidProp,
+ 0,
+ sizeof(int),
+ 0,
+ 0,
+ out _,
+ out _,
+ out _,
+ out _,
+ out var prop2
+ ) != 0
+ || prop2 == nint.Zero
+ )
+ {
+ return null;
+ }
+
+ var pid = *(int*)prop2;
+ X11.XFree(prop2);
+ return pid;
}
}
+
+internal static partial class X11
+{
+ [LibraryImport("libX11.so")]
+ internal static partial int XFree(nint data);
+
+ [LibraryImport("libX11.so")]
+ internal static partial nint XOpenDisplay(nint display);
+
+ [LibraryImport("libX11.so")]
+ internal static partial void XCloseDisplay(nint display);
+
+ [LibraryImport("libX11.so")]
+ internal static partial nint XDefaultRootWindow(nint display);
+
+ [LibraryImport("libX11.so")]
+ internal static partial nint XInternAtom(nint display, nint atomName, int onlyIfExists);
+
+ [LibraryImport("libX11.so")]
+ internal static partial int XGetWindowProperty(
+ IntPtr display,
+ IntPtr window,
+ IntPtr property,
+ long longOffset,
+ long longLength,
+ int delete,
+ ulong reqType,
+ out ulong actualTypeReturn,
+ out int actualFormatReturn,
+ out ulong nItemsReturn,
+ out ulong bytesAfterReturn,
+ out IntPtr propReturn
+ );
+}
diff --git a/YMouseButtonControl.Linux/YMouseButtonControl.Linux.csproj b/YMouseButtonControl.Linux/YMouseButtonControl.Linux.csproj
index 72e9088..fa29fe5 100644
--- a/YMouseButtonControl.Linux/YMouseButtonControl.Linux.csproj
+++ b/YMouseButtonControl.Linux/YMouseButtonControl.Linux.csproj
@@ -5,6 +5,7 @@
enable
enable
default
+ true
diff --git a/YMouseButtonControl/DependencyInjection/ServicesBootstrapper.cs b/YMouseButtonControl/DependencyInjection/ServicesBootstrapper.cs
index 94b21b6..cad166b 100644
--- a/YMouseButtonControl/DependencyInjection/ServicesBootstrapper.cs
+++ b/YMouseButtonControl/DependencyInjection/ServicesBootstrapper.cs
@@ -55,25 +55,7 @@ private static void RegisterLinuxServices(IServiceCollection services)
.AddScoped();
if (Environment.GetEnvironmentVariable("XDG_SESSION_TYPE") == "x11")
{
- var startInfo = new ProcessStartInfo
- {
- FileName = "/bin/bash",
- Arguments = "-c \"xdotool\"",
- RedirectStandardOutput = true,
- RedirectStandardError = true,
- };
- using var proc = new Process();
- proc.StartInfo = startInfo;
- proc.Start();
- proc.WaitForExit();
- if (proc.ExitCode == 1)
- {
- services.AddScoped();
- }
- else
- {
- services.AddScoped();
- }
+ services.AddScoped();
}
else
{