2009-07-21 10 views
11

Tôi muốn chặn thông báo WM_DELETE_WINDOW được đăng lên một số cửa sổ nhất định mà một ứng dụng tôi đang viết (AllTray), để tôi có thể hành động thay vì ứng dụng nhận được. Tôi hiện đang xem thử việc này ở cấp độ GDK via gdk_display_add_client_message_filter nếu có thể, nhưng tôi sẽ hài lòng với giải pháp Xlib nếu có một giải pháp; nó có vẻ như để có thể, nhưng tôi dường như không hiểu cách tôi làm điều đó thành công.Chặn WM_DELETE_WINDOW trên X11?

Hiện nay, tôi có hai chương trình (viết bằng C) mà tôi đang cố gắng sử dụng để có được điều này đã tìm ra, the first one không làm gì nhưng tạo ra một cửa sổ và đăng ký mà nó biết về WM_DELETE_WINDOW, và the second one nỗ lực để bắt thông điệp rằng, nhưng dường như thất bại trong việc làm như vậy; nó dường như không làm gì cả. Tôi có hiểu tài liệu sai về điều này không, hoặc có điều gì đó bổ sung mà tôi cần phải làm (hay tôi cần phải tránh sử dụng GDK hoàn toàn cho điều này)?

Nền là: Trước khi viết lại AllTray, cách thức hoạt động của nó dường như là cố gắng đánh chặn một lần nhấp chuột vào nút X. Đối với một số người quản lý cửa sổ, điều này làm việc đúng, đối với những người khác nó không làm việc gì cả, và đối với những người khác, người dùng phải cấu hình nó bằng tay và hướng dẫn AllTray nơi nút đóng cửa sổ. Những gì tôi đang tìm kiếm là một giải pháp không liên quan đến LD_LIBRARY_PRELOAD và sẽ hoạt động đối với bất kỳ kết hợp trình quản lý/ứng dụng cửa sổ nào phù hợp với các tiêu chuẩn hiện tại và gửi WM_DELETE_WINDOW ClientMessage khi đóng cửa sổ.

CẬP NHẬT: Tôi vẫn đang tìm câu trả lời. Con đường mà tôi đang thực hiện vào lúc này là cố gắng sửa chữa cửa sổ và tự quản lý nó, nhưng tôi không thể làm cho nó hoạt động được. Khi reparenting, tôi dường như không thể lấy lại nó bằng bất kỳ cách nào. Tôi có thể thiếu một cái gì đó rất cơ bản, nhưng tôi không thể hiểu làm thế nào để thực sự làm cho nó xuất hiện nó cửa sổ của riêng tôi một lần nữa, để đưa nó trở lại trên màn hình.

CẬP NHẬT 2: Được rồi, vì vậy tôi đã nhấn vào một bức tường gạch khác. Tài liệu máy chủ X nói để đặt StructureNotifyMask trên mặt nạ sự kiện của cửa sổ để nhận cả sự kiện MapNotify và ReparentNotify. Tôi cũng muốn được nhận. Suy nghĩ hiện tại của tôi là tạo ra một cửa sổ phục vụ như một người nhận sự kiện, và sau đó khi tôi nhận được sự kiện cho những điều thú vị, hãy hành động bằng cách tạo và sửa lại. Tuy nhiên, điều này đơn giản dường như không hoạt động. Các sự kiện duy nhất tôi thực sự nhận được là sự kiện PropertyNotify. Vì vậy, tuyến đường này dường như cũng không hoạt động tốt lắm.

+0

Tôi nghĩ có thể bằng cách sửa đổi cửa sổ bên trong màn hình của riêng bạn và lọc những sự kiện bạn vượt qua? Tôi không nghĩ cách bạn đang cố gắng có thể hoạt động. – wrt

+0

Có bất kỳ nhược điểm nào để thực hiện theo cách đó không? Đó là, có bất cứ điều gì đặc biệt mà sẽ gây ra rằng để can thiệp vào những thứ như XDND hay bất cứ điều gì? Đó có phải là một ý tưởng di động (như trong, nó sẽ không phá vỡ các ứng dụng hoặc trình quản lý cửa sổ)? Tôi dường như có thể tìm thấy rất ít thông tin về điều đó. Tôi giả sử rằng nó cũng có nghĩa là tôi sẽ phải tạo một cửa sổ "cha mẹ" mới cho mỗi cửa sổ ứng dụng khách mới, đúng không? –

Trả lời

1

