From: Damjan Jovanovic Subject: [PATCH 2/3] include/ole32/user32/winemac.drv/winex11.drv: do not allow dropping drags through obscuring native windows Message-Id: Date: Mon, 2 Mar 2015 06:32:24 +0200 Currently Wine does not support dragging from a Wine window to a native Window, but does from a Wine window to another Wine window. What is a big surprise though, is that if a Wine window is obscured by a native window that's on top of it in the z-order, it looks like you are dropping onto the native window, but you are actually dropping THROUGH the native window into the Wine window - in other words, the wrong application gets the drop! This patch fixes that problem by giving ole32 awareness of native windows (represented by a ULONG_PTR) via new Wine-specific functions in user32 that are implemented by the display drivers winex11.drv and winemac.drv (of which only the former is implemented by this patch). The ole32 drag loop can thus tell whether the window under the mouse is a Wine window or a native window, and obtain IDropTarget from either window type, something that is used by the next patch to implement dragging from Wine to native windows. On the winex11.drv side, XTranslateCoordinates() searches down from the root window for the first X11 window with a winContext or the new IAmWine window property that all Wine's windows are now given (as Wine's X11 windows don't have a winContext all the time), and if so assumes the window is Wine's and proceeds on the ole32 side as before. Otherwise it stops the search and reports an X11 window as soon as it finds the XdndAware property set, or when XTranslateCoordinates() can't find any further children at the given coordinates. Damjan Jovanovic --- dlls/ole32/ole2.c | 52 +++++++++++++++++++++++------- dlls/user32/driver.c | 16 +++++++++ dlls/user32/user32.spec | 2 ++ dlls/user32/user_private.h | 2 ++ dlls/user32/win.c | 16 +++++++++ dlls/winemac.drv/dragdrop.c | 17 ++++++++++ dlls/winemac.drv/winemac.drv.spec | 3 ++ dlls/winex11.drv/window.c | 3 ++ dlls/winex11.drv/winex11.drv.spec | 2 ++ dlls/winex11.drv/x11drv.h | 1 + dlls/winex11.drv/x11drv_main.c | 1 + dlls/winex11.drv/xdnd.c | 68 +++++++++++++++++++++++++++++++++++++++ include/winuser.h | 2 ++ 13 files changed, 174 insertions(+), 11 deletions(-) diff --git a/dlls/ole32/ole2.c b/dlls/ole32/ole2.c index b9cc1f0..1942135 100644 --- a/dlls/ole32/ole2.c +++ b/dlls/ole32/ole2.c @@ -68,6 +68,12 @@ typedef struct tagTrackerWindowInfo HRESULT returnValue; BOOL escPressed; + enum { + NONE = 0, /* not tracking any window */ + NATIVE, /* tracking a native window */ + WINE /* tracking a Wine window */ + } windowType; + ULONG_PTR nativeWindow; HWND curTargetHWND; /* window the mouse is hovering over */ HWND curDragTargetHWND; /* might be an ancestor of curTargetHWND */ IDropTarget* curDragTarget; @@ -2235,6 +2241,8 @@ static LRESULT WINAPI OLEDD_DragTrackerWindowProc( */ static void OLEDD_TrackStateChange(TrackerWindowInfo* trackerInfo) { + ULONG_PTR nativeWindowUnderMouse = 0; + BOOL hasWindowChanged; HWND hwndNewTarget = 0; HRESULT hr = S_OK; POINT pt; @@ -2244,7 +2252,14 @@ static void OLEDD_TrackStateChange(TrackerWindowInfo* trackerInfo) */ pt.x = trackerInfo->curMousePos.x; pt.y = trackerInfo->curMousePos.y; - hwndNewTarget = WindowFromPoint(pt); + nativeWindowUnderMouse = __wine_get_native_window_at(pt.x, pt.y); + if (nativeWindowUnderMouse) + hasWindowChanged = trackerInfo->windowType != NATIVE || trackerInfo->nativeWindow != nativeWindowUnderMouse; + else + { + hwndNewTarget = WindowFromPoint(pt); + hasWindowChanged = trackerInfo->windowType != WINE || trackerInfo->curTargetHWND != hwndNewTarget; + } trackerInfo->returnValue = IDropSource_QueryContinueDrag(trackerInfo->dropSource, trackerInfo->escPressed, @@ -2261,7 +2276,7 @@ static void OLEDD_TrackStateChange(TrackerWindowInfo* trackerInfo) * DragOver notification */ if ( (trackerInfo->curDragTarget != 0) && - (trackerInfo->curTargetHWND == hwndNewTarget) ) + !hasWindowChanged ) { IDropTarget_DragOver(trackerInfo->curDragTarget, trackerInfo->dwKeyState, @@ -2281,23 +2296,34 @@ static void OLEDD_TrackStateChange(TrackerWindowInfo* trackerInfo) /* * Make sure we're hovering over a window. */ - if (hwndNewTarget) + if (nativeWindowUnderMouse || hwndNewTarget) { /* * Find-out if there is a drag target under the mouse */ - HWND next_target_wnd = hwndNewTarget; + if (nativeWindowUnderMouse) + { + trackerInfo->nativeWindow = nativeWindowUnderMouse; + if(trackerInfo->curDragTarget) IDropTarget_Release(trackerInfo->curDragTarget); + trackerInfo->curDragTarget = __wine_get_native_droptarget(nativeWindowUnderMouse); + trackerInfo->windowType = NATIVE; + } + else + { + HWND next_target_wnd = hwndNewTarget; - trackerInfo->curTargetHWND = hwndNewTarget; + trackerInfo->curTargetHWND = hwndNewTarget; - while (next_target_wnd && !is_droptarget(next_target_wnd)) - next_target_wnd = GetParent(next_target_wnd); + while (next_target_wnd && !is_droptarget(next_target_wnd)) + next_target_wnd = GetParent(next_target_wnd); - if (next_target_wnd) hwndNewTarget = next_target_wnd; + if (next_target_wnd) hwndNewTarget = next_target_wnd; - trackerInfo->curDragTargetHWND = hwndNewTarget; - if(trackerInfo->curDragTarget) IDropTarget_Release(trackerInfo->curDragTarget); - trackerInfo->curDragTarget = get_droptarget_pointer(hwndNewTarget); + trackerInfo->curDragTargetHWND = hwndNewTarget; + if(trackerInfo->curDragTarget) IDropTarget_Release(trackerInfo->curDragTarget); + trackerInfo->curDragTarget = get_droptarget_pointer(hwndNewTarget); + trackerInfo->windowType = WINE; + } /* * If there is, notify it that we just dragged-in @@ -2314,6 +2340,8 @@ static void OLEDD_TrackStateChange(TrackerWindowInfo* trackerInfo) /* failed DragEnter() means invalid target */ if (hr != S_OK) { + trackerInfo->windowType = NONE; + trackerInfo->nativeWindow = 0; trackerInfo->curDragTargetHWND = 0; trackerInfo->curTargetHWND = 0; IDropTarget_Release(trackerInfo->curDragTarget); @@ -2326,6 +2354,8 @@ static void OLEDD_TrackStateChange(TrackerWindowInfo* trackerInfo) /* * The mouse is not over a window so we don't track anything. */ + trackerInfo->windowType = NONE; + trackerInfo->nativeWindow = 0; trackerInfo->curDragTargetHWND = 0; trackerInfo->curTargetHWND = 0; if(trackerInfo->curDragTarget) IDropTarget_Release(trackerInfo->curDragTarget); diff --git a/dlls/user32/driver.c b/dlls/user32/driver.c index e0bc38c..7ef9224 100644 --- a/dlls/user32/driver.c +++ b/dlls/user32/driver.c @@ -153,6 +153,8 @@ static const USER_DRIVER *load_driver(void) GET_USER_FUNC(WindowMessage); GET_USER_FUNC(WindowPosChanging); GET_USER_FUNC(WindowPosChanged); + GET_USER_FUNC(WineGetNativeWindowAt); + GET_USER_FUNC(WineGetNativeDroptarget); GET_USER_FUNC(SystemParametersInfo); #undef GET_USER_FUNC } @@ -507,6 +509,16 @@ static void CDECL nulldrv_WindowPosChanged( HWND hwnd, HWND insert_after, UINT s { } +static ULONG_PTR CDECL nulldrv_WineGetNativeWindowAt( LONG x, LONG y ) +{ + return 0; +} + +static void* CDECL nulldrv_WineGetNativeDroptarget( ULONG_PTR nativeWindow ) +{ + return NULL; +} + static BOOL CDECL nulldrv_SystemParametersInfo( UINT action, UINT int_param, void *ptr_param, UINT flags ) { return FALSE; @@ -571,6 +583,8 @@ static USER_DRIVER null_driver = nulldrv_WindowMessage, nulldrv_WindowPosChanging, nulldrv_WindowPosChanged, + nulldrv_WineGetNativeWindowAt, + nulldrv_WineGetNativeDroptarget, /* system parameters */ nulldrv_SystemParametersInfo }; @@ -826,6 +840,8 @@ static USER_DRIVER lazy_load_driver = nulldrv_WindowMessage, nulldrv_WindowPosChanging, nulldrv_WindowPosChanged, + nulldrv_WineGetNativeWindowAt, + nulldrv_WineGetNativeDroptarget, /* system parameters */ nulldrv_SystemParametersInfo }; diff --git a/dlls/user32/user32.spec b/dlls/user32/user32.spec index a8bbe83..dbdb20e 100644 --- a/dlls/user32/user32.spec +++ b/dlls/user32/user32.spec @@ -785,5 +785,7 @@ # All functions must be prefixed with '__wine_' (for internal functions) # or 'wine_' (for user-visible functions) to avoid namespace conflicts. # +@ cdecl __wine_get_native_window_at(long long) +@ cdecl __wine_get_native_droptarget(ptr) @ cdecl __wine_send_input(long ptr) @ cdecl __wine_set_pixel_format(long long) diff --git a/dlls/user32/user_private.h b/dlls/user32/user_private.h index adf3f7d..b3a4499 100644 --- a/dlls/user32/user_private.h +++ b/dlls/user32/user_private.h @@ -115,6 +115,8 @@ typedef struct tagUSER_DRIVER { LRESULT (CDECL *pWindowMessage)(HWND,UINT,WPARAM,LPARAM); void (CDECL *pWindowPosChanging)(HWND,HWND,UINT,const RECT *,const RECT *,RECT *,struct window_surface**); void (CDECL *pWindowPosChanged)(HWND,HWND,UINT,const RECT *,const RECT *,const RECT *,const RECT *,struct window_surface*); + ULONG_PTR (CDECL *pWineGetNativeWindowAt)(LONG,LONG); + void* (CDECL *pWineGetNativeDroptarget)(ULONG_PTR); /* system parameters */ BOOL (CDECL *pSystemParametersInfo)(UINT,UINT,void*,UINT); } USER_DRIVER; diff --git a/dlls/user32/win.c b/dlls/user32/win.c index 0516193..3d263e7 100644 --- a/dlls/user32/win.c +++ b/dlls/user32/win.c @@ -3897,3 +3897,19 @@ BOOL WINAPI RegisterTouchWindow(HWND hwnd, ULONG flags) SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } + +/***************************************************************************** + * __wine_get_native_window_at (USER32.@) + */ +ULONG_PTR CDECL __wine_get_native_window_at(LONG x, LONG y) +{ + return USER_Driver->pWineGetNativeWindowAt(x, y); +} + +/***************************************************************************** + * __wine_get_native_droptarget (USER32.@) + */ +void* CDECL __wine_get_native_droptarget(ULONG_PTR nativeWindow) +{ + return USER_Driver->pWineGetNativeDroptarget(nativeWindow); +} diff --git a/dlls/winemac.drv/dragdrop.c b/dlls/winemac.drv/dragdrop.c index ad0bc8c..eb2cfde 100644 --- a/dlls/winemac.drv/dragdrop.c +++ b/dlls/winemac.drv/dragdrop.c @@ -632,3 +632,20 @@ BOOL query_drag_operation(macdrv_query* query) TRACE(" -> %s\n", ret ? "TRUE" : "FALSE"); return ret; } + +/************************************************************************** + * macdrv_WineGetNativeWindowAt + */ +ULONG_PTR CDECL macdrv_WineGetNativeWindowAt(LONG x, LONG y) +{ + /* FIXME: implement */ + return 0; +} + +/************************************************************************** + * macdrv_WineGetNativeDroptarget + */ +IDropTarget* CDECL X11DRV_WineGetNativeDroptarget(ULONG_PTR nativeWindow) +{ + return NULL; +} diff --git a/dlls/winemac.drv/winemac.drv.spec b/dlls/winemac.drv/winemac.drv.spec index 58a894b..dc5eadf 100644 --- a/dlls/winemac.drv/winemac.drv.spec +++ b/dlls/winemac.drv/winemac.drv.spec @@ -50,6 +50,9 @@ @ cdecl WindowMessage(long long long long) macdrv_WindowMessage @ cdecl WindowPosChanged(long long long ptr ptr ptr ptr ptr) macdrv_WindowPosChanged @ cdecl WindowPosChanging(long long long ptr ptr ptr ptr) macdrv_WindowPosChanging +@ cdecl WineGetNativeWindowAt(long long) macdrv_WineGetNativeWindowAt +@ cdecl WineGetNativeDroptarget(ptr) macdrv_WineGetNativeDroptarget + # System tray @ cdecl wine_notify_icon(long ptr) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 06e2294..36c144b 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -857,6 +857,9 @@ static void set_initial_wm_hints( Display *display, Window window ) XChangeProperty(display, window, x11drv_atom(_NET_WM_PID), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&i, 1); + i = 1; + XChangeProperty( display, window, x11drv_atom(IAmWine), + XA_CARDINAL, 32, PropModeReplace, (unsigned char*)&i, 1 ); XChangeProperty( display, window, x11drv_atom(XdndAware), XA_ATOM, 32, PropModeReplace, (unsigned char*)&dndVersion, 1 ); diff --git a/dlls/winex11.drv/winex11.drv.spec b/dlls/winex11.drv/winex11.drv.spec index b6e5c05..78dba8b 100644 --- a/dlls/winex11.drv/winex11.drv.spec +++ b/dlls/winex11.drv/winex11.drv.spec @@ -52,6 +52,8 @@ @ cdecl WindowMessage(long long long long) X11DRV_WindowMessage @ cdecl WindowPosChanging(long long long ptr ptr ptr ptr) X11DRV_WindowPosChanging @ cdecl WindowPosChanged(long long long ptr ptr ptr ptr ptr) X11DRV_WindowPosChanged +@ cdecl WineGetNativeWindowAt(long long) X11DRV_WineGetNativeWindowAt +@ cdecl WineGetNativeDroptarget(ptr) X11DRV_WineGetNativeDroptarget @ cdecl SystemParametersInfo(long long ptr long) X11DRV_SystemParametersInfo # WinTab32 diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 533af2c..7fe9845 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -438,6 +438,7 @@ enum x11drv_atoms XATOM__NET_WORKAREA, XATOM__XEMBED, XATOM__XEMBED_INFO, + XATOM_IAmWine, XATOM_XdndAware, XATOM_XdndEnter, XATOM_XdndPosition, diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index 85c69bf..f7c8e9b 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -153,6 +153,7 @@ static const char * const atom_names[NB_XATOMS - FIRST_XATOM] = "_NET_WORKAREA", "_XEMBED", "_XEMBED_INFO", + "IAmWine", "XdndAware", "XdndEnter", "XdndPosition", diff --git a/dlls/winex11.drv/xdnd.c b/dlls/winex11.drv/xdnd.c index f6ce5e9..fcd2841 100644 --- a/dlls/winex11.drv/xdnd.c +++ b/dlls/winex11.drv/xdnd.c @@ -867,3 +867,71 @@ static IDataObjectVtbl xdndDataObjectVtbl = }; static IDataObject XDNDDataObject = { &xdndDataObjectVtbl }; + +/************************************************************************** + * X11DRV_WineGetNativeWindowAt + */ +ULONG_PTR CDECL X11DRV_WineGetNativeWindowAt(LONG x, LONG y) +{ + int destX, destY; + Window destWindow = 0; + Window childWindow = 0; + + for (destWindow = root_window; + XTranslateCoordinates(thread_display(), root_window, destWindow, x, y, &destX, &destY, &childWindow) && childWindow; + destWindow = childWindow) + { + HWND hwnd; + Atom acttype; + int actfmt; + unsigned long count, bytesret; + int *iAmWine = NULL; + Atom *xdndAware = NULL; + + /* We only want to return an X11 window if there is no Wine window, so check those first */ + if (XFindContext( thread_display(), childWindow, winContext, (char **)&hwnd ) == 0) + { + TRACE("on Wine window %p\n", hwnd); + return 0; + } + + /* Sadly the above only works for the drag source window itself, as + * SetCapture() in DoDragDrag() seems to break winContext for other Wine windows, + * so we also have to do it by reading a custom property only Wine sets... */ + XGetWindowProperty(thread_display(), childWindow, x11drv_atom(IAmWine), + 0, 1, FALSE, XA_CARDINAL, &acttype, &actfmt, &count, + &bytesret, (unsigned char**)&iAmWine); + if (iAmWine) + { + BOOL isWineWindow = *iAmWine == 1; + XFree(iAmWine); + if (isWineWindow) + { + TRACE("on some Wine window\n"); + return 0; + } + } + + /* If XdndAware is set, stop the search right there */ + XGetWindowProperty(thread_display(), childWindow, x11drv_atom(XdndAware), + 0, 1, FALSE, XA_ATOM, &acttype, &actfmt, &count, + &bytesret, (unsigned char**)&xdndAware); + if (xdndAware) + { + Atom value = *xdndAware; + XFree(xdndAware); + TRACE("on droppable (XDnD version %lu) X11 window 0x%lx\n", value, childWindow); + return childWindow; + } + } + TRACE("on non-droppable X11 window 0x%lx\n", destWindow); + return destWindow; +} + +/************************************************************************** + * X11DRV_WineGetNativeDroptarget + */ +IDropTarget* CDECL X11DRV_WineGetNativeDroptarget(ULONG_PTR nativeWindow) +{ + return NULL; +} diff --git a/include/winuser.h b/include/winuser.h index 3d9b937..62e0334 100644 --- a/include/winuser.h +++ b/include/winuser.h @@ -4057,6 +4057,8 @@ WINUSERAPI INT WINAPI wvsprintfW(LPWSTR,LPCWSTR,__ms_va_list); WORD WINAPI SYSTEM_KillSystemTimer( WORD ); #ifdef __WINESRC__ +WINUSERAPI ULONG_PTR CDECL __wine_get_native_window_at( LONG x, LONG y ); +WINUSERAPI void* CDECL __wine_get_native_droptarget( ULONG_PTR nativeWindow ); WINUSERAPI BOOL CDECL __wine_send_input( HWND hwnd, const INPUT *input ); #endif