2011-11-18 4 views
16

Tôi có "Hello World" khá đơn giản trong X11 khi kết thúc câu hỏi. Nhưng khi nó thoát tôi nhận được thông báo lỗi thời gian chạy dưới đây:Làm cách nào để thoát khỏi chương trình X11 mà không có Lỗi

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

Vì vậy, tôi đã cố gắng xử lý wmDeleteMessage bản thân mình, và tôi đã có thể ngăn chặn các cửa sổ từ bế mạc, do đó, tôi biết tôi đang nhận được sự kiện này một cách chính xác. Hơn tôi đã thêm XDestroyWindow() vào xử lý sự kiện và tôi gặp lỗi mới.

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 

Có vẻ như tôi đang cố phá hủy một Cửa sổ đã bị phá hủy, nhưng nếu tôi lấy ra XDestroyWindow() nó vẫn còn trên màn hình của tôi.

Dưới đây là mã của tôi với nỗ lực xử lý cửa sổ hủy. Làm cách nào để thoát mà không có bất kỳ lỗi nào?

#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; 
} 

Cập nhật

dòng được đổi:

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

Mà tôi không thích vì tôi dự định có nhiều hơn cửa sổ, nhưng bây giờ tôi trở lại đầu tiên thông báo lỗi tôi có:

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

Cập nhật

Cố gắng thay đổi nhiều thứ xung quanh giống như có vòng lặp tắt của XPending(). Quyết định chạy hello world của người khác và tôi gặp vấn đề tương tự với mã của họ. Phải là một cái gì đó sai với thiết lập của tôi.

Cập nhật Dường như nhiều người gặp vấn đề này. Google ftk gặp sự cố này và họ đã khắc phục sự cố trong số change log của họ. Họ gọi FTK_QUIT() mà tôi đoán giống như Exit(). Vì vậy, tôi đặt trở lại của tôi ngay trong vòng lặp và giải quyết được vấn đề. Không chắc chắn tại sao nhưng nó đã làm. đang cố định:

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

vẫn sẽ cung cấp cho câu trả lời đúng để ai đó có thể giải thích tại sao và nếu có thể di chuyển các câu lệnh return (cùng với XCloseDisplay) bên ngoài vòng lặp.


Vòng lặp tổ chức sự kiện nên trông như thế này để thoát đúng cách:

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; 

Khi chạy trong một tuyên bố switch mã không hoạt động. Ngay cả khi nó thoát khỏi vòng lặp mà không gọi một hàm X khác. Tuyên bố if ở trên được đặt trước tuyên bố switch của bạn khắc phục vấn đề mà không cần phải trả lại từ chương trình bên trong vòng lặp.

+0

Thêm lối thoát vào vòng lặp vì nhận ra nó không bao giờ thoát ra ngoài. Vẫn còn thông báo lỗi tương tự. –

+0

Tại sao bạn muốn thực hiện lập trình X11 trực tiếp? Tôi khuyên bạn nên sử dụng bộ công cụ đồ họa, như GTK hoặc Qt (nhưng cũng có những công cụ khác: FLTK, Fox ...) –

+0

@Starynkevitch Tìm hiểu thêm về cách hoạt động của nó. Không phải cho công việc hay trường học. –

Trả lời

16

Các giải pháp cho vấn đề này rất đơn giản:

Bạn phải sử dụng các thành viên cấu trúc đúng với các XDestroyWindow() chức năng.

Do tiêu chuẩn triển khai của cấu trúc sự kiện X11, chúng rất giống nhau. Mỗi cấu trúc bắt đầu với thành viên 'loại', và các thành viên đầu tiên thực tế là như nhau.

Bây giờ giả sử:

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

Nếu bạn gọi XDestroyWindow() với e.xdestroywindow.window, bạn sẽ được 28 byte ra khỏi đầu của cấu trúc sự kiện, trong khi nếu bạn sử dụng e.xclient.window, bạn sẽ có 24 byte.