Thật không may, câu trả lời hay nhất cho câu hỏi này là một loạt câu trả lời không phải là câu trả lời; có những cách kỹ thuật để hoàn thành nó, nhưng tất cả chúng đều có những hạn chế khiến chúng cực kỳ không thực tế:

  1. Tạo một proxy X11 cho một ứng dụng, chuyển tất cả các thông điệp giao thức X11 qua lại giữa ứng dụng và máy chủ X. Sau đó, proxy sẽ lọc ra bất kỳ tin nhắn thú vị nào. Nhược điểm cho điều này là đây là một số tiền khủng khiếp của chi phí cho một tính năng nhỏ bé duy nhất, và giao thức X11 là phức tạp. Cũng có thể có những hậu quả không mong muốn, điều này khiến cho việc này trở nên không hấp dẫn hơn nữa.
  2. Khởi chạy dưới dạng ứng dụng chuẩn đóng vai trò trung gian giữa trình quản lý cửa sổ và ứng dụng khách "thú vị". Điều này phá vỡ một số điều, chẳng hạn như XDnD. Trong thực tế, nó không phải là không giống như tùy chọn đầu tiên, ngoại trừ việc proxy là ở cấp cửa sổ như trái ngược với mức giao thức X11.
  3. Sử dụng thủ thuật thư viện không phải di động LD_PRELOAD. Điều này có một số khác nhau các nhược điểm:
    1. Không thể di chuyển trên các liên kết động: không phải tất cả các liên kết động hỗ trợ LD_PRELOAD, ngay cả trong các hệ thống giống UNIX.
    2. Không thể di chuyển trên các hệ điều hành: không phải tất cả các hệ điều hành đều hỗ trợ các liên kết động đầy đủ tính năng.
    3. Nó phá vỡ tính minh bạch của mạng: thư viện liên kết/đối tượng được chia sẻ phải nằm trên máy chủ khi quá trình con đang được thực thi.
    4. Không phải tất cả các ứng dụng X11 đều sử dụng Xlib; nó sẽ là cần thiết để viết một mô-đun LD_PRELOAD cho mỗi thư viện mà một ứng dụng có thể sử dụng để nói chuyện với X11. Ngoài các điểm cuối cùng, không phải tất cả các ứng dụng sẽ dễ bị LD_PRELOAD ngay cả khi chúng chạy dưới một trình liên kết hỗ trợ nó, bởi vì chúng không thể sử dụng một đối tượng được chia sẻ hoặc DLL để giao tiếp với X; xem xét, ví dụ, một ứng dụng Java sử dụng một thư viện giao thức X11 được viết bằng Java.
    5. Trên một số hệ điều hành giống UNIX, LD_PRELOAD thư viện phải được setuid/setgid nếu chúng được sử dụng với các chương trình setuid/setgid. Đây là, tất nhiên, một lỗ hổng bảo mật tiềm năng.
    6. Tôi khá chắc chắn rằng có nhiều nhược điểm mà tôi không thể nghĩ đến.
  4. Triển khai tiện ích mở rộng cho hệ thống Cửa sổ X. Không di chuyển giữa các lần triển khai X11, phức tạp và phức tạp khi tất cả thoát ra ngoài và hoàn toàn nằm ngoài câu hỏi.
  5. Triển khai tiện ích hoặc trình cắm cho người quản lý cửa sổ. Có nhiều người quản lý cửa sổ vì có ý kiến ​​về người quản lý cửa sổ, và do đó điều này hoàn toàn không khả thi.

Cuối cùng, tôi đã có thể hoàn thành mục tiêu của mình bằng cách sử dụng một cơ chế hoàn toàn riêng biệt; bất kỳ ai quan tâm, hãy xem hỗ trợ Close-to-Tray trong AllTray 0.7.5.1dev và sau đó, bao gồm the git master branch available on github.

11

Tôi không biết X11, nhưng tôi đã googled sử dụng "Intercept WM_DELETE_WINDOW X11" làm từ khóa. Đã tìm thấy 17k - MarkMailMplayer-commits r154 - trunk/libvo. Trong cả hai trường hợp, họ đang làm điều tương tự.

/* This is used to intercept window closing requests. */ 
static Atom wm_delete_window; 

trong static void x11_init(),

XMapWindow(display, win); 
wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False); 
XSetWMProtocols(display, win, &wm_delete_window, 1); 

sau đó, trong vòng static int x11_check_events(),

XEvent Event; 
while (XPending(display)) { 
    XNextEvent(display, &Event); 
    if (Event.type == ClientMessage) { 
     if ((Atom)Event.xclient.data.l[0] == wm_delete_window) { 
      /* your code here */ 
     } 
    } 
} 

Xem XInternAtom, XSetWMProtocolsXNextEvent.

Sau khi tôi đã viết ở trên, tôi thấy Handling window close in an X11 app:

Khi người dùng nhấp vào nút đóng [x] trên ứng dụng X11 của chúng tôi, chúng tôi muốn nó để bật aa thoại hỏi “làm bạn thực sự muốn bỏ ? ”. Đây là ứng dụng đơn giản X. Không có tiện ích GTK hoặc QT ưa thích tại đây. Vậy làm cách nào để nắm bắt thông báo “cửa sổ là bị đóng”?

