2009-07-21 100 views
11

我想攔截WM_DELETE_WINDOW消息,該消息發佈到我正在編寫的應用程序(AllTray)的某個特定窗口選擇中,以便我可以對其執行操作,而不是接收應用程序。如果可能的話,我目前正在考慮在GDK級別via gdk_display_add_client_message_filter上嘗試此操作,但如果有Xlib解決方案,我也會很高興;它似乎是可能的,但我似乎並沒有理解我是如何成功地做到這一點。在X11上攔截WM_DELETE_WINDOW?

目前,我有兩個程序(用C編寫的),我想用它來得到這個想通了,the first one什麼也不做,但創建一個窗口,並註冊它知道WM_DELETE_WINDOWthe second one企圖趕上消息,但似乎失敗了;它似乎沒有做任何事情。我是否理解錯誤的文檔,或者是否有其他需要做的事情(或者我需要完全避免使用GDK)?

背景是這樣的:在我重寫AllTray之前,它會做的事情似乎是嘗試攔截鼠標單擊X按鈕本身。對於一些窗口管理器來說,這可以正常工作,對於其他人來說根本無法工作,對於其他人,用戶必須手動配置它,並指示AllTray關閉窗口的按鈕。我正在尋找的是一個不涉及LD_LIBRARY_PRELOAD的解決方案,它適用於任何符合當前標準的窗口管理器/應用程序組合,並在窗口關閉時發送ClientMessage。

UPDATE:我仍在尋找答案。我目前所採取的路線是嘗試重新放置窗戶並自己管理,但我無法使其工作。重新裝修後,我似乎無法以任何方式恢復原狀。我可能會錯過一些非常基本的東西,但我無法弄清楚如何讓它再次出現在我自己的窗口中,將它重新顯示在屏幕上。

UPDATE 2:好的,所以我撞上了另一堵磚牆。 X服務器文檔說在窗口的事件掩碼上設置StructureNotifyMask以接收MapNotify和ReparentNotify事件。我有興趣收到。我目前的想法是創建一個窗口,作爲一個事件接收器,然後當我得到有趣的事件的事件,通過創建和重新制定行動。但是,這似乎並不奏效。我實際收到的唯一事件是PropertyNotify事件。所以,這條路線似乎也不怎麼樣。

+0

我認爲這可能是通過重新設置你自己的頂層窗口並過濾你傳遞的事件嗎?我不認爲你現在正在努力的方式可以工作。 – wrt 2009-07-23 21:39:59

+0

這樣做有什麼缺點嗎?也就是說,有什麼特別的東西會導致干擾XDND之類的東西?這是一個可移植的想法嗎?(因爲它不會破壞應用程序或窗口管理器)? 我似乎能夠找到非常少的信息。我認爲這也意味着我將不得不爲每個新的客戶端窗口創建一個新的「父窗口」,對嗎? – 2009-07-23 23:10:32

回答

1

不幸的是,這個問題的最佳答案是一系列的非答案;有技術上的方式來完成它,但他們都有挫折,使他們非常不切實際:

  1. 爲應用程序創建的X11代理,通過所有X11協議消息來回的應用程序和X服務器之間。代理會過濾掉任何有趣的消息。這樣做的缺點是這對於一個小小的特性來說是一個非常大的開銷,而且X11協議非常複雜。也可能會產生意想不到的後果,這使得這更加沒有吸引力。
  2. 作爲標準應用程序啓動,充當窗口管理器和「有趣」客戶端應用程序之間的中介。這打破了一些東西,比如XDnD。實際上,它與第一個選項不同,除了代理位於Window級別而不是X11協議級別。
  3. 使用非便攜式LD_PRELOAD庫技巧。這有幾個缺點
    1. ,對面動態鏈接程序會不可移植的:不是所有的動態鏈接器支持LD_PRELOAD,即使在類UNIX系統。
    2. 它是跨操作系統不可移植的:並非所有操作系統都支持有特色的動態鏈接器。
    3. 它破壞了網絡透明性:共享對象/動態鏈接庫必須作爲正在執行的子進程駐留在主機上。
    4. 並非所有X11應用程序都使用Xlib;有必要爲每個應用程序可能用來與X11交談的庫編寫一個LD_PRELOAD模塊。
    5. 除了最後一點,並非所有應用程序都會受到LD_PRELOAD的影響,即使它們在支持它的鏈接器下運行,因爲它們可能不會使用共享對象或DLL來與X通信;例如,考慮使用Java本身編寫的X11協議庫的Java應用程序。
    6. 在某些類UNIX操作系統上,如果要將其與setuid/setgid程序一起使用,則必須將它們的setuid/setgid設置爲LD_PRELOAD。這當然是一個潛在的安全漏洞。
    7. 我相當肯定,我想不到更多的缺點。
  4. 實現對X Window系統的擴展。在X11實現中不可移植,複雜而複雜,因爲所有問題都出來了,絕對沒有問題。
  5. 爲窗口管理器實現擴展或插件。窗口管理者的意見和窗口管理者一樣多,因此這是完全不可行的。

最終,我終於通過完全獨立的機制實現了我的目標;任何有興趣的人,請參閱AllTray 0.7.5.1dev及更高版本中的Close-to-Tray支持,包括the git master branch available on github

11

我不知道X11,但我使用"Intercept WM_DELETE_WINDOW X11"作爲關鍵字進行搜索。發現17k - MarkMailMplayer-commits r154 - trunk/libvo。在這兩種情況下,他們都在做同樣的事情。

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

static void x11_init()

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

然後,內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 */ 
     } 
    } 
} 

參見XInternAtomXSetWMProtocolsXNextEvent

後,我寫了上面,我發現Handling window close in an X11 app

當用戶點擊我們的X11應用程序的關閉按鈕 [x]我們希望它 流行AA對話框,詢問「你 真的要退出?」。這是一個普通的 X應用程序。沒有花哨的GTK或QT小部件 在這裏。那麼如何趕上「窗口被關閉」消息呢?

答案是通過調用XSetWMProtocols和 註冊WM_DELETE_WINDOW消息 它告訴窗口 經理,我們感興趣的是這些 事件。然後我們會從窗口管理器獲得一個客戶端 消息,如果 有人試圖關閉窗口,並且 它不會關閉它,它會讓我們 由我們決定。這是一個例子...。

// 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

這將是偉大的...只要它解決了我的問題。 問題不在於我擁有一個窗口,而是我想要接收事件;我可以做得很好(並且會採用上述方法之一)。問題是我的應用程序需要攔截其他窗口。不是我的Windows,我沒有創建。我仍然試圖弄清楚第一個評論者強行重新設計窗戶的意義,但我還沒有真正實現這個目標。 我一直在Google上搜尋幾天。這就是我來到這裏的原因。 – 2009-07-27 05:52:38

+0

我假設您已經閱讀過http://tronche.com/gui/x/xlib/window-and-session-manager/XReparentWindow.html。 – 2009-07-27 14:37:15

0

好,闡述我早先提出的建議,您可能需要調查XEmbed。至少,這可能會給你一些想法嘗試。

如果不這樣做,我會看看其他類似的軟件如何工作(例如,wmdock,或如何實現GtkPlug/GtkSocket),但我相信在這些情況下,應用程序都需要明確的支持。

希望這是更有幫助。

0

您應該閱讀ICCCM,告訴您窗口管理器如何與客戶端進行通信。大多數WM將創建一個框架窗口,通過重新設置來包含頂層窗口。因此,如果您的重新投資可能會破壞WM和客戶窗口已知的關係。