Hello.
As I'm working on getting MSHTML to work using Linux Gecko, I need support of XEmbed embedding in Wine. It can be also useful for Winelib applications (and maybe in future for some other parts of Wine) to show the X11 windows in Wine. My current version of XEmbed patch and a simple test application that creates gtk button inside Wine window is attached. Using this patch I can display Gecko in the Wine window (now only in my test application, but I'm working on integrating it with MSHTML).
The way I want to do it is: 1. Wine application creates window of "_XEMBED" class 2. Embedding window can be done in two ways (as in specification): - Wine application sends _WM_ATTACH_WINDOW message passing X11 Window (not yet implemented) or - Client window is created as a child window of Wine window 3. Window is displayed and Wine communicates with X11 window using XEmbed protocol
I know my patch needs some polishing and more implementations (focus, activating, handling unmapping of window....), but I'd like to know if the way I do it is proper? What needs to be done for the first patch to be accepted?
Thanks, Jacek
? dlls/x11drv/diff ? dlls/x11drv/xembed.c 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 23 May 2005 19:26:25 -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.54 diff -u -p -r1.54 event.c --- dlls/x11drv/event.c 24 Mar 2005 20:41:28 -0000 1.54 +++ dlls/x11drv/event.c 23 May 2005 19:26:27 -0000 @@ -98,7 +98,7 @@ static struct event_handler handlers[MAX /* GraphicsExpose */ /* NoExpose */ /* VisibilityNotify */ - /* CreateNotify */ + { CreateNotify, X11DRV_CreateNotify }, /* DestroyNotify */ { UnmapNotify, X11DRV_UnmapNotify }, { MapNotify, X11DRV_MapNotify }, @@ -875,7 +875,6 @@ static void handle_dnd_protocol( HWND hw EVENT_DropURLs(hwnd, event); }
- struct client_message_handler { int atom; /* protocol atom */ @@ -889,7 +888,8 @@ static const struct client_message_handl { XATOM_XdndEnter, X11DRV_XDND_EnterEvent }, { XATOM_XdndPosition, X11DRV_XDND_PositionEvent }, { XATOM_XdndDrop, X11DRV_XDND_DropEvent }, - { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent } + { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }, + { XATOM__XEMBED, X11DRV_XEmbed_protocol } };
Index: dlls/x11drv/window.c =================================================================== RCS file: /home/wine/wine/dlls/x11drv/window.c,v retrieving revision 1.108 diff -u -p -r1.108 window.c --- dlls/x11drv/window.c 26 Apr 2005 08:16:17 -0000 1.108 +++ dlls/x11drv/window.c 23 May 2005 19:26:28 -0000 @@ -97,6 +97,8 @@ static const char * const atom_names[NB_ "XdndSelection", "XdndTarget", "XdndTypeList", + "_XEMBED", + "_XEMBED_INFO", "WCF_DIB", "image/gif", "text/html", @@ -183,7 +185,8 @@ static int get_window_attributes( struct attr->event_mask = (ExposureMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | KeyPressMask | KeyReleaseMask | StructureNotifyMask | - FocusChangeMask | KeymapStateMask); + FocusChangeMask | KeymapStateMask | + SubstructureNotifyMask);
return (CWOverrideRedirect | CWSaveUnder | CWEventMask | CWColormap | CWCursor); } Index: dlls/x11drv/x11drv.h =================================================================== RCS file: /home/wine/wine/dlls/x11drv/x11drv.h,v retrieving revision 1.72 diff -u -p -r1.72 x11drv.h --- dlls/x11drv/x11drv.h 21 Apr 2005 17:31:50 -0000 1.72 +++ dlls/x11drv/x11drv.h 23 May 2005 19:26:28 -0000 @@ -288,6 +288,9 @@ extern void X11DRV_XDND_PositionEvent( H extern void X11DRV_XDND_DropEvent( HWND hWnd, XClientMessageEvent *event ); extern void X11DRV_XDND_LeaveEvent( HWND hWnd, XClientMessageEvent *event );
+extern void X11DRV_XEmbed_protocol(HWND, XClientMessageEvent*); +extern void X11DRV_XEmbed_Init(); + /* exported dib functions for now */
/* DIB Section sync state */ @@ -586,6 +589,8 @@ enum x11drv_atoms XATOM_XdndSelection, XATOM_XdndTarget, XATOM_XdndTypeList, + XATOM__XEMBED, + XATOM__XEMBED_INFO, XATOM_WCF_DIB, XATOM_image_gif, XATOM_text_html, @@ -621,6 +626,7 @@ extern void X11DRV_MappingNotify( HWND h extern void X11DRV_DGAMotionEvent( HWND hwnd, XEvent *event ); extern void X11DRV_DGAButtonPressEvent( HWND hwnd, XEvent *event ); extern void X11DRV_DGAButtonReleaseEvent( HWND hwnd, XEvent *event ); +extern void X11DRV_CreateNotify( HWND hwnd, XEvent *event );
extern DWORD EVENT_x11_time_to_win32_time(Time time);
Index: dlls/x11drv/x11drv_main.c =================================================================== RCS file: /home/wine/wine/dlls/x11drv/x11drv_main.c,v retrieving revision 1.103 diff -u -p -r1.103 x11drv_main.c --- dlls/x11drv/x11drv_main.c 21 Apr 2005 17:31:50 -0000 1.103 +++ dlls/x11drv/x11drv_main.c 23 May 2005 19:26:29 -0000 @@ -384,6 +384,9 @@ static BOOL process_attach(void)
X11DRV_InitKeyboard();
+ /* initialize XEmbed */ + X11DRV_XEmbed_Init(); + return TRUE; }
@@ -483,7 +486,6 @@ struct x11drv_thread_data *x11drv_init_t if (desktop_tid) AttachThreadInput( GetCurrentThreadId(), desktop_tid, TRUE ); return data; } -
/*********************************************************************** * X11DRV initialisation routine ? dlls/user/diff Index: dlls/user/user_main.c =================================================================== RCS file: /home/wine/wine/dlls/user/user_main.c,v retrieving revision 1.83 diff -u -p -r1.83 user_main.c --- dlls/user/user_main.c 6 May 2005 19:38:50 -0000 1.83 +++ dlls/user/user_main.c 23 May 2005 19:26:39 -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; --- /dev/null 1970-01-01 01:00:00.000000000 +0100 +++ dlls/x11drv/xembed.c 2005-05-23 21:23:42.000000000 +0200 @@ -0,0 +1,385 @@ +/* + * 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 + */ + +#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_WINDOW WM_USER+600 + +WINE_DEFAULT_DEBUG_CHANNEL(xembed); + +typedef struct wnd_list_t { + HWND hwnd; + struct wnd_list_t *next; +} wnd_list_t; + +static wnd_list_t *wnd_list = NULL; + +static void wnd_list_add(HWND hwnd) +{ + wnd_list_t *new = HeapAlloc(GetProcessHeap(), 0, sizeof(wnd_list_t)); + new->hwnd = hwnd; + new->next = wnd_list; + wnd_list = new; +} + +static void wnd_list_remove(HWND hwnd) +{ + wnd_list_t *iter = wnd_list; + + if(!iter) + return; + + if(iter->hwnd == hwnd) { + wnd_list = iter->next; + HeapFree(GetProcessHeap(), 0, iter); + }else { + while(iter->next && iter->next->hwnd != hwnd) + iter = iter->next; + if(iter->next) { + wnd_list_t *tmp = iter->next; + iter->next = tmp->next; + HeapFree(GetProcessHeap(), 0, tmp); + } + } +} + +BOOL 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); + wine_tsx11_unlock(); + + if(res != Success || !format) { + TRACE("Could not get _XEMBED_INFO\n"); + XFree(data); + return FALSE; + } + + *version = data[0]; + *flags = data[1]; + + XFree(data); + + TRACE("got version=%08lx flags=%08lx\n", *version, *flags); + return TRUE; +} + +static void xembed_client_show(Display *display, HWND hwnd, BOOL show) +{ + Window window = (Window)GetPropA(hwnd, "__wine_x11_client_window"); + + TRACE("(%p %p %x) window=%08lx\n", display, hwnd, show, window); + + if(!window) + return; + + if(!display) + display = ((struct x11drv_thread_data*)NtCurrentTeb()->driver_data)->display; + + wine_tsx11_lock(); + if(show) + XMapWindow(display, window); + else + XUnmapWindow(display, window); + wine_tsx11_unlock(); +} + +static void xembed_client_set_position(Display *display, HWND hwnd) +{ + Window window = (Window)GetPropA(hwnd, "__wine_x11_client_window"); + HWND parent = (HWND)GetPropA(hwnd, "__wine_x11_embedder_window_hwnd"); + WINDOWINFO wininfo, parent_wininfo; + + TRACE("(%p %p) window=%08lx, parent=%p\n", display, hwnd, window, parent); + + if(!window) + return; + + if(!display) + display = ((struct x11drv_thread_data*)NtCurrentTeb()->driver_data)->display; + + wininfo.cbSize = sizeof(WINDOWINFO); + parent_wininfo.cbSize = sizeof(WINDOWINFO); + GetWindowInfo(hwnd, &wininfo); + GetWindowInfo(parent, &parent_wininfo); + + wine_tsx11_lock(); + XMoveWindow(display, window, + wininfo.rcClient.left - parent_wininfo.rcClient.left, + wininfo.rcClient.top - parent_wininfo.rcClient.top); + wine_tsx11_unlock(); +} + +static void xembed_client_set_size(Display *display, HWND hwnd) +{ + Window window = (Window)GetPropA(hwnd, "__wine_x11_client_window"); + RECT rect; + + TRACE("(%p %p) window=%08lx\n", display, hwnd, window); + + if(!window) + return; + + if(!display) + display = ((struct x11drv_thread_data*)NtCurrentTeb()->driver_data)->display; + + GetClientRect(hwnd, &rect); + + wine_tsx11_lock(); + XResizeWindow(display, window, rect.right, rect.bottom); + 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] = 0; /* FIXME */ + 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 LRESULT attach_window(HWND hwnd, Window window) { + struct x11drv_thread_data *data = NtCurrentTeb()->driver_data; + unsigned long version = 0, flags = XEMBED_MAPPED; + + TRACE("(%p %lx)\n", hwnd, window); + + SetPropA(hwnd, "__wine_x11_client_window", (LPVOID)window); + + check_xembed_info(data->display, window, &version, &flags); + + /* FIXME: handle XEmbed version correctly (until there is only version 0 it's not really important) */ + send_xembed_event(data->display, window, XEMBED_EMBEDDED_NOTIFY, 0, + (Window)GetPropA(hwnd, "__wine_x11_embedder_window"), 0); + + if(!(flags & XEMBED_MAPPED)) { + unsigned int prop[] = {0, XEMBED_MAPPED}; + XChangeProperty(data->display, window, x11drv_atom(_XEMBED_INFO), + XA_CARDINAL, 32, PropModeReplace, + (unsigned char*)prop, 2); + } + + xembed_client_set_position(data->display, hwnd); + xembed_client_set_size(data->display, hwnd); + + if(IsWindowVisible(hwnd)) + xembed_client_show(data->display, hwnd, TRUE); + + send_xembed_event(data->display, window, XEMBED_WINDOW_ACTIVATE, 0, 0, 0); + send_xembed_event(data->display, window, XEMBED_FOCUS_IN, XEMBED_FOCUS_FIRST, 0, 0); + + return 0; +} + +HWND find_hwnd_for_window(Window parent) +{ + wnd_list_t *iter; + + TRACE("(%08lx)\n", parent); + + /* + * FIXME: + * We attach the window to the first HWND on the list. It would be better + * to have some kind of a filter (callback function or a message to parent?) + * determining which HWND to select. + */ + for(iter = wnd_list; iter; iter = iter->next) { + if(!GetPropA(iter->hwnd, "__wine_x11_client_window") + && (Window)GetPropA(iter->hwnd, "__wine_x11_embedder_window") == parent) + break; + } + + if(!iter) { + WARN("Not found embedder window\n"); + return NULL; + } + + TRACE("Fount hwnd %p\n", iter->hwnd); + return iter->hwnd; +} + +/********************************************************************** + * X11DRV_CreateNotify + */ +void X11DRV_CreateNotify(HWND hwnd, XEvent *event) +{ + HWND embed_hwnd; + + TRACE("(%p %p) window = %lx\n", hwnd, event, event->xcreatewindow.window); + + embed_hwnd = find_hwnd_for_window(event->xcreatewindow.parent); + if(embed_hwnd) + attach_window(embed_hwnd, event->xcreatewindow.window); +} + +/********************************************************************** + * X11DRV_XEmbed_protocol + */ +void X11DRV_XEmbed_protocol(HWND hwnd, XClientMessageEvent *event) { + switch(event->data.l[1]) { + case XEMBED_EMBEDDED_NOTIFY: + TRACE("XEMBED_EMBEDDED_NOTIFY\n"); + break; + case XEMBED_WINDOW_ACTIVATE: + TRACE("XEMBED_WINDOW_ACTIVATE\n"); + break; + case XEMBED_WINDOW_DEACTIVATE: + TRACE("XEMBED_WINDOW_DEACTIVATE\n"); + break; + case XEMBED_REQUEST_FOCUS: + TRACE("XEMBED_REQUEST_FOCUS\n"); + break; + case XEMBED_FOCUS_IN: + TRACE("XEMBED_FOCUS_IN\n"); + break; + case XEMBED_FOCUS_OUT: + TRACE("XEMBED_FOCUS_OUT\n"); + break; + case XEMBED_FOCUS_NEXT: + TRACE("XEMBED_FOCUS_NEXT\n"); + break; + case XEMBED_FOCUS_PREV: + TRACE("XEMBED_FOCUS_PREV\n"); + break; + case XEMBED_MODALITY_ON: + TRACE("XEMBED_MODALITY_ON\n"); + break; + case XEMBED_MODALITY_OFF: + TRACE("XEMBED_MODALITY_OFF\n"); + break; + case XEMBED_REGISTER_ACCELERATOR: + TRACE("XEMBED_REGISTER_ACCELERATOR\n"); + break; + case XEMBED_UNREGISTER_ACCELERATOR: + TRACE("XEMBED_UNREGISTER_ACCELERATOR\n"); + break; + case XEMBED_ACTIVATE_ACCELERATOR: + TRACE("XEMBED_ACTIVATE_ACCELERATOR\n"); + break; + }; +} + +/********************************************************************** + * XEmbed_WndProc + */ +static LRESULT WINAPI XEmbed_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch(msg) { + case WM_CREATE: { + HWND parent; + Window window; + + TRACE("(%p) WM_CREATE\n", hwnd); + parent = hwnd; + + while(!(window = (Window)GetPropA(parent, "__wine_x11_whole_window"))) + parent = GetParent(parent); + + TRACE("x11 window = %08lx\n", window); + + SetPropA(hwnd, "__wine_x11_embedder_window", (LPVOID)window); + SetPropA(hwnd, "__wine_x11_embedder_window_hwnd", (LPVOID)parent); + wnd_list_add(hwnd); + break; + } + case WM_DESTROY: + TRACE("(%p) WM_DESTROY\n", hwnd); + wnd_list_remove(hwnd); + break; + case WM_SHOWWINDOW: + xembed_client_show(NULL, hwnd, wParam); + break; + case WM_MOVE: + xembed_client_set_position(NULL, hwnd); + break; + case WM_SIZE: + xembed_client_set_size(NULL, hwnd); + break; + case _WM_ATTACH_WINDOW: + WARN("_WM_ATTACH_WINDOW\n"); + }; + + return DefWindowProcW(hwnd, msg, wParam, lParam); +} + +/********************************************************************** + * X11DRV_XEmbed_Init + */ +void X11DRV_XEmbed_Init() +{ + static const WCHAR wszXEMBED[] = {'_','X','E','M','B','E','D',0}; + static WNDCLASSEXW wndclass = { + sizeof(WNDCLASSEXW), + 0, + XEmbed_WndProc, + 0, 0, NULL, NULL, NULL, NULL, NULL, + wszXEMBED, + NULL + }; + + TRACE("()\n"); + + RegisterClassExW(&wndclass); +}
#include <assert.h> #include <unistd.h> #include <stdio.h> #include <gtk/gtk.h> #include <gdk/gdkx.h> #include <windows.h>
static GdkNativeWindow xwindow; static HINSTANCE hInst;
static DWORD WINAPI make_plug(LPVOID arg) { GtkWidget *plug, *button;
assert(xwindow);
gtk_init(NULL, NULL);
plug = gtk_plug_new(xwindow); assert(plug);
gtk_widget_show(plug); button = gtk_button_new(); gtk_container_add((GtkContainer*)plug, button); gtk_widget_show(button);
gtk_main(); }
static LRESULT WINAPI WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_CREATE: xwindow = (GdkNativeWindow)GetPropA(hwnd, "__wine_x11_whole_window"); HWND xembed_hwnd = CreateWindowExA(0, "_XEMBED", NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0, 0, 300, 300, hwnd, NULL, hInst, NULL); ShowWindow(xembed_hwnd, TRUE); xwindow = (GdkNativeWindow)GetPropA(xembed_hwnd, "__wine_x11_embedder_window"); CreateThread(NULL, 0, make_plug, NULL, 0, NULL); break; case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProc(hwnd, msg, wParam, lParam); }
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow) { MSG msg; HWND hwnd; static char appName[] = "XEmbed Test"; WNDCLASSEX wndclass; if(!hPrevInst) { hInst = hInstance; wndclass.cbSize = sizeof(wndclass); wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL,IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL,IDC_ARROW); wndclass.hbrBackground = 0; wndclass.lpszMenuName = NULL; wndclass.lpszClassName = appName; wndclass.hIconSm = LoadIcon(NULL,IDI_APPLICATION);
if(!RegisterClassEx(&wndclass)) return FALSE; }
hwnd = CreateWindow(appName, appName, WS_OVERLAPPEDWINDOW, 0, 0, 300, 300, NULL, NULL, hInstance, NULL);
assert(hwnd);
ShowWindow(hwnd,cmdshow);
while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }
On Mon, 23 May 2005 21:31:26 +0200, Jacek Caban wrote:
- Window is displayed and Wine communicates with X11 window using XEmbed protocol
How does this work if the app wants to embed an X window as a child win32 window? We no longer map child win32 windows to child X windows since the WM rewrite so there's nothing to embed into.
thanks -mike
Mike Hearn wrote:
On Mon, 23 May 2005 21:31:26 +0200, Jacek Caban wrote:
- Window is displayed and Wine communicates with X11 window using XEmbed protocol
How does this work if the app wants to embed an X window as a child win32 window? We no longer map child win32 windows to child X windows since the WM rewrite so there's nothing to embed into.
thanks -mike
The X window is created as a child window of frame's X window (excluding desktop mode the frame has associated X window), but win32 window can be a child window of any window in this frame. In desktop mode it has to be child window of desktop's X window, but it doesn't work yet. The application can get a window that has to be the parent of embedded window from "__wine_x11_embedder_window" property of _XEMBED window (in application I sent as example I forgot to remove getting window from "__wine_x11_whole_window").
Jacek