@@ -124,81 +124,94 @@ public override Rectangle GetWindowRectangle(IntPtr windowHandle)
124124 return null ;
125125 }
126126
127-
127+ private static readonly Lock X11Sync = new ( ) ;
128128
129129
130130 public override List < WindowInfo > GetWindowList ( )
131131 {
132+ DebugHelper . WriteLine ( $ "GetWindowList called") ;
132133 var windows = new List < WindowInfo > ( ) ;
133- if ( IsWayland ( ) )
134+ lock ( X11Sync )
134135 {
135- if ( IsPlasma ( ) )
136+ // if (IsWayland())
137+ // {
138+ // if (IsPlasma())
139+ // {
140+ // // Interact with Plasmashell over DBus
141+ // return windows;
142+ // }
143+ //
144+ // if (IsGNOME())
145+ // {
146+ // // Interact with GNOME Shell
147+ // return windows;
148+ // }
149+ //
150+ // return windows;
151+ // }
152+
153+ var display = XOpenDisplay ( null ) ;
154+ if ( display == IntPtr . Zero )
136155 {
137- // Interact with Plasmashell over DBus
156+ DebugHelper . Logger ? . Debug ( "Unable to open X display" ) ;
138157 return windows ;
139158 }
140159
141- if ( IsGNOME ( ) )
160+ var root = XDefaultRootWindow ( display ) ; // Get the root window of the X display
161+
162+ // Get all the child windows of the root window
163+ var status = XQueryTree (
164+ display ,
165+ root ,
166+ out root ,
167+ out _ ,
168+ out var windowsPtr ,
169+ out var nchildren
170+ ) ;
171+ if ( status == 0 || windowsPtr == IntPtr . Zero )
142172 {
143- // Interact with GNOME Shell
173+ DebugHelper . Logger ? . Debug ( "XQueryTree failed" ) ;
174+ XCloseDisplay ( display ) ;
144175 return windows ;
145176 }
177+ DebugHelper . Logger ? . Debug ( "XQueryTree returned: " + status ) ;
178+ DebugHelper . Logger ? . Debug ( "nchildren: " + nchildren ) ;
146179
147- return windows ;
148- }
180+ for ( uint i = 0 ; i < nchildren ; i ++ )
181+ {
182+ var window = Marshal . ReadIntPtr ( windowsPtr , ( int ) ( i * IntPtr . Size ) ) ;
183+ DebugHelper . Logger ? . Debug ( "Window ptr: {Window:X0}" , window ) ;
149184
150- var display = XOpenDisplay ( null ) ;
151- if ( display == IntPtr . Zero )
152- {
153- DebugHelper . Logger ? . Debug ( "Unable to open X display" ) ;
154- return windows ;
155- }
185+ var title = GetWindowTitle ( display , window ) ;
186+ DebugHelper . Logger ? . Debug ( "Window title: {Title}" , title ) ;
187+ if ( title == string . Empty || title == "Untitled" )
188+ {
189+ DebugHelper . Logger ? . Debug ( "Window is untitled, skipping" ) ;
190+ continue ;
191+ }
156192
157- var root = XDefaultRootWindow ( display ) ; // Get the root window of the X display
193+ XGetWindowAttributes ( display , window , out var attributes ) ;
194+ var isVisible = attributes . is_colormap_installed ;
195+ // Active window
196+ XGetInputFocus ( display , out var focusWindow , out _ ) ;
197+ var isActive = focusWindow == window ;
198+ var rect = GetWindowRectangle ( window ) ;
199+ windows . Add (
200+ new WindowInfo
201+ {
202+ Handle = window ,
203+ Title = title ,
204+ IsVisible = isVisible ,
205+ Rectangle = rect ,
206+ // IsMinimized = IsWindowMinimized(display, window),
207+ IsActive = isActive ,
208+ }
209+ ) ;
210+ }
158211
159- // Get all the child windows of the root window
160- var status = XQueryTree (
161- display ,
162- root ,
163- out root ,
164- out _ ,
165- out var windowsPtr ,
166- out var nchildren
167- ) ;
168- if ( status == 0 )
169- {
170- DebugHelper . Logger ? . Debug ( "XQueryTree failed" ) ;
171212 XCloseDisplay ( display ) ;
172- return windows ;
173213 }
174214
175- for ( uint i = 0 ; i < nchildren ; i ++ )
176- {
177- var window = Marshal . ReadIntPtr ( windowsPtr , ( int ) ( i * IntPtr . Size ) ) ;
178- var title = GetWindowTitle ( display , window ) ;
179- XGetGeometry ( display , window , out root , out _ , out _ , out _ , out _ , out _ , out _ ) ;
180-
181- XGetWindowAttributes ( display , window , out var attributes ) ;
182- var isVisible = attributes . is_colormap_installed ;
183-
184- // Active window
185- XGetInputFocus ( display , out var focusWindow , out _ ) ;
186- var isActive = focusWindow == window ;
187- var rect = GetWindowRectangle ( window ) ;
188- windows . Add (
189- new WindowInfo
190- {
191- Handle = window ,
192- Title = title ,
193- IsVisible = isVisible ,
194- Rectangle = rect ,
195- IsMinimized = IsWindowMinimized ( display , window ) ,
196- IsActive = isActive ,
197- }
198- ) ;
199- }
200-
201- XCloseDisplay ( display ) ;
202215 return windows ;
203216 }
204217
@@ -449,6 +462,103 @@ internal static Image TakeScreenshotWithX11(Screen screen)
449462
450463 return image ;
451464 }
465+ internal Image ? TakeScreenshotOfX11Window ( WindowInfo window )
466+ {
467+ DebugHelper . WriteLine ( $ "Screenshotting window '{ window . Title } ' with bounds { window . Rectangle } ") ;
468+
469+ var display = XOpenDisplay ( null ) ;
470+ if ( display == IntPtr . Zero )
471+ throw new Exception ( "Unable to open X display." ) ;
472+
473+ // Get window attributes
474+ XGetWindowAttributes ( display , window . Handle , out var attributes ) ;
475+
476+ int width = attributes . width ;
477+ int height = attributes . height ;
478+
479+ DebugHelper . Logger ? . Debug ( "Window Attributes: width={Width}, height={Height}, depth={Depth}" , width , height , attributes . depth ) ;
480+ DebugHelper . Logger ? . Debug ( "visual: {Visual}, colormap: {Colormap}" , attributes . visual , attributes . colormap ) ;
481+
482+ // Capture the image from the window using XGetImage
483+ var imagePtr = XGetImage (
484+ display ,
485+ window . Handle ,
486+ 0 , 0 ,
487+ ( uint ) width ,
488+ ( uint ) height ,
489+ ALL_PLANES ,
490+ ZPIXMAP
491+ ) ;
492+
493+ if ( imagePtr == IntPtr . Zero )
494+ throw new Exception ( "Unable to capture window image using XGetImage." ) ;
495+
496+ var xImage = Marshal . PtrToStructure < XImage > ( imagePtr ) ;
497+ var bpp = xImage . bits_per_pixel ;
498+ var bytesPerPixel = bpp / 8 ;
499+
500+ DebugHelper . Logger ? . Debug ( $ "XImage: width={ xImage . width } , height={ xImage . height } , bits_per_pixel={ bpp } ") ;
501+ DebugHelper . Logger ? . Debug ( $ "XImage masks: red=0x{ xImage . red_mask : X} , green=0x{ xImage . green_mask : X} , blue=0x{ xImage . blue_mask : X} ") ;
502+
503+ var rawPixels = new byte [ width * height * bytesPerPixel ] ;
504+ Marshal . Copy ( xImage . data , rawPixels , 0 , rawPixels . Length ) ;
505+
506+ var pixelData = new byte [ width * height * 4 ] ;
507+
508+ for ( var y = 0 ; y < height ; y ++ )
509+ {
510+ if ( y % 100 == 0 )
511+ DebugHelper . Logger ? . Debug ( $ "Processing row { y } /{ height } ") ;
512+
513+ for ( var x = 0 ; x < width ; x ++ )
514+ {
515+ var pixelOffset = ( y * width + x ) * bytesPerPixel ;
516+
517+ ulong pixel = 0 ;
518+ var rawPixelBytes = new byte [ bytesPerPixel ] ;
519+
520+ for ( var byteIndex = 0 ; byteIndex < bytesPerPixel ; byteIndex ++ )
521+ {
522+ var currentByte = rawPixels [ pixelOffset + byteIndex ] ;
523+ rawPixelBytes [ byteIndex ] = currentByte ;
524+ pixel |= ( ulong ) currentByte << ( 8 * byteIndex ) ; // assuming little-endian
525+ }
526+
527+ if ( ( x == 0 && y == 0 ) || ( x == width / 2 && y == height / 2 ) )
528+ {
529+ DebugHelper . Logger ? . Debug (
530+ "Pixel at ({X},{Y}): Raw Bytes: {RawBytes}, Combined pixel ulong: 0x{Pixel:X}, BytesPerPixel: {BPP}" ,
531+ x , y ,
532+ BitConverter . ToString ( rawPixelBytes ) ,
533+ pixel ,
534+ bytesPerPixel
535+ ) ;
536+ DebugHelper . Logger ? . Debug (
537+ "Masks - Red: 0x{RedMask:X}, Green: 0x{GreenMask:X}, Blue: 0x{BlueMask:X}" ,
538+ xImage . red_mask , xImage . green_mask , xImage . blue_mask
539+ ) ;
540+ }
541+
542+ var r = ExtractColorComponent ( pixel , xImage . red_mask ) ;
543+ var g = ExtractColorComponent ( pixel , xImage . green_mask ) ;
544+ var b = ExtractColorComponent ( pixel , xImage . blue_mask ) ;
545+
546+ var idx = ( y * width + x ) * 4 ;
547+ pixelData [ idx + 0 ] = r ;
548+ pixelData [ idx + 1 ] = g ;
549+ pixelData [ idx + 2 ] = b ;
550+ pixelData [ idx + 3 ] = 255 ;
551+ }
552+ }
553+
554+ var image = Image . LoadPixelData < Rgba32 > ( pixelData , width , height ) ;
555+
556+ XDestroyImage ( imagePtr ) ;
557+ XCloseDisplay ( display ) ;
558+
559+ return image ;
560+ }
561+
452562 static byte ExtractColorComponent ( ulong pixel , ulong mask )
453563 {
454564 if ( mask == 0 )
@@ -481,14 +591,44 @@ static int CountBits(ulong mask)
481591 }
482592 return count ;
483593 }
594+ private static readonly IntPtr XA_WM_NAME = new IntPtr ( 39 ) ; // Usually 39, but can be obtained via XInternAtom
595+ [ StructLayout ( LayoutKind . Sequential ) ]
596+ public struct XTextProperty
597+ {
598+ public IntPtr value ;
599+ public IntPtr encoding ;
600+ public int format ;
601+ public ulong nitems ;
602+ }
603+
604+ [ LibraryImport ( LibX11 ) ]
605+ internal static partial int XGetTextProperty ( IntPtr display , IntPtr window , out XTextProperty textProp , IntPtr property ) ;
606+
484607 private static string GetWindowTitle ( IntPtr display , IntPtr window )
485608 {
486- var windowTitlePtr = XFetchName ( display , window ) ;
487- return windowTitlePtr != IntPtr . Zero
488- ? Marshal . PtrToStringAnsi ( windowTitlePtr ) ?? "Untitled"
489- : "Untitled" ;
609+ var netWmNameAtom = XInternAtom ( display , "_NET_WM_NAME" , false ) ;
610+ XTextProperty textProp ;
611+
612+ if ( XGetTextProperty ( display , window , out textProp , netWmNameAtom ) != 0 && textProp . value != IntPtr . Zero )
613+ {
614+ var title = Marshal . PtrToStringAnsi ( textProp . value ) ;
615+ XFree ( textProp . value ) ;
616+ return title ?? "Untitled" ;
617+ }
618+
619+ // Fallback to XA_WM_NAME
620+ if ( XGetTextProperty ( display , window , out textProp , XA_WM_NAME ) != 0 && textProp . value != IntPtr . Zero )
621+ {
622+ var title = Marshal . PtrToStringAnsi ( textProp . value ) ;
623+ XFree ( textProp . value ) ;
624+ return title ?? "Untitled" ;
625+ }
626+
627+ return "Untitled" ;
628+
490629 }
491630
631+
492632 [ LibraryImport ( LibX11 ) ]
493633 internal static partial IntPtr XGetSelectionOwner ( IntPtr display , IntPtr selection ) ;
494634
@@ -517,8 +657,8 @@ internal static partial int XQueryTree(
517657 out uint nchildren
518658 ) ;
519659
520- [ LibraryImport ( LibX11 ) ]
521- internal static partial IntPtr XFetchName ( IntPtr display , IntPtr window ) ;
660+ [ LibraryImport ( LibX11 , EntryPoint = "XFetchName" , StringMarshalling = StringMarshalling . Utf8 ) ]
661+ internal static partial int XFetchName ( IntPtr display , IntPtr window , out IntPtr windowName ) ;
522662
523663 [ LibraryImport ( LibX11 ) ]
524664 internal static partial int XDestroyImage ( IntPtr ximage ) ;
0 commit comments