diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index aa381793982..ae0875bc1d0 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -800,7 +800,7 @@ static void X11DRV_MapNotify( HWND hwnd, XEvent *event ) struct x11drv_win_data *data; if (!(data = X11DRV_get_win_data( hwnd ))) return; - if (!data->mapped) return; + if (!data->mapped || data->embedded) return; if (!data->managed) { @@ -849,20 +849,47 @@ static void X11DRV_ReparentNotify( HWND hwnd, XEvent *xev ) { XReparentEvent *event = &xev->xreparent; struct x11drv_win_data *data; + HWND parent, old_parent; + DWORD style; if (!(data = X11DRV_get_win_data( hwnd ))) return; if (!data->embedded) return; + + if (data->whole_window) + { + if (event->parent == root_window) + { + TRACE( "%p/%lx reparented to root\n", hwnd, data->whole_window ); + data->embedder = 0; + SendMessageW( hwnd, WM_CLOSE, 0, 0 ); + return; + } + data->embedder = event->parent; + } + + TRACE( "%p/%lx reparented to %lx\n", hwnd, data->whole_window, event->parent ); + + style = GetWindowLongW( hwnd, GWL_STYLE ); if (event->parent == root_window) { - TRACE( "%p/%lx reparented to root\n", hwnd, data->whole_window ); - data->embedder = 0; - SendMessageW( hwnd, WM_CLOSE, 0, 0 ); + parent = GetDesktopWindow(); + style = (style & ~WS_CHILD) | WS_POPUP; } else { - TRACE( "%p/%lx reparented to %lx\n", hwnd, data->whole_window, event->parent ); - data->embedder = event->parent; + if (!(parent = create_foreign_window( event->display, event->parent ))) return; + style = (style & ~WS_POPUP) | WS_CHILD; } + + ShowWindow( hwnd, SW_HIDE ); + old_parent = SetParent( hwnd, parent ); + SetWindowLongW( hwnd, GWL_STYLE, style ); + SetWindowPos( hwnd, HWND_TOP, event->x, event->y, 0, 0, + SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOCOPYBITS | + ((style & WS_VISIBLE) ? SWP_SHOWWINDOW : 0) ); + + /* make old parent destroy itself if it no longer has children */ + if (old_parent != GetDesktopWindow()) PostMessageW( old_parent, WM_CLOSE, 0, 0 ); } @@ -881,7 +908,10 @@ void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev ) if (!hwnd) return; if (!(data = X11DRV_get_win_data( hwnd ))) return; - if (!data->mapped || data->iconic || !data->managed) return; + if (!data->mapped || data->iconic) return; + if (data->whole_window && !data->managed) return; + /* ignore synthetic events on foreign windows */ + if (event->send_event && !data->whole_window) return; if (data->configure_serial && (long)(data->configure_serial - event->serial) > 0) { TRACE( "win %p/%lx event %d,%d,%dx%d ignoring old serial %lu/%lu\n", @@ -943,6 +973,8 @@ void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev ) cy = rect.bottom - rect.top; flags = SWP_NOACTIVATE | SWP_NOZORDER; + if (!data->whole_window) flags |= SWP_NOCOPYBITS; /* we can't copy bits of foreign windows */ + if (data->window_rect.left == x && data->window_rect.top == y) flags |= SWP_NOMOVE; else TRACE( "%p moving from (%d,%d) to (%d,%d)\n", @@ -973,7 +1005,7 @@ static void X11DRV_GravityNotify( HWND hwnd, XEvent *xev ) struct x11drv_win_data *data = X11DRV_get_win_data( hwnd ); RECT rect; - if (!data) return; + if (!data || data->whole_window) return; /* only handle this for foreign windows */ rect.left = event->x; rect.top = event->y; diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 2a0f53f1492..446b71b7b75 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1892,9 +1892,16 @@ void X11DRV_DestroyNotify( HWND hwnd, XEvent *event ) struct x11drv_win_data *data; if (!(data = X11DRV_get_win_data( hwnd ))) return; - if (!data->embedded) FIXME( "window %p/%lx destroyed from the outside\n", hwnd, data->whole_window ); - destroy_whole_window( display, data, TRUE ); + + if (!data->whole_window) + { + wine_tsx11_lock(); + XDeleteContext( display, event->xdestroywindow.window, winContext ); + wine_tsx11_unlock(); + } + else destroy_whole_window( display, data, TRUE ); + if (data->embedded) SendMessageW( hwnd, WM_CLOSE, 0, 0 ); } @@ -2047,6 +2054,118 @@ struct x11drv_win_data *X11DRV_create_win_data( HWND hwnd ) } +/* window procedure for foreign windows */ +static LRESULT WINAPI foreign_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + switch(msg) + { + case WM_PARENTNOTIFY: + if (LOWORD(wparam) == WM_DESTROY) + { + TRACE( "%p: got parent notify destroy for win %lx\n", hwnd, lparam ); + PostMessageW( hwnd, WM_CLOSE, 0, 0 ); /* so that we come back here once the child is gone */ + } + return 0; + case WM_CLOSE: + if (GetWindow( hwnd, GW_CHILD )) return 0; /* refuse to die if we still have children */ + break; + } + return DefWindowProcW( hwnd, msg, wparam, lparam ); +} + + +/*********************************************************************** + * create_foreign_window + * + * Create a foreign window for the specified X window and its ancestors + */ +HWND create_foreign_window( Display *display, Window xwin ) +{ + static const WCHAR classW[] = {'_','_','w','i','n','e','_','x','1','1','_','f','o','r','e','i','g','n','_','w','i','n','d','o','w',0}; + static BOOL class_registered; + struct x11drv_win_data *data; + HWND hwnd, parent; + Window xparent, xroot; + Window *xchildren; + unsigned int nchildren; + XWindowAttributes attr; + DWORD style = WS_CLIPCHILDREN; + + if (!class_registered) + { + WNDCLASSEXW class; + + memset( &class, 0, sizeof(class) ); + class.cbSize = sizeof(class); + class.lpfnWndProc = foreign_window_proc; + class.lpszClassName = classW; + if (!RegisterClassExW( &class ) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) + { + ERR( "Could not register foreign window class\n" ); + return FALSE; + } + class_registered = TRUE; + } + + wine_tsx11_lock(); + if (XFindContext( display, xwin, winContext, (char **)&hwnd )) hwnd = 0; + if (hwnd) /* already created */ + { + wine_tsx11_unlock(); + return hwnd; + } + + XSelectInput( display, xwin, StructureNotifyMask ); + if (!XGetWindowAttributes( display, xwin, &attr ) || + !XQueryTree( display, xwin, &xroot, &xparent, &xchildren, &nchildren )) + { + XSelectInput( display, xwin, 0 ); + wine_tsx11_unlock(); + return 0; + } + XFree( xchildren ); + wine_tsx11_unlock(); + + if (xparent == xroot) + { + parent = GetDesktopWindow(); + style |= WS_POPUP; + attr.x += virtual_screen_rect.left; + attr.y += virtual_screen_rect.top; + } + else + { + parent = create_foreign_window( display, xparent ); + style |= WS_CHILD; + } + + hwnd = CreateWindowW( classW, NULL, style, attr.x, attr.y, attr.width, attr.height, + parent, 0, 0, NULL ); + + if (!(data = alloc_win_data( display, hwnd ))) + { + DestroyWindow( hwnd ); + return 0; + } + SetRect( &data->window_rect, attr.x, attr.y, attr.x + attr.width, attr.y + attr.height ); + data->whole_rect = data->client_rect = data->window_rect; + data->whole_window = data->client_window = 0; + data->embedded = TRUE; + data->mapped = TRUE; + + wine_tsx11_lock(); + XSaveContext( display, xwin, winContext, (char *)data->hwnd ); + wine_tsx11_unlock(); + + ShowWindow( hwnd, SW_SHOW ); + + TRACE( "win %lx parent %p style %08x %s -> hwnd %p\n", + xwin, parent, style, wine_dbgstr_rect(&data->window_rect), hwnd ); + + return hwnd; +} + + /*********************************************************************** * X11DRV_get_whole_window * @@ -2234,6 +2353,7 @@ void CDECL X11DRV_SetParent( HWND hwnd, HWND parent, HWND old_parent ) if (!data) return; if (parent == old_parent) return; + if (data->embedded) return; if (parent != GetDesktopWindow()) /* a child window */ { @@ -2384,10 +2504,11 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags if (thread_data->current_event && thread_data->current_event->xany.window == data->whole_window) event_type = thread_data->current_event->type; - if (event_type != ConfigureNotify && event_type != PropertyNotify && event_type != GravityNotify) + if (event_type != ConfigureNotify && event_type != PropertyNotify && + event_type != GravityNotify && event_type != ReparentNotify) event_type = 0; /* ignore other events */ - if (data->mapped) + if (data->mapped && event_type != ReparentNotify) { if (((swp_flags & SWP_HIDEWINDOW) && !(new_style & WS_VISIBLE)) || (event_type != ConfigureNotify && diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 4412f3e1308..f48b51f7718 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -784,6 +784,7 @@ extern void update_user_time( Time time ); extern void update_net_wm_states( Display *display, struct x11drv_win_data *data ); extern void make_window_embedded( Display *display, struct x11drv_win_data *data ); extern void change_systray_owner( Display *display, Window systray_window ); +extern HWND create_foreign_window( Display *display, Window window ); static inline void mirror_rect( const RECT *window_rect, RECT *rect ) {