Search code examples
c++linuxubuntux11xlib

Many XSetInputFocus's and XSync Causes Error


I'm getting an error when closing the display (or synchronizing it) after switching the input focus to each window with a matching process id. Below is the error that I am getting and the source code which produces it.

Source code:

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <iostream>
#include <list>

using namespace std;

class WindowsMatchingPid{
public:
    WindowsMatchingPid(Display *display, Window wRoot, unsigned long pid)
        : _display(display)
        , _pid(pid)
    {
    // Get the PID property atom.
        _atomPID = XInternAtom(display, "_NET_WM_PID", True);
        if(_atomPID == None)
        {
            cout << "No such atom" << endl;
            return;
        }

        search(wRoot);
    }

    const list<Window> &result() const { return _result; }

private:
    unsigned long  _pid;
    Atom           _atomPID;
    Display       *_display;
    list<Window>   _result;

    void search(Window w)
    {
    // Get the PID for the current Window.
        Atom           type;
        int            format;
        unsigned long  nItems;
        unsigned long  bytesAfter;
        unsigned char *propPID = 0;
        if(Success == XGetWindowProperty(_display, w, _atomPID, 0, 1, False, XA_CARDINAL,
                                         &type, &format, &nItems, &bytesAfter, &propPID))
        {
            if(propPID != 0)
            {
            // If the PID matches, add this window to the result set.
                if(_pid == *((unsigned long *)propPID))
                    _result.push_back(w);

                XFree(propPID);
            }
        }

    // Recurse into child windows.
        Window    wRoot;
        Window    wParent;
        Window   *wChild;
        unsigned  nChildren;
        if(0 != XQueryTree(_display, w, &wRoot, &wParent, &wChild, &nChildren))
        {
            for(unsigned i = 0; i < nChildren; i++)
                search(wChild[i]);
        }
    }
};

main()
{
 // Obtain the X11 display.
    Display *display = XOpenDisplay(0);
    if(display == NULL)
        return -1;

 // Get the root window for the current display.
    Window winRoot = XDefaultRootWindow(display);

    WindowsMatchingPid wmp(display,winRoot,4344);
    list<Window> lw = wmp.result();

    for(list<Window>::iterator it=lw.begin(); it != lw.end(); it++ ){
        XSetInputFocus(display,*it,RevertToParent,CurrentTime);
    }
    //XSync(display,false);
    XCloseDisplay(display);
    return 0;
}

Error:

X Error of failed request:  BadMatch (invalid parameter attributes)
  Major opcode of failed request:  42 (X_SetInputFocus)
  Serial number of failed request:  495
  Current serial number in output stream:  506

It produces the error when it reaches XSync or XCloseDisplay. When I removed these two calls, it does not produce these errors. I'm not sure what I am doing wrong here that causes both XSync and XCloseDisplay to complain.


Solution

  • From the documentation, it states the following:

    The specified focus window must be viewable at the time XSetInputFocus is called, or a BadMatch error results. If the focus window later becomes not viewable, the X server evaluates the revert_to argument to determine the new focus window as follows:

    • If revert_to is RevertToParent, the focus reverts to the parent (or the closest viewable ancestor), and the new revert_to value is taken to be RevertToNone.

    • If revert_to is RevertToPointerRoot or RevertToNone, the focus reverts to PointerRoot or None, respectively. When the focus reverts, the X server generates FocusIn and FocusOut events, but the last-focus-change time is not affected.

    XSetInputFocus can generate BadMatch, BadValue, and BadWindow errors.

    So, I was missing a check to determine whether the window is viewable or not. The following change will fix the problem:

    main()
    {
     // Obtain the X11 display.
        Display *display = XOpenDisplay(0);
        if(display == NULL)
            return -1;
    
     // Get the root window for the current display.
        Window winRoot = XDefaultRootWindow(display);
    
        WindowsMatchingPid wmp(display,winRoot,4344);
        list<Window> lw = wmp.result();
    
        for(list<Window>::iterator it=lw.begin(); it != lw.end(); it++ ){
            XWindowAttributes attribute; // <-- Added
            XGetWindowAttributes(display,*it,&attribute); // <-- Added
            if(attribute.map_state == IsViewable ){ // <-- Added
                XSetInputFocus(display,*it,RevertToParent,CurrentTime);
            } // <-- Added
        }
        XCloseDisplay(display);
        return 0;
    }