winex11: Add minimal XEmbed client support

Sebastian Lackner sebastian at fds-team.de
Thu Aug 29 20:17:06 CDT 2013


This patch adds XEmbed client support as described in the corresponding
protocol specification. This allows external applications to re-parent a
wine window inside any X11 window with proper event handling. The code
has been developed as part of the Pipelight project, which allows to
embed (for example) Silverlight directly into the native Linux browser.

The protocol specification can be seen for example here:
http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html

Please note that this is still a bit tricky for several reasons:

* The wine application cannot do the re-parent itself, but instead an
other application has to do that and moreover also has to generate the
appropriate events during the initialization. You can take a look at
this code used in our project:
https://bitbucket.org/mmueller2012/pipelight/src/v0.1-2/src/linux/pluginfunctions.c?at=master#cl-683

* This patch doesn't implement all possible XEMBED_* messages, but only
a minimal set which is required to get the event handling working
appropriately. TAB chain handling or accelerator support for example is
still missing.

---
 dlls/winex11.drv/event.c |   88
++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 86 insertions(+), 2 deletions(-)
-------------- next part --------------
>From 4186029c0359c8da0251c6eed98b17161ae02db2 Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian at fds-team.de>
Date: Fri, 30 Aug 2013 01:13:08 +0200
Subject: winex11: Add minimal XEmbed client support

---
 dlls/winex11.drv/event.c |   88 ++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 86 insertions(+), 2 deletions(-)

diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c
index 5e5b19e..2bc3583 100644
--- a/dlls/winex11.drv/event.c
+++ b/dlls/winex11.drv/event.c
@@ -182,6 +182,32 @@ static inline void free_event_data( XEvent *event )
 #endif
 }
 
+
+/***********************************************************************
+ *           sendXembedMessage
+ *
+ * Sends an XEMBED event according to the specification.
+ * See: http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html
+ */
+void sendXembedMessage(Display* display, Window win, long message, long detail, long data1, long data2){
+    XEvent ev;
+    memset(&ev, 0, sizeof(ev));
+
+    ev.xclient.type         = ClientMessage;
+    ev.xclient.window       = win;
+    ev.xclient.message_type = XInternAtom(display, "_XEMBED", False);
+    ev.xclient.format       = 32;
+
+    ev.xclient.data.l[0]    = CurrentTime;
+    ev.xclient.data.l[1]    = message;
+    ev.xclient.data.l[2]    = detail;
+    ev.xclient.data.l[3]    = data1;
+    ev.xclient.data.l[4]    = data2;
+
+    XSendEvent(display, win, False, NoEventMask, &ev);
+    XSync(display, False);
+}
+
 /***********************************************************************
  *           X11DRV_register_event_handler
  *
@@ -562,6 +588,7 @@ static void set_focus( Display *display, HWND hwnd, Time time )
     HWND focus;
     Window win;
     GUITHREADINFO threadinfo;
+    struct x11drv_win_data *data;
 
     TRACE( "setting foreground window to %p\n", hwnd );
     SetForegroundWindow( hwnd );
@@ -576,7 +603,19 @@ static void set_focus( Display *display, HWND hwnd, Time time )
     if (win)
     {
         TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
-        XSetInputFocus( display, win, RevertToParent, time );
+
+        /*  Note: When a window has an embedder it is not allowed to just take the XInputFocus, but instead
+            we have to request the embedder to redirect the messages for us. */
+
+        data = get_win_data( hwnd );
+
+        if(!data || !data->embedder){
+            XSetInputFocus( display, win, RevertToParent, time );
+        }else{
+            sendXembedMessage( display, data->embedder, XEMBED_REQUEST_FOCUS, 0, 0, 0);
+        }
+
+        if(data) release_win_data(data);
     }
 }
 
@@ -1337,7 +1376,17 @@ void CDECL X11DRV_SetFocus( HWND hwnd )
 
     if (!(hwnd = GetAncestor( hwnd, GA_ROOT ))) return;
     if (!(data = get_win_data( hwnd ))) return;
-    if (!data->managed) set_input_focus( data->display, data->whole_window );
+
+    /*  Note: When a window has an embedder it is not allowed to just take the XInputFocus, but instead
+        we have to request the embedder to redirect the messages for us. */
+
+    if(data->embedder){
+        sendXembedMessage( data->display, data->embedder, XEMBED_REQUEST_FOCUS, 0, 0, 0);
+
+    }else if (!data->managed){
+        set_input_focus( data->display, data->whole_window );
+    }
+
     release_win_data( data );
 }
 
@@ -1598,16 +1647,51 @@ static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
  */
 static void handle_xembed_protocol( HWND hwnd, XClientMessageEvent *event )
 {
+    XFocusChangeEvent focusEvent;
     struct x11drv_win_data *data = get_win_data( hwnd );
 
     if (!data) return;
 
     switch (event->data.l[1])
     {
+
     case XEMBED_EMBEDDED_NOTIFY:
+        /* embedder -> client: reparenting the window finished */
         TRACE( "win %p/%lx XEMBED_EMBEDDED_NOTIFY owner %lx\n", hwnd, event->window, event->data.l[3] );
+
         data->embedder = event->data.l[3];
+        data->embedded = TRUE;
         break;
+
+    case XEMBED_FOCUS_OUT:
+        /* embedder -> client: client looses focus */
+    case XEMBED_WINDOW_DEACTIVATE:
+        /* embedder -> client: client looses keyboard focus */
+        TRACE( "win %p/%lx XEMBED_FOCUS_OUT or XEMBED_WINDOW_DEACTIVATE message\n", hwnd, event->window);
+
+        /* we just simulate a FocusOut event */
+        focusEvent.type         = FocusOut;
+        focusEvent.serial       = event->serial;
+        focusEvent.send_event   = event->send_event;
+        focusEvent.display      = event->display;
+        focusEvent.window       = event->window;
+        focusEvent.mode         = NotifyNormal;
+        focusEvent.detail       = NotifyNonlinear;
+        X11DRV_FocusOut(hwnd, &focusEvent);
+        break;
+
+    case XEMBED_MODALITY_ON:
+        /* embedder -> client: enable ignore mouse input */
+        TRACE( "win %p/%lx XEMBED_MODALITY_ON message\n", hwnd, event->window);
+        EnableWindow(hwnd, FALSE);
+        break;
+
+    case XEMBED_MODALITY_OFF:
+        /* embedder -> client: disable ignore mouse input */
+        TRACE( "win %p/%lx XEMBED_MODALITY_OFF message\n", hwnd, event->window);
+        EnableWindow(hwnd, TRUE);
+        break;
+
     default:
         TRACE( "win %p/%lx XEMBED message %lu(%lu)\n",
                hwnd, event->window, event->data.l[1], event->data.l[2] );
-- 
1.7.9.5



More information about the wine-patches mailing list