1- using System . Diagnostics ;
1+ using System . Runtime . InteropServices ;
22using YMouseButtonControl . Core . Services . Processes ;
33
44namespace YMouseButtonControl . Linux . Services ;
@@ -7,36 +7,123 @@ public class CurrentWindowServiceX11 : ICurrentWindowService
77{
88 public string ForegroundWindow => GetForegroundWindow ( ) ;
99
10- private string GetForegroundWindow ( )
10+ private static string GetForegroundWindow ( )
1111 {
12- var startInfo = new ProcessStartInfo
12+ var display = X11 . XOpenDisplay ( nint . Zero ) ;
13+ if ( display == nint . Zero )
1314 {
14- FileName = "/bin/bash" ,
15- Arguments = "-c \" xdotool getwindowfocus getwindowpid\" " ,
16- RedirectStandardOutput = true ,
17- } ;
18- using var xdoProc = new Process ( ) ;
19- xdoProc . StartInfo = startInfo ;
20- xdoProc . Start ( ) ;
21- var pid = xdoProc . StandardOutput . ReadToEnd ( ) . TrimEnd ( ) ;
22- xdoProc . WaitForExit ( ) ;
23-
24- if ( string . IsNullOrWhiteSpace ( pid ) )
15+ throw new Exception ( "Error opening display" ) ;
16+ }
17+
18+ try
2519 {
26- return "" ;
20+ var pid = GetForegroundWindowPid ( display ) ;
21+ if ( pid is null )
22+ {
23+ return "" ;
24+ }
25+
26+ return GetPathFromPid ( pid ) ?? "" ;
2727 }
28+ finally
29+ {
30+ X11 . XCloseDisplay ( display ) ;
31+ }
32+ }
2833
29- startInfo = new ProcessStartInfo
34+ private static string ? GetPathFromPid ( int ? pid )
35+ {
36+ var fi = new FileInfo ( $ "/proc/{ pid } /exe") ;
37+ return fi . LinkTarget ;
38+ }
39+
40+ private static unsafe int ? GetForegroundWindowPid ( nint display )
41+ {
42+ var root = X11 . XDefaultRootWindow ( display ) ;
43+ var prop = X11 . XInternAtom ( display , Marshal . StringToHGlobalAnsi ( "_NET_ACTIVE_WINDOW" ) , 0 ) ;
44+ var pidProp = X11 . XInternAtom ( display , Marshal . StringToHGlobalAnsi ( "_NET_WM_PID" ) , 1 ) ;
45+
46+ if (
47+ X11 . XGetWindowProperty (
48+ display ,
49+ root ,
50+ prop ,
51+ 0 ,
52+ sizeof ( ulong ) ,
53+ 0 ,
54+ 0 ,
55+ out _ ,
56+ out _ ,
57+ out _ ,
58+ out _ ,
59+ out var outProp
60+ ) != 0
61+ || outProp == nint . Zero
62+ )
3063 {
31- FileName = "/bin/bash" ,
32- Arguments = $ "-c \" ls -l /proc/{ pid } /exe\" ",
33- RedirectStandardOutput = true ,
34- } ;
35- using var proc = new Process ( ) ;
36- proc . StartInfo = startInfo ;
37- proc . Start ( ) ;
38- var path = proc . StandardOutput . ReadToEnd ( ) . Split ( "-> " ) [ 1 ] . Trim ( ) ;
39- proc . WaitForExit ( ) ;
40- return path ;
64+ return null ;
65+ }
66+
67+ var activeWindow = * ( nint * ) outProp ;
68+ X11 . XFree ( outProp ) ;
69+
70+ if (
71+ X11 . XGetWindowProperty (
72+ display ,
73+ activeWindow ,
74+ pidProp ,
75+ 0 ,
76+ sizeof ( int ) ,
77+ 0 ,
78+ 0 ,
79+ out _ ,
80+ out _ ,
81+ out _ ,
82+ out _ ,
83+ out var prop2
84+ ) != 0
85+ || prop2 == nint . Zero
86+ )
87+ {
88+ return null ;
89+ }
90+
91+ var pid = * ( int * ) prop2 ;
92+ X11 . XFree ( prop2 ) ;
93+ return pid ;
4194 }
4295}
96+
97+ internal static partial class X11
98+ {
99+ [ LibraryImport ( "libX11.so" ) ]
100+ internal static partial int XFree ( nint data ) ;
101+
102+ [ LibraryImport ( "libX11.so" ) ]
103+ internal static partial nint XOpenDisplay ( nint display ) ;
104+
105+ [ LibraryImport ( "libX11.so" ) ]
106+ internal static partial void XCloseDisplay ( nint display ) ;
107+
108+ [ LibraryImport ( "libX11.so" ) ]
109+ internal static partial nint XDefaultRootWindow ( nint display ) ;
110+
111+ [ LibraryImport ( "libX11.so" ) ]
112+ internal static partial nint XInternAtom ( nint display , nint atomName , int onlyIfExists ) ;
113+
114+ [ LibraryImport ( "libX11.so" ) ]
115+ internal static partial int XGetWindowProperty (
116+ IntPtr display ,
117+ IntPtr window ,
118+ IntPtr property ,
119+ long longOffset ,
120+ long longLength ,
121+ int delete ,
122+ ulong reqType ,
123+ out ulong actualTypeReturn ,
124+ out int actualFormatReturn ,
125+ out ulong nItemsReturn ,
126+ out ulong bytesAfterReturn ,
127+ out IntPtr propReturn
128+ ) ;
129+ }
0 commit comments