How do you exit X11 program without Error

让人想犯罪 __ 提交于 2020-12-01 02:06:29

问题


I have a fairly simple "Hello World" in X11 at end of question. But when it exits I get the run time error messages below:

$ ./xtest
XIO:  fatal IO error 11 (Resource temporarily unavailable) on X server ":0"
      after 9 requests (7 known processed) with 0 events remaining.

So I tried handling the wmDeleteMessage myself, and I was able to stop the window from closing, so i know I am getting the event correctly. Than I added a XDestroyWindow() to the event handling and I get new errors.

X Error of failed request:  BadWindow (invalid Window parameter)
  Major opcode of failed request:  4 (X_DestroyWindow)
  Resource id in failed request:  0x130
  Serial number of failed request:  12
  Current serial number in output stream:  12

It sounds like i am trying to destroy a already destroyed Window, but if I take out the XDestroyWindow() it stays alive on my screen.

Below is my code with an attempt at a destroy window handler. How do I exit without any errors?

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

int main()
{
  Display *display;
    if(!(display=XOpenDisplay(NULL))) 
    {
      std::cerr << "ERROR: could not open display\n";
      return 1;
    }

  int screen = DefaultScreen(display);
  Window rootwind = RootWindow(display, screen);
  Colormap cmap = DefaultColormap(display, screen);      
  Atom wmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False);

  int blackColor = BlackPixel(display, screen);
  int whiteColor = WhitePixel(display, screen);

  Window w = XCreateSimpleWindow(display, rootwind, 0, 0, 200, 100, 0, blackColor, blackColor);
  XMapWindow(display, w);
  XSetWMProtocols(display, w, &wmDeleteMessage, 1);
  bool running = true;
  while(running) 
  {
    XEvent e;
    XNextEvent(display, &e);      
    switch  (e.type) 
    {
      case ClientMessage:
        if(e.xclient.data.l[0] == wmDeleteMessage) 
        {
          std::cout << "Shutting down now!!!" << std::endl;
          XDestroyWindow(display,e.xdestroywindow.window);
          running=false;
          break;
        }
        break;
    }
  }

    XCloseDisplay(display);
    return 0;
}

Update

Changed line to :

   std::cout << "Shutting down now!!!" << std::endl;
        XDestroyWindow(display,w);

Which I don't like because I plan on having more than window, but for now I am back to the first error message I had :

XIO:  fatal IO error 11 (Resource temporarily unavailable) on X server ":0"
      after 9 requests (7 known processed) with 0 events remaining.

Update

Tried changing many things around like having the loop run off of XPending(). Decided to run someone else's hello world and I get the same problem with their code. Must be something wrong with my setup.

Update Apparently alot of people have this problem. Google ftk had this problem and they fixed it in their change log. They call FTK_QUIT() which i am guessing is like Exit(). So i put my return right there inside the loop and that solved the problem. Not sure why but it did. fixed code:

  case ClientMessage:
    if(e.xclient.data.l[0] == wmDeleteMessage) 
    {
      XDestroyWindow(display,e.xclient.window);
      XCloseDisplay(display);
      return 0;
    }

Will still give correct answer to someone who can explain why and if is possible move the return statement (along with the XCloseDisplay) outside of the loop.


The Event loop should look like this to exit properly:

  XEvent e;
  do
  {
    XNextEvent(display, &e);      
    if(e.type == ClientMessage && e.xclient.data.l[0] == wmDeleteMessage) 
    {
      XDestroyWindow(display,e.xclient.window);
      break;    
    }
    //...
  }while (XPending(display) > 0)
  XCloseDisplay(display);
  return 0;

When running in a switch statement the code does not work. Even if it exits the loop without calling another X function. The if statement above placed before your switch statement fixes the issue without returning from the program inside the loop.


回答1:


The solution to this problem is straightforward:

You must use the right structure member with the XDestroyWindow() function.

Due to the implementation standard of the X11 event structures, they're very similar each other. Every structure begins with the 'type' member, and the first members are practically always the same.

Now assume:

int = 4 bytes
Bool = 4 bytes
unsigned long = 8 bytes
Display* = 8 bytes
Window = 4 bytes

If you call XDestroyWindow() with e.xdestroywindow.window, you are going to be 28 bytes away from the beginning of the event structure, while if you use e.xclient.window, you would be 24 bytes away.

Since you're going to call XDestroyWindow() with a wrong Window argument, it will fail. Instead if you call it using e.xdestroywindow.event (which is 24 bytes away from the beginning of the event structure), the address would be right and the function would work gracefully.

If you take a look yourself at the Xlib.h file, you'll notice that the two structures have the window element positioned differently.

Stated this, remember that Xlib has been developed for years and many programmers every day work with it, so if there is a mysterious error, it's probably not within Xlib. As a last hint I want to tell you: if you want to get farther with Xlib programming, always take the header files as the primary reference, followed by the system manual, then all the rest.

The only error with your code in the end is:

XDestroyWindow(display,e.xdestroywindow.window);

Which must be changed to this:

XDestroyWindow(display,e.xclient.window);

Instead the usage of switch is good, and is the most implemented, with no issues on the X11 code.

NOTE: I've tested your code myself, by changing that line only, and then doing various tests, printing the result. The XDestroyWindow() line is for sure the only error.




