Added XEmbed embedder support

Jacek Caban jack at itma.pwr.wroc.pl
Sun Jun 19 17:05:41 CDT 2005


Hello.

This patch adds support for XEmbed embedder
It implements most of it (remaining todos
are listed in comments of xembed.c). This patch
make it possible to implement MSHTML
using Linux Gecko (at last it is implemented in
my tree). To create an embedder, the application
has to create a window of _XEMBED class. Then the
X window is created as a child of the whole window.
This is "embedder window", that has to be kept
sync with "Wine window". To do so, for every HWND,
we store a counter of child windows being embedder.
Functions like SetWindowPos can simply check if the
counter is positive and if so, sync every child. This
way, if I missed something (eg. I'm not sure if resizing
should be changed), it's easy to add more sync code.
XEmbed client is just a child of the embedder and
there is not that much to do with it. It is completely
untested in the desktop mode - I think we can take
care of it later.

Changelog:
    Added XEmbed embedder support
-------------- next part --------------
? dlls/x11drv/bak
? dlls/x11drv/diff
? dlls/x11drv/log
? dlls/x11drv/xembed.c
Index: dlls/user/user_main.c
===================================================================
RCS file: /home/wine/wine/dlls/user/user_main.c,v
retrieving revision 1.85
diff -u -p -r1.85 user_main.c
--- dlls/user/user_main.c	13 Jun 2005 18:56:01 -0000	1.85
+++ dlls/user/user_main.c	19 Jun 2005 21:41:06 -0000
@@ -260,6 +260,9 @@ static BOOL process_attach(void)
     /* some Win9x dlls expect keyboard to be loaded */
     if (GetVersion() & 0x80000000) LoadLibrary16( "keyboard.drv" );
 
+    /* Initialize built-in window classes */
+    CLASS_RegisterBuiltinClasses();
+
     /* Load the graphics driver */
     if (!load_driver()) return FALSE;
 
@@ -268,9 +271,6 @@ static BOOL process_attach(void)
 
     /* Setup palette function pointers */
     palette_init();
-
-    /* Initialize built-in window classes */
-    CLASS_RegisterBuiltinClasses();
 
     /* Initialize menus */
     if (!MENU_Init()) return FALSE;
Index: dlls/x11drv/Makefile.in
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/Makefile.in,v
retrieving revision 1.43
diff -u -p -r1.43 Makefile.in
--- dlls/x11drv/Makefile.in	6 May 2005 19:38:50 -0000	1.43
+++ dlls/x11drv/Makefile.in	19 Jun 2005 21:41:07 -0000
@@ -40,6 +40,7 @@ C_SRCS = \
 	xdnd.c \
 	xfont.c \
 	xim.c \
+	xembed.c \
 	xrandr.c \
 	xrender.c \
 	xvidmode.c