Câu trả lời là để nói với Window Manager chúng tôi quan tâm đến sự kiện này bằng cách gọi XSetWMProtocols và đăng ký một WM_DELETE_WINDOW nhắn với nó. Sau đó, chúng tôi sẽ nhận được thông báo của khách hàng từ Người quản lý cửa sổ nếu ai đó cố gắng đóng cửa sổ và sẽ không đóng cửa sổ, chúng tôi sẽ để chúng tôi tùy thuộc vào chúng tôi. Đây là một ví dụ….

// example.cpp 
#include <X11/Xlib.h> 
#include <X11/Xatom.h> 
#include <iostream> 

int main() 
{ 
    Display* display = XOpenDisplay(NULL); 
    Window window = XCreateSimpleWindow(display, 
             DefaultRootWindow(display), 
             0, 0, 
             500, 400, 
             0, 
             0, 0); 

    // register interest in the delete window message 
    Atom wmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False); 
    XSetWMProtocols(display, window, &wmDeleteMessage, 1); 

    std::cout << "Starting up..." << std::endl; 
    XMapWindow(display, window); 

    while (true) { 
     XEvent event; 
     XNextEvent(display, &event); 

     if (event.type == ClientMessage && 
      event.xclient.data.l[0] == wmDeleteMessage) { 
     std::cout << "Shutting down now!!!" << std::endl; 
     break; 
     } 
    } 

    XCloseDisplay(display); 
    return 0; 
} 
+0

Điều đó sẽ rất tuyệt ... nếu chỉ giải quyết được vấn đề của tôi. Vấn đề không phải là tôi sở hữu một cửa sổ và tôi muốn nhận sự kiện; rằng tôi có thể làm tốt (và sẽ làm một trong những cách trên). Vấn đề là ứng dụng của tôi cần chặn nó cho các cửa sổ khác. Windows không phải của tôi, mà tôi không tạo ra. Tôi vẫn đang cố gắng tìm hiểu xem người bình luận đầu tiên có nghĩa là gì bằng cách buộc phải sửa chữa cửa sổ, nhưng tôi vẫn chưa thực sự làm việc đó. Tôi đã googling trong nhiều ngày. Đó là lý do tôi đến đây. –

+0

Tôi giả sử bạn đã đọc http://tronche.com/gui/x/xlib/window-and-session-manager/XReparentWindow.html rồi. –

+0

Thật vậy, tôi có. Tôi vẫn không chắc chắn phải làm gì để làm cho nó hoạt động chính xác; Tôi đã quản lý để có được cửa sổ reparented, nhưng tôi phải mất một cái gì đó bởi vì tôi không thể đạt được mục tiêu của tôi làm cho mọi thứ hoạt động. : -/Tôi thực sự có tài liệu tham khảo Xlib trên bàn của tôi, mà tôi đã chải qua nhiều lần để cố gắng tìm ra điều này. : -/ –

0

Ok, để xây dựng trên đề nghị trước đây của tôi, bạn có thể muốn điều tra XEmbed. Ít nhất, điều đó có thể cung cấp cho bạn một số ý tưởng để thử.

Nếu không, tôi sẽ xem xét cách phần mềm tương tự khác có thể hoạt động (ví dụ: wmdock hoặc cách GtkPlug/GtkSocket được triển khai), mặc dù tôi tin rằng cả hai trường hợp đó đều cần hỗ trợ rõ ràng trong các ứng dụng.

Hy vọng điều đó hữu ích hơn.

+0

Tôi đã thử nghiệm với điều này tại một thời điểm; tuy nhiên, nó rất _very_ không đáng tin cậy. Nó không đáng tin cậy như là không thích hợp cho một chương trình sản xuất. Tôi đã may mắn tìm được một cách khác để hoàn thành mục tiêu cuối cùng mà tôi đã nghĩ đến. –

0

Bạn nên đọc ICCCM cho bạn biết cách trình quản lý cửa sổ liên lạc với khách hàng. Hầu hết WM sẽ tạo ra một cửa sổ khung để chứa cửa sổ cấp cao nhất của bạn thông qua reparenting. Vì vậy, nếu reparent của bạn có thể phá vỡ mối quan hệ được biết đến bởi WM và cửa sổ khách hàng của bạn.

+1

Cảm ơn. Tôi đã đọc ICCCM và một chút công bằng hơn về X11. Reparenting không phải là một tùy chọn mà không có * lot * công việc. Old AllTray đã sửa đổi thủ đoạn, điều này đã phá vỡ rất nhiều thứ trong quá trình; ví dụ, XDnD. –