D. R. Evans wrote on 2/11/26 5:09 PM:
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?

I have more detail on this problem.

Firstly, I cannot make the problem occur here on my debian trixie systems. However, the user that reports it has now tried it with the attached program on Manjaro and debian trixie, on a Mac and on a Dell, and all of them fail (for him) the same way.

When the attached program is run, an example output on his screen [this is from the Dell running debian trixie] is:

---

window ID = 1
Display locked
XGrabKey has returned OK
Input selected OK
Display unlocked
LOOP NUMBER 1
 LOCKED
 FIRST PENDING COMPLETED

---

and then the file error_log is created, with the contents:

---

X Error handler called; program will exit
XErrorEvent:
  type         : 0
  display ptr  : 0x5589c2688530
  resourceid   : 1
  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:40
   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:86
   7# main at /home/w5qld/Downloads/xtest.cpp:143
   8#      at :0
   9# __libc_start_main at :0
  10# _start at :0
  11#

---

When I execute it here, I get the result that I expect:

---

window ID = 4194686
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
LOOP NUMBER 3
  LOCKED
  FIRST PENDING COMPLETED
  SECOND PENDING COMPLETED
  UNLOCKED
LOOP NUMBER 4
  LOCKED
  FIRST PENDING COMPLETED
  SECOND PENDING COMPLETED
  UNLOCKED
LOOP NUMBER 5
  LOCKED
  FIRST PENDING COMPLETED
  SECOND PENDING COMPLETED
  UNLOCKED

---

with normal completion and an empty error_log file.

What is going on? I have absolutely no idea how to proceed any further with this, since it all works on my systems :-( :-( :-(

  Doc

--
Web:  http://enginehousebooks.com/drevans
#include <fstream>
#include <iostream>
#include <stacktrace>
#include <thread>

#include <xcb/xcb.h>
#include <X11/Xlib.h>

using namespace std;
using namespace   chrono;        // std::chrono
using namespace   this_thread;   // std::this_thread

ofstream est { "error_log"s };

int _x_error_handler(Display* display_p, XErrorEvent* error_event_p)
{ if ( (static_cast<int>(error_event_p->error_code) == BadMatch) and (static_cast<int>(error_event_p->request_code) == 73) )
  { est << "X BadMatch error in XGetImage() ignored" << endl;
    return 0;
  }
  
  est << "X Error handler called; program will exit" << endl;

  est << "XErrorEvent: " << endl
      << "  type         : " << error_event_p->type << /* " (should be " << X_ERROR << ")" << */ endl
      << "  display ptr  : " << error_event_p->display << endl
      << "  resourceid   : " << error_event_p->resourceid << endl
      << "  serial       : " << error_event_p->serial << endl
      << "  error code   : " << static_cast<int>(error_event_p->error_code) << endl;

  constexpr int BUF_SIZE { 4096 };

  char buf[BUF_SIZE];

  XGetErrorText(error_event_p->display, error_event_p->error_code, &buf[0], BUF_SIZE);

  est << "  error code text: " << buf << endl
      << "  request code : " << static_cast<int>(error_event_p->request_code) << endl
      << "  minor code   : " << static_cast<int>(error_event_p->minor_code) << endl;

  std::stacktrace trace = stacktrace::current();
      
  est << "backtrace in X error handler call: " << endl << trace << endl;

  exit(-1);
}

/*
// This is never called, even if installed
int _x_io_error_handler(Display* display_p)
{ Display* tmp = display_p;   // perhaps remove this parameter?
  
  if (tmp)
    tmp = nullptr;

  est << "X IO Error; terminating" << endl;

  exit(-1);
  
  return -1;
}
*/

/*! \brief                        Exercise calls to XPending() in a loop: lock, Xpending(), Xpending(), unlock
    \param  _display_p           the display pointer
    \param  n_loops              number of times around the loop
    \param  gap_ms               milliseconds between the two calls to XPending()
    \param  per_loop_delay_ms   milliseconds to pause after each loop
*/
void execute_loop(Display* _display_p, const int n_loops, const int gap_ms, const int per_loop_delay_ms)
{ int counter { 0 };

// interleave writes to the window with glances at the keyboard queue
  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));
  }
}

int main(int argc, char** argv)
{ Status status { XInitThreads() };
    
  if (status == 0)
  { cerr << "ERROR returned from XInitThreads; aborting" << endl;
    exit(-1);
  }

// set the error handler 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);
  }

// 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(_display_p, AnyKey, AnyModifier, _window_id, false, GrabModeAsync, GrabModeAsync);
  cout << "XGrabKey has returned OK" << 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;  
  
  execute_loop(_display_p, 5, 10, 1000);
  
  return 0;
}

Reply via email to