Index: dlls/x11drv/event.c
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/event.c,v
retrieving revision 1.55
diff -u -p -r1.55 event.c
--- dlls/x11drv/event.c	1 Jun 2005 11:08:39 -0000	1.55
+++ dlls/x11drv/event.c	19 Jun 2005 21:41:07 -0000
@@ -246,6 +246,14 @@ static int process_events( Display *disp
         count++;
         if (XFilterEvent( &event, None )) continue;  /* filtered, ignore it */
 
+        if(!XFindContext( display, event.xany.window, winXEmbedContext, (char **)&hwnd)) {
+            /* XEmbed embedder window */
+            wine_tsx11_unlock();
+            XEMBED_event(hwnd, &event);
+            wine_tsx11_lock();
+            continue;
+        }
+
         if (!(handler = find_handler( event.type )))
         {
             TRACE( "%s, ignoring\n", dbgstr_event( event.type ));
@@ -521,7 +529,8 @@ static void EVENT_FocusOut( HWND hwnd, X
     XGetInputFocus( thread_display(), &focus_win, &revert );
     if (focus_win)
     {
-        if (XFindContext( thread_display(), focus_win, winContext, (char **)&hwnd_tmp ) != 0)
+        if (XFindContext( thread_display(), focus_win, winContext, (char **)&hwnd_tmp ) != 0
+                && XFindContext( thread_display(), focus_win, winXEmbedContext, (char **)&hwnd_tmp) != 0)
             focus_win = 0;
     }
     wine_tsx11_unlock();
@@ -874,7 +883,6 @@ static void handle_dnd_protocol( HWND hw
     else if (event->data.l[0] == DndURL)
         EVENT_DropURLs(hwnd, event);
 }
-
 
 struct client_message_handler
 {
Index: dlls/x11drv/window.c
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/window.c,v
retrieving revision 1.109
diff -u -p -r1.109 window.c
--- dlls/x11drv/window.c	24 May 2005 11:44:59 -0000	1.109
+++ dlls/x11drv/window.c	19 Jun 2005 21:41:07 -0000
@@ -97,6 +97,8 @@ static const char * const atom_names[NB_
     "XdndSelection",
     "XdndTarget",
     "XdndTypeList",
+    "_XEMBED",
+    "_XEMBED_INFO",
     "WCF_DIB",
     "image/gif",
     "text/html",
@@ -902,6 +904,7 @@ BOOL X11DRV_CreateWindow( HWND hwnd, CRE
     data->dce           = NULL;
     data->hWMIconBitmap = 0;
     data->hWMIconMask   = 0;
+    data->xembed.cnt    = 0;
 
     wine_tsx11_lock();
     if (!win_data_context) win_data_context = XUniqueContext();
@@ -1103,7 +1106,8 @@ HWND X11DRV_SetParent( HWND hwnd, HWND p
     WND *wndPtr;
     BOOL ret;
     HWND old_parent = 0;
-
+    struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
+    
     /* Windows hides the window first, then shows it again
      * including the WM_SHOWWINDOW messages and all */
     BOOL was_visible = ShowWindow( hwnd, SW_HIDE );
@@ -1128,8 +1132,6 @@ HWND X11DRV_SetParent( HWND hwnd, HWND p
 
     if (parent != old_parent)
     {
-        struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
-
         if (!data) return 0;
 
         if (parent != GetDesktopWindow()) /* a child window */
@@ -1152,6 +1154,9 @@ HWND X11DRV_SetParent( HWND hwnd, HWND p
             create_whole_window( display, data, GetWindowLongW( hwnd, GWL_STYLE ) );
         }
     }
+
+    if(data->xembed.cnt)
+        XEMBED_reparent(hwnd, parent, old_parent);
 
     /* SetParent additionally needs to make hwnd the topmost window
        in the x-order and send the expected WM_WINDOWPOSCHANGING and
Index: dlls/x11drv/winpos.c
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/winpos.c,v
retrieving revision 1.134
diff -u -p -r1.134 winpos.c
--- dlls/x11drv/winpos.c	17 Jun 2005 21:05:16 -0000	1.134
+++ dlls/x11drv/winpos.c	19 Jun 2005 21:41:08 -0000
@@ -677,6 +677,7 @@ BOOL X11DRV_SetWindowPos( WINDOWPOS *win
 {
     RECT newWindowRect, newClientRect, valid_rects[2];
     UINT orig_flags;
+    struct x11drv_win_data *data;
 
     TRACE( "hwnd %p, after %p, swp %d,%d %dx%d flags %08x\n",
            winpos->hwnd, winpos->hwndInsertAfter, winpos->x, winpos->y,
@@ -752,6 +753,10 @@ BOOL X11DRV_SetWindowPos( WINDOWPOS *win
         winpos->cy = newWindowRect.bottom - newWindowRect.top;
         SendMessageW( winpos->hwnd, WM_WINDOWPOSCHANGED, 0, (LPARAM)winpos );
     }
+
+    data = X11DRV_get_win_data(winpos->hwnd);
+    if(data->xembed.cnt)
+        XEMBED_update_child_pos(winpos->hwnd, winpos->flags);
 
     return TRUE;
 }
Index: dlls/x11drv/x11drv.h
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/x11drv.h,v
retrieving revision 1.75
diff -u -p -r1.75 x11drv.h
--- dlls/x11drv/x11drv.h	14 Jun 2005 18:12:15 -0000	1.75
+++ dlls/x11drv/x11drv.h	19 Jun 2005 21:41:09 -0000
@@ -287,6 +287,17 @@ extern void X11DRV_XDND_PositionEvent( H
 extern void X11DRV_XDND_DropEvent( HWND hWnd, XClientMessageEvent *event );
 extern void X11DRV_XDND_LeaveEvent( HWND hWnd, XClientMessageEvent *event );
 
+struct xembed_list
+{
+    struct xembed_list *next;
+    HWND hwnd;
+};
+
+extern void XEMBED_update_child_pos(HWND, UINT);
+extern void XEMBED_reparent(HWND, HWND, HWND);
+extern void XEMBED_event(HWND, XEvent*);
+extern void XEMBED_init();
+
 /* exported dib functions for now */
 
 /* DIB Section sync state */
@@ -590,6 +601,8 @@ enum x11drv_atoms
     XATOM_XdndSelection,
     XATOM_XdndTarget,
     XATOM_XdndTypeList,
+    XATOM__XEMBED,
+    XATOM__XEMBED_INFO,
     XATOM_WCF_DIB,
     XATOM_image_gif,
     XATOM_text_html,
@@ -649,6 +662,10 @@ struct x11drv_win_data
     struct dce *dce;            /* DCE for CS_OWNDC or CS_CLASSDC windows */
     HBITMAP     hWMIconBitmap;
     HBITMAP     hWMIconMask;
+    union {
+        struct xembed_list *list;
+        ULONG_PTR cnt;
+    } xembed;
 };
 
 extern struct x11drv_win_data *X11DRV_get_win_data( HWND hwnd );
@@ -662,6 +679,7 @@ extern void invalidate_dce( HWND hwnd, c
 
 /* X context to associate a hwnd to an X window */
 extern XContext winContext;
+extern XContext winXEmbedContext;
 
 extern void X11DRV_InitClipboard(void);
 extern void X11DRV_AcquireClipboard(HWND hWndClipWindow);
Index: dlls/x11drv/x11drv_main.c
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/x11drv_main.c,v
retrieving revision 1.107
diff -u -p -r1.107 x11drv_main.c
--- dlls/x11drv/x11drv_main.c	16 Jun 2005 16:14:46 -0000	1.107
+++ dlls/x11drv/x11drv_main.c	19 Jun 2005 21:41:09 -0000
@@ -397,6 +397,9 @@ static BOOL process_attach(void)
 
     X11DRV_InitKeyboard();
 
+    /* initialize XEmbed */
+    XEMBED_init();
+
     return TRUE;
 }
 
@@ -497,7 +500,6 @@ struct x11drv_thread_data *x11drv_init_t
     if (desktop_tid) AttachThreadInput( GetCurrentThreadId(), desktop_tid, TRUE );
     return data;
 }
-
 
 /***********************************************************************
  *           X11DRV initialisation routine
--- /dev/null	1970-01-01 01:00:00.000000000 +0100
+++ dlls/x11drv/xembed.c	2005-06-19 23:43:46.000000000 +0200
@@ -0,0 +1,628 @@
+/*
+ * Copyright 2005 Jacek Caban
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * 
+ * TODO:
+ *   - Implement Wine window as XEmbed client
+ *   - Implement accelerators
+ *   - Better focus handling
+ *   - Implement XReparentNotify so that client can be attached calling XReparentWindow itself
+ */
+
+#include "config.h"
+
+#include <X11/Xlib.h>
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wine/debug.h"
+
+#include "x11drv.h"
+
+#define XEMBED_EMBEDDED_NOTIFY         0
+#define XEMBED_WINDOW_ACTIVATE         1
+#define XEMBED_WINDOW_DEACTIVATE       2
+#define XEMBED_REQUEST_FOCUS           3
+#define XEMBED_FOCUS_IN                4
+#define XEMBED_FOCUS_OUT               5
+#define XEMBED_FOCUS_NEXT              6
+#define XEMBED_FOCUS_PREV              7
+#define XEMBED_MODALITY_ON             10
+#define XEMBED_MODALITY_OFF            11
+#define XEMBED_REGISTER_ACCELERATOR    12
+#define XEMBED_UNREGISTER_ACCELERATOR  13
+#define XEMBED_ACTIVATE_ACCELERATOR    14
+
+#define XEMBED_FOCUS_CURRENT  0
+#define XEMBED_FOCUS_FIRST    1
+#define XEMBED_FOCUS_LAST     2
+
+#define XEMBED_MAPPED 1 
+
+#define WM_ATTACH_XWINDOW WM_USER+600
+
+WINE_DEFAULT_DEBUG_CHANNEL(xembed);
+
+static const WCHAR wszClientWindow[] = 
+    {'_','_','w','i','n','e','_','x','1','1','_','c','l','i','e','n','t','_','w','i','n','d','o','w',0};
+static const WCHAR wszEmbedWindow[] =
+    {'_','_','w','i','n','e','_','x','1','1','_','e','m','b','e','d','d','e','r','_','w','i','n','d','o','w',0};
+
+XContext winXEmbedContext = 0;
+
+static void xembed_list_add(struct x11drv_win_data *data, HWND hwnd)
+{
+    struct xembed_list *new_elem = HeapAlloc(GetProcessHeap(), 0, sizeof(struct xembed_list));
+
+    new_elem->hwnd = hwnd;
+    new_elem->next = data->xembed.list;
+    data->xembed.list = new_elem;
+}
+
+static void xembed_list_remove(struct x11drv_win_data *data, HWND hwnd)
+{
+    struct xembed_list *iter;
+
+    if(!(iter = data->xembed.list))
+        return;
+
+    if(iter->hwnd == hwnd) {
+        data->xembed.list = iter->next;
+        HeapFree(GetProcessHeap(), 0, iter);
+    }else {
+        while(iter->next && iter->next->hwnd != hwnd)
+            iter = iter->next;
+        if(iter->next) {
+            struct xembed_list *tmp = iter->next;
+            iter->next = tmp->next;
+            HeapFree(GetProcessHeap(), 0, tmp);
+        }
+    }
+}
+
+static Window get_client_window(HWND hwnd)
+{
+    return (Window)GetPropW(hwnd, wszClientWindow);
+}
+
+static void set_client_window(HWND hwnd, Window window)
+{
+    SetPropW(hwnd, wszClientWindow, (LPVOID)window);
+}
+
+static Window get_embedder_window(HWND hwnd)
+{
+    return (Window)GetPropW(hwnd, wszEmbedWindow);
+}
+
+static void set_embedder_window(HWND hwnd, Window window)
+{
+    SetPropW(hwnd, wszEmbedWindow, (LPVOID)window);
+}
+
+static void check_xembed_info(Display *display, Window win, unsigned long *version, long unsigned *flags)
+{
+    Atom type;
+    int format, res;
+    unsigned long nitems, bytes_after, *data;
+
+    wine_tsx11_lock();
+    res = XGetWindowProperty(display, win, x11drv_atom(_XEMBED_INFO), 0, 2, False,
+            x11drv_atom(_XEMBED_INFO), &type, &format, &nitems, &bytes_after, (unsigned char**)&data);
+
+    if(res != Success || !format) {
+        XFree(data);
+        wine_tsx11_unlock();
+        WARN("Could not get _XEMBED_INFO\n");
+        return;
+    }
+
+    *version = data[0];
+    *flags = data[1];
+
+    XFree(data);
+    wine_tsx11_unlock();
+
+    TRACE("got version=%08lx flags=%08lx\n", data[0], data[1]);
+}
+
+static BOOL is_window_visible(HWND hwnd, HWND skip_hwnd)
+{
+    struct x11drv_win_data *data = X11DRV_get_win_data(hwnd);
+
+    while(!data->whole_window) {
+        if(data->hwnd != skip_hwnd && !(GetWindowLongW(data->hwnd, GWL_STYLE) & WS_VISIBLE))
+            return FALSE;
+        data = X11DRV_get_win_data(GetParent(data->hwnd));
+    }
+
+    return TRUE;
+}
+
+static void update_winpos(HWND hwnd, HWND whole_win_hwnd, HWND update_hwnd, UINT flags)
+{
+    Display *display = ((struct x11drv_thread_data*)TlsGetValue(thread_data_tls_index))->display;
+    Window window;
+
+    if(!(flags & SWP_NOMOVE)) {
+        WINDOWINFO wininfo, whole_win_info;
+
+        TRACE("moving window %p\n", hwnd);
+
+        wininfo.cbSize = sizeof(WINDOWINFO);
+        whole_win_info.cbSize = sizeof(WINDOWINFO);
+        GetWindowInfo(whole_win_hwnd, &whole_win_info);
+        GetWindowInfo(hwnd, &wininfo);
+
+        window = get_embedder_window(hwnd);
+
+        wine_tsx11_lock();
+        XMoveWindow(display, window,
+                wininfo.rcClient.left - whole_win_info.rcClient.left,
+                wininfo.rcClient.top - whole_win_info.rcClient.top);
+        wine_tsx11_unlock();
+    }
+
+    if(!(flags & SWP_NOSIZE)) {
+        RECT rect;
+
+        TRACE("resizing window %p\n", hwnd);
+
+        GetClientRect(hwnd, &rect);
+        window = get_embedder_window(hwnd);
+
+        wine_tsx11_lock();
+        XResizeWindow(display, window, rect.right, rect.bottom);
+        wine_tsx11_unlock();
+    }
+
+    if(flags & SWP_HIDEWINDOW) {
+        TRACE("hidding window %p\n", hwnd);
+
+        window = get_embedder_window(hwnd);
+        wine_tsx11_lock();
+        XUnmapWindow(display, window);
+        wine_tsx11_unlock();
+    }else if(flags & SWP_SHOWWINDOW && is_window_visible(hwnd, update_hwnd)) {
+        TRACE("showing window %p\n", hwnd);
+
+        window = get_embedder_window(hwnd);
+        wine_tsx11_lock();
+        XMapWindow(display, window);
+        wine_tsx11_unlock();
+    }
+}
+
+static void send_xembed_event(Display *display, Window window, int message,
+        unsigned long detail, unsigned long data1, unsigned long data2)
+{
+    XClientMessageEvent event;
+
+    event.type = ClientMessage;
+    event.window = window;
+    event.message_type = x11drv_atom(_XEMBED);
+    event.format = 32;
+    event.data.l[0] = CurrentTime;
+    event.data.l[1] = message;
+    event.data.l[2] = detail;
+    event.data.l[3] = data1;
+    event.data.l[4] = data2;
+
+    wine_tsx11_lock();
+    XSendEvent(display, window, False, NoEventMask, (XEvent*)&event);
+    wine_tsx11_unlock();
+}
+
+static void attach_window(HWND hwnd, Window window) {
+    Display *display = ((struct x11drv_thread_data*)TlsGetValue(thread_data_tls_index))->display;
+    unsigned long version = 0, flags = XEMBED_MAPPED;
+    RECT rect;
+
+    TRACE("(%p %08lx)\n", hwnd, window);
+
+    if(get_client_window(hwnd)) {
+        WARN("Embedder alredy has client\n");
+        return;
+    }
+
+    set_client_window(hwnd, window);
+
+    check_xembed_info(display, window, &version, &flags);
+
+    /* FIXME: handle XEmbed version correctly (while it is only version 0 it's not really important) */
+    send_xembed_event(display, window, XEMBED_EMBEDDED_NOTIFY, 0, get_embedder_window(hwnd), 0);
+
+    if(!(flags & XEMBED_MAPPED)) {
+        unsigned int prop[] = {0, XEMBED_MAPPED};
+        wine_tsx11_lock();
+        XChangeProperty(display, window, x11drv_atom(_XEMBED_INFO), XA_CARDINAL,
+                32, PropModeReplace, (unsigned char*)prop, 2);
+        wine_tsx11_unlock();
+    }
+
+    GetClientRect(hwnd, &rect);
+
+    wine_tsx11_lock();
+    XMapWindow(display, window);
+    XMoveResizeWindow(display, window, 0, 0, rect.right, rect.bottom);
+    wine_tsx11_unlock();
+
+    send_xembed_event(display, window, XEMBED_WINDOW_ACTIVATE, 0, 0, 0);
+
+    if(GetFocus() == hwnd)
+        send_xembed_event(display, window, XEMBED_FOCUS_IN, XEMBED_FOCUS_FIRST, 0, 0);
+}
+
+static void detach_window(HWND hwnd)
+{
+    Window client = get_client_window(hwnd);
+
+    TRACE("(%p) client=%08lx\n", hwnd, client);
+
+    if(client)
+        set_client_window(hwnd, 0);
+}
+
+static void xembed_window_create(HWND hwnd)
+{
+    struct x11drv_win_data *data;
+    Window window;
+    Display * display = ((struct x11drv_thread_data*)TlsGetValue(thread_data_tls_index))->display;
+    XSetWindowAttributes attr;
+
+    TRACE("(%p)\n", hwnd);
+
+    data = X11DRV_get_win_data(hwnd);
+    while(!data->whole_window) {
+        data->xembed.cnt++;
+        data = X11DRV_get_win_data(GetParent(data->hwnd));
+    }
+
+    attr.event_mask = (ExposureMask | KeyPressMask | KeyReleaseMask
+            | SubstructureNotifyMask | StructureNotifyMask);
+    attr.bit_gravity = NorthWestGravity;
+    attr.backing_store = NotUseful;
+    
+    wine_tsx11_lock();
+    window = XCreateWindow(display, data->whole_window, 0, 0, 100, 100,
+            0, screen_depth, InputOutput, visual,
+            CWEventMask | CWBitGravity | CWBackingStore, &attr);
+    XSaveContext(display, window, winXEmbedContext, (char*)hwnd);
+    XSync(display, False);
+    wine_tsx11_unlock();
+
+    TRACE("window = %08lx\n", window);
+
+    set_embedder_window(hwnd, window);
+    update_winpos(hwnd, data->hwnd, hwnd, (IsWindowVisible(hwnd) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW));
+    xembed_list_add(data, hwnd);
+}
+
+static void xembed_window_destroy(HWND hwnd) {
+    HWND parent = hwnd;
+    struct x11drv_win_data *data;
+    Display * display = ((struct x11drv_thread_data*)TlsGetValue(thread_data_tls_index))->display;
+    Window window;
+
+    TRACE("(%p)\n", hwnd);
+
+    detach_window(hwnd);
+
+    window = get_embedder_window(hwnd);
+    wine_tsx11_lock();
+    XDestroyWindow(display, window);
+    wine_tsx11_unlock();
+
+    data = X11DRV_get_win_data(hwnd);
+    if(!data->whole_window) {
+        do {
+            data->xembed.cnt--;
+            parent = GetParent(parent);
+            data = X11DRV_get_win_data(parent);
+        }while(!data->whole_window);
+    }
+    xembed_list_remove(data, hwnd);
+}
+
+static void xembed_window_paint(HWND hwnd)
+{
+    Window client = get_client_window(hwnd);
+
+    TRACE("(%p)\n", hwnd);
+
+    if(client) {
+        Display *display = ((struct x11drv_thread_data*)TlsGetValue(thread_data_tls_index))->display;
+        RECT rect;
+
+        GetUpdateRect(hwnd, &rect, FALSE);
+
+        wine_tsx11_lock();
+        XClearArea(display, client, rect.left, rect.top, rect.right, rect.bottom, True);
+        wine_tsx11_unlock();
+    }
+}
+
+static void xembed_window_attach_xwindow(HWND hwnd, Window client)
+{
+    Window window = get_embedder_window(hwnd);
+    struct x11drv_thread_data *data = (struct x11drv_thread_data*)TlsGetValue(thread_data_tls_index);
+
+    TRACE("(%p %08lx)\n", hwnd, client);
+
+    wine_tsx11_lock();
+    XReparentWindow(data->display, client, window, 0, 0);
+    wine_tsx11_unlock();
+}
+
+static void xembed_window_set_focus(HWND hwnd)
+{
+    Window window = get_embedder_window(hwnd);
+    Window client = get_client_window(hwnd);
+    struct x11drv_thread_data *data = (struct x11drv_thread_data*)TlsGetValue(thread_data_tls_index);
+
+    TRACE("(%p)\n", hwnd);
+    
+    wine_tsx11_lock();
+    XSetInputFocus(data->display, window, RevertToParent, CurrentTime);
+    wine_tsx11_unlock();
+
+    if(client)
+        send_xembed_event(data->display, client, XEMBED_FOCUS_IN, XEMBED_FOCUS_FIRST, 0, 0);
+}
+
+static void xembed_window_kill_focus(HWND hwnd)
+{
+    Window client = get_client_window(hwnd);
+    struct x11drv_thread_data *data = (struct x11drv_thread_data*)TlsGetValue(thread_data_tls_index);
+    struct x11drv_win_data *win_data = X11DRV_get_win_data(hwnd);
+
+    TRACE("(%p)\n", hwnd);
+
+    while(!win_data->whole_window)
+        win_data = X11DRV_get_win_data(GetParent(win_data->hwnd));
+
+    wine_tsx11_lock();
+    XSetInputFocus(data->display, win_data->whole_window, RevertToParent, CurrentTime);
+    wine_tsx11_unlock();
+
+    if(client)
+        send_xembed_event(data->display, client, XEMBED_FOCUS_OUT, 0, 0, 0);
+}
+
+static void xembed_CreateNotify(HWND hwnd, XCreateWindowEvent *event)
+{
+    TRACE("(%p %p) window = %08lx parent = %08lx\n", hwnd, event, event->window, event->parent);
+
+    attach_window(hwnd, event->window);
+}
+
+static void xembed_ReparentNotify(HWND hwnd, XReparentEvent *event)
+{
+    TRACE("(%p %p)\n", hwnd, event);
+
+    if(event->parent == event->event)
+        attach_window(hwnd, event->window);
+}
+
+static void xembed_ConfigureNotify(HWND hwnd, XConfigureEvent *event)
+{
+    Window client;
+    
+    if(event->event != event->window)
+        return;
+
+    client = get_client_window(hwnd);
+
+    TRACE("(%p %p) window=%08lx client=%08lx\n", hwnd, event, event->window, client);
+
+    if(client) {
+        wine_tsx11_lock();
+        XResizeWindow(event->display, client, event->width, event->height);
+        wine_tsx11_unlock();
+    }
+}
+
+static void xembed_KeyNotify(HWND hwnd, XKeyEvent *event)
+{
+    Window client = get_client_window(hwnd);
+
+    TRACE("(%p %p)\n", hwnd, event);
+
+    if(client) {
+        event->window = client;
+
+        wine_tsx11_lock();
+        XSendEvent(event->display, client, False, NoEventMask, (XEvent*)event);
+        wine_tsx11_unlock();
+    }
+}
+
+static void xembed_protocol(HWND hwnd, XClientMessageEvent *event) {
+    if(event->message_type != x11drv_atom(_XEMBED))
+        return;
+
+    switch(event->data.l[1]) {
+    case XEMBED_REQUEST_FOCUS:
+        TRACE("XEMBED_REQUEST_FOCUS\n");
+        if(GetFocus() != hwnd)
+            SetFocus(hwnd);
+        break;
+    case XEMBED_FOCUS_NEXT:
+        TRACE("XEMBED_FOCUS_NEXT\n");
+        SendMessageW(GetParent(hwnd), WM_KEYDOWN, VK_TAB, 0);
+        break;
+    case XEMBED_FOCUS_PREV:
+        WARN("XEMBED_FOCUS_PREV\n");
+        break;
+    case XEMBED_REGISTER_ACCELERATOR:
+        WARN("XEMBED_REGISTER_ACCELERATOR\n");
+        break;
+    case XEMBED_UNREGISTER_ACCELERATOR:
+        WARN("XEMBED_UNREGISTER_ACCELERATOR\n");
+        break;
+    case XEMBED_ACTIVATE_ACCELERATOR:
+        WARN("XEMBED_ACTIVATE_ACCELERATOR\n");
+        break;
+    default:
+        WARN("got unknown message: %ld\n", event->data.l[1]);
+    };
+}
+
+/**********************************************************************
+ *              XEMBED_update_child_pos
+ */
+void XEMBED_update_child_pos(HWND hwnd, UINT flags)
+{
+    struct xembed_list *iter;
+    struct x11drv_win_data *data = X11DRV_get_win_data(hwnd);
+
+    TRACE("(%p %08x)\n", hwnd, flags);
+
+    if(data->whole_window)
+        return;
+
+    do {
+        data = X11DRV_get_win_data(GetParent(data->hwnd));
+    } while(!data->whole_window);
+
+    for(iter = data->xembed.list; iter; iter = iter->next) {
+        if(iter->hwnd == hwnd || IsChild(hwnd, iter->hwnd))
+            update_winpos(iter->hwnd, data->hwnd, hwnd, flags);
+    }
+}
+
+/**********************************************************************
+ *              XEMBED_reparent
+ */
+void XEMBED_reparent(HWND hwnd, HWND new_parent, HWND old_parent)
+{
+    struct x11drv_win_data *old_win_data, *new_win_data;
+    int cnt = X11DRV_get_win_data(hwnd)->xembed.cnt;
+    struct xembed_list *iter, *tmp;
+    Display *display = ((struct x11drv_thread_data*)TlsGetValue(thread_data_tls_index))->display;
+
+    TRACE("(%p %p %p)\n", hwnd, old_parent, new_parent);
+
+    old_win_data = X11DRV_get_win_data(old_parent);
+    if(!old_win_data->whole_window) {
+        do {
+            old_win_data->xembed.cnt -= cnt;
+            old_win_data = X11DRV_get_win_data(GetParent(old_win_data->hwnd));
+        }while(!old_win_data->whole_window);
+    }
+
+    new_win_data = X11DRV_get_win_data(new_parent);
+    if(!new_win_data->whole_window) {
+        do {
+            new_win_data->xembed.cnt += cnt;
+            new_win_data = X11DRV_get_win_data(GetParent(new_win_data->hwnd));
+        }while(!new_win_data->whole_window);
+    }
+
+    if(old_win_data == new_win_data)
+        return;
+
+    for(iter = old_win_data->xembed.list; iter; iter = tmp) {
+        tmp = iter->next;
+        if(iter->hwnd == hwnd || IsChild(hwnd, iter->hwnd)) {
+            wine_tsx11_lock();
+            XReparentWindow(display, get_embedder_window(iter->hwnd), new_win_data->whole_window, 0, 0);
+            wine_tsx11_unlock();
+
+            xembed_list_add(new_win_data, iter->hwnd);
+            xembed_list_remove(old_win_data, iter->hwnd);
+        }
+    }
+}
+
+/**********************************************************************
+ *              XEMBED_event
+ */
+void XEMBED_event(HWND hwnd, XEvent *event) 
+{
+    switch(event->xany.type) {
+    case CreateNotify:
+        xembed_CreateNotify(hwnd, &event->xcreatewindow);
+        break;
+    case ReparentNotify:
+        xembed_ReparentNotify(hwnd, &event->xreparent);
+        break;
+    case ConfigureNotify:
+        xembed_ConfigureNotify(hwnd, &event->xconfigure);
+        break;
+    case KeyPress:
+    case KeyRelease:
+        xembed_KeyNotify(hwnd, &event->xkey);
+        break;
+    case ClientMessage:
+        xembed_protocol(hwnd, &event->xclient);
+        break;
+    };
+}
+
+/**********************************************************************
+ *              xembed_wndproc
+ */
+static LRESULT WINAPI xembed_wndproc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    switch(msg) {
+    case WM_CREATE:
+        xembed_window_create(hwnd);
+        break;
+    case WM_DESTROY:
+        xembed_window_destroy(hwnd);
+        break;
+    case WM_PAINT:
+        xembed_window_paint(hwnd);
+        break;
+    case WM_SETFOCUS:
+        xembed_window_set_focus(hwnd);
+        break;
+    case WM_KILLFOCUS:
+        xembed_window_kill_focus(hwnd);
+        break;
+    case WM_ATTACH_XWINDOW:
+        xembed_window_attach_xwindow(hwnd, lParam);
+        break;
+    };
+
+    return DefWindowProcW(hwnd, msg, wParam, lParam);
+}
+
+/**********************************************************************
+ *              XEMBED_init
+ */
+void XEMBED_init()
+{
+    static const WCHAR wszXEMBED[] = {'_','X','E','M','B','E','D',0};
+    static const WNDCLASSEXW wndclass = {
+        sizeof(WNDCLASSEXW),
+        0,
+        xembed_wndproc,
+        0, 0, NULL, NULL, NULL, NULL, NULL,
+        wszXEMBED,
+        NULL
+    };
+
+    RegisterClassExW(&wndclass);
+
+    wine_tsx11_lock();
+    winXEmbedContext = XUniqueContext();
+    wine_tsx11_unlock();
+}


More information about the wine-patches mailing list