Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Investigate the Window Manager Shell Window ID

5.00/5 (1 vote)
10 Jul 2016CPOL1 min read 6.4K   20  
Investigate the window manager's shell window ID and additional information of an OpenTK window.

Introduction

The OpenTk.NativeWindow class provides the property WindowInfo, which returns an IWindowInfo interface.

On X11, there are more properties than those, defined by the IWindowInfo interface. These properties are visible within the debugger and accessible via reflection.

C#
int screen = 0; // Fallback.
PropertyInfo piScreen = nativeWindow.WindowInfo.GetType().GetProperty("Screen");
if (piScreen != null)
     screen = (System.Int32)piScreen.GetValue(nativeWindow.WindowInfo, null);

On X11, the WindowInfo property contains the client window ID within the WindowInfo.WindowHandle property. To investigate the details of this relationship, and especially the window manager's shell window ID, the code shown below can be used.

Background

The libX11 provides the XQueryTree() method, that can be used to traverse the windows tree. Assumed, the OpenTK window to investigate is an independent application window (not a child window), the search depth is just one (otherwise the code has to be adopted).

Using the Code

Under the circumstances mentioned above, the following code is ready to use:

C#
/// <summary>Try to find the indicated native window's X11 window on default screen by conparing the
/// window bounds and, optionally, by the windows title.</summary>
/// <param name="nativeWindow">The <see cref="OpenTK.NativeWindow"/> window to find the X11 window
/// for.</param>
/// <param name="x11window">The found <see cref="System.IntPtr"/> X11 window ID.</param>
/// <param name="checkTitle">If set to <c>true</c> check validity by window title.</param>
/// <returns>Returns <c>true</c>, if X11 window on default screen was found, or <c>false</c>
/// otherwise.</returns>
public static bool TryFindX11WindowOnScreen(NativeWindow nativeWindow, out IntPtr x11window,
                                            bool checkTitle)
{
    x11window = IntPtr.Zero;

    if (nativeWindow == null)
        return false;

    IntPtr display = X11lib.XOpenDisplay ("");

    if (display != IntPtr.Zero)
    {
        int screen = 0; // Fallback.
        PropertyInfo piScreen = nativeWindow.WindowInfo.GetType().GetProperty("Screen");
        if (piScreen != null)
            screen = (System.Int32)piScreen.GetValue(nativeWindow.WindowInfo, null);

        IntPtr rootWindow = X11lib.XRootWindow (display, (X11lib.TInt)screen);
        System.Collections.Generic.List<IntPtr> children = X11lib.XQueryTree(display, rootWindow);

        for (int countChild = 0; countChild < children.Count; countChild++)
        {
            X11lib.XWindowAttributes attributes = new X11lib.XWindowAttributes();
            if ((int)X11lib.XGetWindowAttributes(display, children[countChild], ref attributes) != 0)
            {
                if ((int)attributes.x == nativeWindow.Bounds.X &&
                    (int)attributes.y == nativeWindow.Bounds.Y &&
                    (int)attributes.width == nativeWindow.Bounds.Width &&
                    (int)attributes.height == nativeWindow.Bounds.Height)
                {
                    x11window = children[countChild];
                    // Console.WriteLine("Found bounds matching window at " + " X=" + attributes.x +
                                         " Y=" + attributes.y +
                                         " W=" + attributes.width +
                                         " H=" + attributes.height);
                    break;
                }
            }
        }
        children.Clear();

        if (x11window != IntPtr.Zero && checkTitle)
        {
            bool match = false;

            if (x11window != IntPtr.Zero)
            {
                string childName = X11lib.XGetWMName(display, x11window);
                if (nativeWindow.Title == childName)
                {
                    match = true;
                    // Console.WriteLine("Verified the found window successfully by name '" +
                    //                   childName + "'.");
                }
                else
                {
                    children = X11lib.XQueryTree(display, x11window);
                    for (int countChild = 0; countChild < children.Count; countChild++)
                    {
                        X11lib.XWindowAttributes attributes = new X11lib.XWindowAttributes();
                        if ((int)X11lib.XGetWindowAttributes(display, children[countChild],
                                                             ref attributes) != 0)
                        {
                            childName = X11lib.XGetWMName(display, children[countChild]);
                            if (nativeWindow.Title == childName)
                            {
                                match = true;
                                // Console.WriteLine("Verified the found window successfully by name '" +
                                //                   childName + "'.");
                                break;
                            }
                        }
                    }
                }
            }

            if (match == false)
                x11window = IntPtr.Zero;
        }
        X11lib.XCloseDisplay(display);
    }

    return (x11window != IntPtr.Zero);
}

The prototypes of the methods within the X11lib helper class are provieded with the download above.

The investigation shows that the window manager's shell window has plenty of child windows, the requested OpenTK surface among them.

The image shows an exploded view of the shell window to demonstrate the child windows of the window manager's shell window.

Points of Interest

It is possible to investigate a OpenTK/OpenGL window's window manager's shell window to manipulate the shell window's property?

History

  • 11th July, 2016: First version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)