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