Vì bạn sẽ gọi XDestroyWindow() với tham số Cửa sổ sai, nó sẽ không thành công. Thay vào đó, nếu bạn gọi nó bằng cách sử dụng e.xdestroywindow.event (cách cấu trúc sự kiện 24 byte), địa chỉ sẽ đúng và chức năng sẽ hoạt động một cách duyên dáng.

Nếu bạn xem chính mình tại tệp Xlib.h, bạn sẽ nhận thấy rằng hai cấu trúc có phần tử được đặt khác nhau.

Nói điều này, hãy nhớ rằng Xlib đã được phát triển trong nhiều năm và nhiều lập trình viên mỗi ngày làm việc với nó, vì vậy nếu có một lỗi bí ẩn, nó có lẽ không phải trong Xlib. Như một gợi ý cuối cùng tôi muốn nói với bạn: nếu bạn muốn làm việc với lập trình Xlib xa hơn, luôn lấy các tệp tiêu đề làm tham chiếu chính, theo sau là sổ tay hệ thống, sau đó tất cả các phần còn lại.

Các lỗi chỉ với mã của bạn cuối cùng là:

XDestroyWindow(display,e.xdestroywindow.window); 

nào phải được thay đổi như sau:

XDestroyWindow(display,e.xclient.window); 

Thay vì việc sử dụng switch là tốt, và là nhất được triển khai, không có vấn đề gì về mã X11.

LƯU Ý: Tôi đã tự kiểm tra mã của mình, chỉ bằng cách thay đổi dòng đó và sau đó thực hiện các thử nghiệm khác nhau, in kết quả.Dòng XDestroyWindow() chắc chắn là lỗi duy nhất.

+0

+1 không chỉ là giải pháp mà còn là _explanation_ của vấn đề/giải pháp. Cảm ơn bạn. – tjklemz

3

Chỉ cần gọi XDestroyWindow() ngay trước XCloseDisplay().

Edit:

Xin lỗi, tôi không hiểu điều XSetWMProtocols. Bây giờ tôi đã đọc nó. Tôi nghĩ rằng bạn đang truy cập vào thành viên sai của liên minh sự kiện.

XDestroyWindow (display, e.xdestroywindow.window);

lẽ nên là:

XDestroyWindow(display,e.xclient.window); 
+0

Điều đó không khắc phục được lỗi. –

+0

@JoeMcGrath Đã thay đổi câu trả lời của tôi. Xin lỗi vì sự nhầm lẫn. –

+0

Cảm ơn, tôi đã học được điều gì đó, mặc dù không khắc phục được sự cố. Ít nhất tôi có cửa sổ bên phải xử lý ngay bây giờ. –

3

Tôi đã gặp vấn đề tương tự, và sau khi đào qua tài liệu Xlib và rất nhiều thử nghiệm tôi nghĩ tôi biết câu trả lời cho câu hỏi của bạn và tôi có thể giải thích cho bạn.

Khi bạn gọi XCreateWindow hoặc XCreateSimpleWindow và sau đó XMapWindow, bạn hướng dẫn X Server tạo cửa sổ và ánh xạ lên màn hình. Sau khi gửi các lệnh này từ bộ đệm cục bộ đến máy chủ (bằng cách gọi XFlush hoặc bất kỳ chức năng nào yêu cầu một số dữ liệu từ máy chủ, vì nó ngầm xóa bộ đệm lệnh), X Server sẽ hiển thị cửa sổ của bạn. Sau đó, đó là công việc của Trình quản lý cửa sổ để đính kèm tất cả các đồ trang trí vào cửa sổ của bạn, ví dụ: một số đường viền, thanh tiêu đề, menu cửa sổ và các nút đó để thu nhỏ/phóng to/đóng cửa sổ.

Bây giờ cửa sổ của bạn đang được hiển thị và sau một thời gian bạn có thể quyết định hủy nó bằng XDestroyWindow và đóng kết nối với X Server bằng cách gọi XCloseDisplay và mọi thứ sẽ ổn, không có lỗi.

