My very-high-level question is:
if XPending() is called twice on the same display, how can it be that the first call succeeds and the second, immediately afterwards, with no other keyboard activity on the window, calls the error handler?

Here's the contest for the question:

I have a stripped-down C++ program that executes fine on debian stable, but fails and calls the error handler on Manjaro. (The original big program is at: https://github.com/N7DR/drlog; I've winnowed it down to what appears to be a simple case that triggers the problem that was reported by a user using Manjaro. The original is multithreaded, so, even though the test program is just a single thread, I have kept the calls that lock and unlock the display.)

The program is run inside an X window.

main() looks like this:

int main(int argc, char** argv)
{
// initialise everything ready for use

  Status status { XInitThreads() };

  if (status == 0)
  { cerr << "ERROR returned from XInitThreads; aborting" << endl;
    exit(-1);
  }

// set the error handlers for X
  XSetErrorHandler(_x_error_handler);
  XSetIOErrorHandler(_x_io_error_handler);

// open the display
  Display* _display_p { XOpenDisplay(NULL) };

  if (!_display_p)
  { cerr << "Fatal error: unable to open display" << endl;
    exit(-1);
  }

  char* server_vendor { ServerVendor(_display_p) };

  cout << "Vendor of X server is: " << server_vendor << endl;

// get the window ID
  const char* cp { getenv("WINDOWID") };

  if (!cp)
{ cerr << "Fatal error: unable to obtain window ID; perhaps you are not running in an xterm; program must be run in an xterm" << endl;
    exit(-1);
  }

  Window _window_id = atoi(cp);

  cout << "window ID = " << _window_id << endl;

// make it so that we are the only process to have access to key presses in our window
  XLockDisplay(_display_p);

  cout << "Display locked" << endl;

// XGrabKey() always returns 1: https://github.com/mirror/libX11/blob/master/src/GrKey.c. So don't bother checking. XGrabKey(_display_p, AnyKey, AnyModifier, _window_id, false, GrabModeAsync, GrabModeAsync);

  cout << "XGrabKey has returned" << endl;

// we are interested only in key events
  XSelectInput(_display_p, _window_id, KeyPressMask | KeyReleaseMask);

  cout << "Input selected OK" << endl;

  XUnlockDisplay(_display_p);

  cout << "Display unlocked" << endl;

// the following line errors out
  execute_loop(_display_p, 5, 10, 1000);

  exit(0);
}

The execute_loop() function looks like:

void execute_loop(Display* _display_p, const int n_loops, const int gap_ms, const int per_loop_delay_ms)
{ int counter { 0 };

  for (int n { 0 }; n < n_loops; ++n)
  { cout << "LOOP NUMBER " << ++counter << endl;

    XLockDisplay(_display_p);

    cout << "  LOCKED " << endl;

    int pending { XPending(_display_p) };

    cout << "  FIRST PENDING COMPLETED " << endl;

    sleep_for(milliseconds(gap_ms));

    pending = XPending(_display_p);

    cout << "  SECOND PENDING COMPLETED "  << endl;

    XUnlockDisplay(_display_p);

    cout << "  UNLOCKED " << endl;

    sleep_for(milliseconds(per_loop_delay_ms));
  }
}

On debian, the output is (as I expected):

Vendor of X server is: The X.Org Foundation
window ID = 56623486
Display locked
XGrabKey has returned OK
Input selected OK
Display unlocked
LOOP NUMBER 1
  LOCKED
  FIRST PENDING COMPLETED
  SECOND PENDING COMPLETED
  UNLOCKED
LOOP NUMBER 2
  LOCKED
  FIRST PENDING COMPLETED
  SECOND PENDING COMPLETED
  UNLOCKED
...etc., until 5 loops have been executed.

On Manjaro, the output is:

Vendor of X server is: The X.Org Foundation
window ID = 570622048
Display locked
XGrabKey has returned OK
Input selected OK
Display unlocked
LOOP NUMBER 1
 LOCKED
 FIRST PENDING COMPLETED
X Error handler called; program will exit
XErrorEvent:
 type         : 0
 display ptr  : 0x55c0faa9a3b0
 resourceid   : 570622048
 serial       : 7
 error code   : 3
 error code text: BadWindow (invalid Window parameter)
 request code : 33
 minor code   : 0
backtrace in X error handler call:
  0# _x_error_handler(_XDisplay*, XErrorEvent*) at
/home/w5qld/Downloads/xtest.cpp:60
  1# _XError at :0
  2#      at :0
  3#      at :0
  4# _XEventsQueued at :0
  5# XPending at :0
  6# execute_loop(_XDisplay*, int, int, int) at
/home/w5qld/Downloads/xtest.cpp:96
  7# main at /home/w5qld/Downloads/xtest.cpp:163
  8#      at :0
  9# __libc_start_main at :0
 10# _start at :0
 11#

So what is failing appears to be:
    int pending { XPending(_display_p) };               // executes

    cout << "  FIRST PENDING COMPLETED " << endl; // executes

    sleep_for(milliseconds(gap_ms));                    // executes

    pending = XPending(_display_p);                     // fails

if XPending() worked the first time, how can it fail the second time? I don't see anything in the documentation for XPending() that suggests that it can fail, or, if it does fail, what can cause the failure.
:-(

I expect that I'm simply not understanding something about how the keyboard queue works -- although why the behaviour on debian and manjaro would be different seems rather mysterious.

  Doc Evans
--
Web:  http://enginehousebooks.com/drevans

Reply via email to