回答2:


Just call XDestroyWindow() right before XCloseDisplay().

Edit:

Sorry, I didn't understand the XSetWMProtocols thing. Now I've read up on it. I think you're accessing the wrong member of the event union.

XDestroyWindow(display,e.xdestroywindow.window);

Should probably be:

XDestroyWindow(display,e.xclient.window);



回答3:


I had the same problem, and after digging through Xlib documentation and a lot of experimenting I think I know the answer to your question and I can explain it to you.

When you call XCreateWindow or XCreateSimpleWindow and then XMapWindow, you instruct the X Server to create your window and map in onto the screen. After sending these commands from the local buffer to the server (by calling XFlush or any function which requests some data from the server, since it implicitly flushes the command buffer), the X Server displays your window. Then it's a job of the Window Manager to attach all the decorations to your window, e.g. some borders, title bar, window menu and those buttons to minimize/maximize/close the window.

Now your window is being displayed, and after a while you can decide to destroy it with XDestroyWindow and close the connection to the X Server by calling XCloseDisplay, and everything will be fine, no errors.

The problem is that when the user clicks on that X on your window's title bar, it is not the job of the X Server to handle it, but the Window Manager's job (the X Server knows nothing about those decorations and it doesn't care). The usual reaction of the Window Manager when the user closes the top-level window of your program is to destroy the window and close the connection to the X Server, because that's what most users would expect. Your program may still run off-screen, but the top-level window is usually associated with the X Server connection by the Window Manager.

So when the Window Manager destroys your window, you cannot call XDestroyWindow, because the window is already destroyed and its Window handle is invalid. You will get an error about BadWindow. You also cannot call XCloseDisplay, because the connection to the X Server is already closed, and this will cause the XIO: fatal IO error 11 (Resource temporarily unavailable) on X server error many users experience from applications whose authors didn't knew that. It is a common mistake, because in one hand you are encouraged to clean up after yourself, but in the other hand the documentation is misleading about how this should be done properly.

There is a convention, though, about how X Server and Window Manager should cooperate, which also covers responding to user's commands to close the top-level window. There's an extension to the X protocol that handles it. Here's how the Xlib documentation explains it:

Clients, usually those with multiple top-level windows, whose server connection must survive the deletion of some of their top-level windows, should include the atom WM_DELETE_WINDOW in the WM_PROTOCOLS property on each such window. They will receive a ClientMessage event as described above whose data[0] field is WM_DELETE_WINDOW.
[...]
Clients that choose not to include WM_DELETE_WINDOW in the WM_PROTOCOLS property may be disconnected from the server if the user asks for one of the client's top-level windows to be deleted.

So there are two solutions to this problem: either avoid calling XDestroyWindow and XCloseDisplay when your window is being closed by the Window Manager and not by yourself (you actually don't have to clean up the top-level window since the X Server will destroy it nevertheless when your program ends), or you need to register the WM_DESTROY_WINDOW extension and wait for notification from the Window Manager when it is instructed by the user to close your window (it will send you a ClientMessage event then, with its data[0] set to WM_DELETE_WINDOW). And after receiving it just destroy the window and close the connection to the X Server yourself, and end your program. Or leave the connection to the X Server open to perform some more communication with it if you wish. When you handle WM_DESTROY_WINDOW, the Window Manager will not try to destroy your window nor close the connection to the X Server.




回答4:


// this code from : https://en.wikibooks.org/wiki/X_Window_Programming/Xlib don't show error...

  /*
   Simple Xlib application drawing a box in a window.
   To Compile: gcc -o test test.c -lX11  */


 #include<X11/Xlib.h>
 #include<stdio.h>
 #include<stdlib.h> // prevents error for exit on line 18 when compiling with gcc
 int main() {
   Display *d;
   int s;
   Window w;
   XEvent e;


                        /* open connection with the server */
   d=XOpenDisplay(NULL);
   if(d==NULL) {
     printf("Cannot open display\n");
     exit(1);
   }
   s=DefaultScreen(d);


                        /* create window */
   w=XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 100, 100, 1,
                         BlackPixel(d, s), WhitePixel(d, s));


   // Process Window Close Event through event handler so XNextEvent does Not fail
   Atom delWindow = XInternAtom( d, "WM_DELETE_WINDOW", 0 );
   XSetWMProtocols(d , w, &delWindow, 1);


                        /* select kind of events we are interested in */
   XSelectInput(d, w, ExposureMask | KeyPressMask);


                        /* map (show) the window */
   XMapWindow(d, w);


                        /* event loop */
   while(1) {
     XNextEvent(d, &e);
                        /* draw or redraw the window */
     if(e.type==Expose) {
       XFillRectangle(d, w, DefaultGC(d, s), 20, 20, 10, 10);
     }
                        /* exit on key press */
     if(e.type==KeyPress)
       break;
     // Handle Windows Close Event
     if(e.type==ClientMessage)
        break;
   }
                        /* destroy our window */
   XDestroyWindow(d, w);
                        /* close connection to server */
   XCloseDisplay(d);
   return 0;

}



来源:https://stackoverflow.com/questions/8176493/how-do-you-exit-x11-program-without-error

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!