Vấn đề là khi người dùng nhấp vào số X trên thanh tiêu đề của cửa sổ, nó không phải là công việc của X Server để xử lý nó, nhưng công việc của người quản lý cửa sổ (X Server không biết gì về những trang trí và nó không quan tâm). Phản ứng thông thường của Trình quản lý cửa sổ khi người dùng đóng cửa sổ cấp cao nhất của chương trình là phá hủy cửa sổ và đóng kết nối với X Server, vì đó là điều mà hầu hết người dùng mong đợi. Chương trình của bạn vẫn có thể chạy ngoài màn hình, nhưng cửa sổ cấp cao nhất thường được kết nối với kết nối X Server bởi Trình quản lý cửa sổ.

Vì vậy, khi Trình quản lý cửa sổ phá hủy cửa sổ của bạn, bạn không thể gọi XDestroyWindow, vì cửa sổ đã bị hủy và tay cầm Window không hợp lệ. Bạn sẽ gặp lỗi về số BadWindow. Bạn cũng không thể gọi XCloseDisplay, vì kết nối với X Server đã bị đóng và điều này sẽ gây ra lỗi XIO: fatal IO error 11 (Resource temporarily unavailable) on X server nhiều trải nghiệm người dùng từ các ứng dụng mà tác giả không biết điều đó. Đó là một sai lầm phổ biến, bởi vì trong một tay bạn được khuyến khích để làm sạch sau khi chính mình, nhưng mặt khác tài liệu hướng dẫn là gây hiểu lầm về cách thức này nên được thực hiện đúng cách.

Có một quy ước, tuy nhiên, về cách X Server và Trình quản lý cửa sổ nên hợp tác, cũng bao gồm việc trả lời các lệnh của người dùng để đóng cửa sổ cấp cao nhất. Có một phần mở rộng cho giao thức X xử lý nó. Dưới đây là cách Xlib documentation giải thích nó:

khách hàng, thường là những người có nhiều cửa sổ cấp cao nhất, có kết nối máy chủ phải tồn tại việc xóa một số cửa sổ cấp cao nhất của họ, nên bao gồm các nguyên tử WM_DELETE_WINDOW trong WM_PROTOCOLS tài sản trên mỗi cửa sổ như vậy. Họ sẽ nhận được sự kiện ClientMessage như được mô tả ở trên có trường data[0]WM_DELETE_WINDOW.
[...]
Khách hàng chọn không bao gồm WM_DELETE_WINDOW trong thuộc tính WM_PROTOCOLS có thể bị ngắt kết nối khỏi máy chủ nếu người dùng yêu cầu xóa một trong các cửa sổ cấp cao nhất của khách hàng.

Vì vậy, có hai giải pháp cho vấn đề này: hoặc tránh gọi XDestroyWindowXCloseDisplay khi cửa sổ của bạn đang được đóng bởi người quản lý cửa sổ và không một mình (bạn thực sự không cần phải dọn dẹp cửa sổ cấp cao vì X Server sẽ phá hủy nó khi chương trình của bạn kết thúc) hoặc bạn cần đăng ký tiện ích mở rộng WM_DESTROY_WINDOW và đợi thông báo từ Trình quản lý cửa sổ khi người dùng hướng dẫn đóng cửa sổ của bạn (nó sẽ gửi cho bạn sự kiện ClientMessage sau đó , với data[0] được đặt thành WM_DELETE_WINDOW). Và sau khi nhận được nó chỉ phá hủy cửa sổ và đóng kết nối với X Server, và kết thúc chương trình của bạn. Hoặc để kết nối với X Server mở để thực hiện một số giao tiếp với nó nếu bạn muốn. Khi bạn xử lý WM_DESTROY_WINDOW, Trình quản lý cửa sổ sẽ không cố gắng phá hủy cửa sổ của bạn cũng như không đóng kết nối với X Server.