Commit 0c468c81 authored by Ulrich Czekalla's avatar Ulrich Czekalla Committed by Alexandre Julliard

x11drv: Improve handling of the case where the clipboard is opened

with a window from another thread or process.
parent 00b2511b
...@@ -102,7 +102,6 @@ static const USER_DRIVER *load_driver(void) ...@@ -102,7 +102,6 @@ static const USER_DRIVER *load_driver(void)
GET_USER_FUNC(RegisterClipboardFormat); GET_USER_FUNC(RegisterClipboardFormat);
GET_USER_FUNC(GetClipboardFormatName); GET_USER_FUNC(GetClipboardFormatName);
GET_USER_FUNC(EndClipboardUpdate); GET_USER_FUNC(EndClipboardUpdate);
GET_USER_FUNC(ResetSelectionOwner);
GET_USER_FUNC(ChangeDisplaySettingsEx); GET_USER_FUNC(ChangeDisplaySettingsEx);
GET_USER_FUNC(EnumDisplaySettingsEx); GET_USER_FUNC(EnumDisplaySettingsEx);
GET_USER_FUNC(CreateDesktopWindow); GET_USER_FUNC(CreateDesktopWindow);
...@@ -239,8 +238,9 @@ static void nulldrv_SetScreenSaveActive( BOOL on ) ...@@ -239,8 +238,9 @@ static void nulldrv_SetScreenSaveActive( BOOL on )
{ {
} }
static void nulldrv_AcquireClipboard( HWND hwnd ) static INT nulldrv_AcquireClipboard( HWND hwnd )
{ {
return 0;
} }
static BOOL nulldrv_CountClipboardFormats(void) static BOOL nulldrv_CountClipboardFormats(void)
...@@ -281,10 +281,6 @@ static UINT nulldrv_RegisterClipboardFormat( LPCWSTR name ) ...@@ -281,10 +281,6 @@ static UINT nulldrv_RegisterClipboardFormat( LPCWSTR name )
return 0; return 0;
} }
static void nulldrv_ResetSelectionOwner( HWND hwnd, BOOL flag )
{
}
static BOOL nulldrv_SetClipboardData( UINT format, HANDLE16 h16, HANDLE h32, BOOL owner ) static BOOL nulldrv_SetClipboardData( UINT format, HANDLE16 h16, HANDLE h32, BOOL owner )
{ {
return FALSE; return FALSE;
...@@ -438,7 +434,6 @@ static const USER_DRIVER null_driver = ...@@ -438,7 +434,6 @@ static const USER_DRIVER null_driver =
nulldrv_GetClipboardFormatName, nulldrv_GetClipboardFormatName,
nulldrv_IsClipboardFormatAvailable, nulldrv_IsClipboardFormatAvailable,
nulldrv_RegisterClipboardFormat, nulldrv_RegisterClipboardFormat,
nulldrv_ResetSelectionOwner,
nulldrv_SetClipboardData, nulldrv_SetClipboardData,
/* display modes */ /* display modes */
nulldrv_ChangeDisplaySettingsEx, nulldrv_ChangeDisplaySettingsEx,
...@@ -563,9 +558,9 @@ static void loaderdrv_SetScreenSaveActive( BOOL on ) ...@@ -563,9 +558,9 @@ static void loaderdrv_SetScreenSaveActive( BOOL on )
load_driver()->pSetScreenSaveActive( on ); load_driver()->pSetScreenSaveActive( on );
} }
static void loaderdrv_AcquireClipboard( HWND hwnd ) static INT loaderdrv_AcquireClipboard( HWND hwnd )
{ {
load_driver()->pAcquireClipboard( hwnd ); return load_driver()->pAcquireClipboard( hwnd );
} }
static BOOL loaderdrv_CountClipboardFormats(void) static BOOL loaderdrv_CountClipboardFormats(void)
...@@ -608,11 +603,6 @@ static UINT loaderdrv_RegisterClipboardFormat( LPCWSTR name ) ...@@ -608,11 +603,6 @@ static UINT loaderdrv_RegisterClipboardFormat( LPCWSTR name )
return load_driver()->pRegisterClipboardFormat( name ); return load_driver()->pRegisterClipboardFormat( name );
} }
static void loaderdrv_ResetSelectionOwner( HWND hwnd, BOOL flag )
{
load_driver()->pResetSelectionOwner( hwnd, flag );
}
static BOOL loaderdrv_SetClipboardData( UINT format, HANDLE16 h16, HANDLE h32, BOOL owner ) static BOOL loaderdrv_SetClipboardData( UINT format, HANDLE16 h16, HANDLE h32, BOOL owner )
{ {
return load_driver()->pSetClipboardData( format, h16, h32, owner ); return load_driver()->pSetClipboardData( format, h16, h32, owner );
...@@ -754,7 +744,6 @@ static const USER_DRIVER lazy_load_driver = ...@@ -754,7 +744,6 @@ static const USER_DRIVER lazy_load_driver =
loaderdrv_GetClipboardFormatName, loaderdrv_GetClipboardFormatName,
loaderdrv_IsClipboardFormatAvailable, loaderdrv_IsClipboardFormatAvailable,
loaderdrv_RegisterClipboardFormat, loaderdrv_RegisterClipboardFormat,
loaderdrv_ResetSelectionOwner,
loaderdrv_SetClipboardData, loaderdrv_SetClipboardData,
/* display modes */ /* display modes */
loaderdrv_ChangeDisplaySettingsEx, loaderdrv_ChangeDisplaySettingsEx,
......
...@@ -122,7 +122,7 @@ typedef struct tagUSER_DRIVER { ...@@ -122,7 +122,7 @@ typedef struct tagUSER_DRIVER {
BOOL (*pGetScreenSaveActive)(void); BOOL (*pGetScreenSaveActive)(void);
void (*pSetScreenSaveActive)(BOOL); void (*pSetScreenSaveActive)(BOOL);
/* clipboard functions */ /* clipboard functions */
void (*pAcquireClipboard)(HWND); /* Acquire selection */ INT (*pAcquireClipboard)(HWND); /* Acquire selection */
BOOL (*pCountClipboardFormats)(void); /* Count available clipboard formats */ BOOL (*pCountClipboardFormats)(void); /* Count available clipboard formats */
void (*pEmptyClipboard)(BOOL); /* Empty clipboard data */ void (*pEmptyClipboard)(BOOL); /* Empty clipboard data */
void (*pEndClipboardUpdate)(void); /* End clipboard update */ void (*pEndClipboardUpdate)(void); /* End clipboard update */
...@@ -131,7 +131,6 @@ typedef struct tagUSER_DRIVER { ...@@ -131,7 +131,6 @@ typedef struct tagUSER_DRIVER {
INT (*pGetClipboardFormatName)(UINT, LPWSTR, UINT); /* Get a clipboard format name */ INT (*pGetClipboardFormatName)(UINT, LPWSTR, UINT); /* Get a clipboard format name */
BOOL (*pIsClipboardFormatAvailable)(UINT); /* Check if specified format is available */ BOOL (*pIsClipboardFormatAvailable)(UINT); /* Check if specified format is available */
UINT (*pRegisterClipboardFormat)(LPCWSTR); /* Register a clipboard format */ UINT (*pRegisterClipboardFormat)(LPCWSTR); /* Register a clipboard format */
void (*pResetSelectionOwner)(HWND, BOOL);
BOOL (*pSetClipboardData)(UINT, HANDLE16, HANDLE, BOOL); /* Set specified selection data */ BOOL (*pSetClipboardData)(UINT, HANDLE16, HANDLE, BOOL); /* Set specified selection data */
/* display modes */ /* display modes */
LONG (*pChangeDisplaySettingsEx)(LPCWSTR,LPDEVMODEW,HWND,DWORD,LPVOID); LONG (*pChangeDisplaySettingsEx)(LPCWSTR,LPDEVMODEW,HWND,DWORD,LPVOID);
......
...@@ -1292,7 +1292,6 @@ static void WIN_SendDestroyMsg( HWND hwnd ) ...@@ -1292,7 +1292,6 @@ static void WIN_SendDestroyMsg( HWND hwnd )
if (hwnd == info.hwndCaret) DestroyCaret(); if (hwnd == info.hwndCaret) DestroyCaret();
if (hwnd == info.hwndActive) WINPOS_ActivateOtherWindow( hwnd ); if (hwnd == info.hwndActive) WINPOS_ActivateOtherWindow( hwnd );
} }
USER_Driver->pResetSelectionOwner( hwnd, TRUE );
/* /*
* Send the WM_DESTROY to the window. * Send the WM_DESTROY to the window.
...@@ -1358,8 +1357,6 @@ BOOL WINAPI DestroyWindow( HWND hwnd ) ...@@ -1358,8 +1357,6 @@ BOOL WINAPI DestroyWindow( HWND hwnd )
if (!IsWindow(hwnd)) return TRUE; if (!IsWindow(hwnd)) return TRUE;
USER_Driver->pResetSelectionOwner( hwnd, FALSE ); /* before the window is unmapped */
/* Hide the window */ /* Hide the window */
if (GetWindowLongW( hwnd, GWL_STYLE ) & WS_VISIBLE) if (GetWindowLongW( hwnd, GWL_STYLE ) & WS_VISIBLE)
{ {
......
...@@ -334,9 +334,14 @@ static Window thread_selection_wnd(void) ...@@ -334,9 +334,14 @@ static Window thread_selection_wnd(void)
if (!w) if (!w)
{ {
XSetWindowAttributes attr;
attr.event_mask = (ExposureMask | KeyPressMask | KeyReleaseMask | PointerMotionMask |
ButtonPressMask | ButtonReleaseMask | EnterWindowMask);
wine_tsx11_lock(); wine_tsx11_lock();
w = XCreateWindow(thread_display(), root_window, 0, 0, 1, 1, 0, screen_depth, w = XCreateWindow(thread_display(), root_window, 0, 0, 1, 1, 0, screen_depth,
InputOutput, CopyFromParent, 0, NULL); InputOutput, CopyFromParent, CWEventMask, &attr);
wine_tsx11_unlock(); wine_tsx11_unlock();
if (w) if (w)
...@@ -2304,70 +2309,67 @@ INT X11DRV_GetClipboardFormatName(UINT wFormat, LPWSTR retStr, INT maxlen) ...@@ -2304,70 +2309,67 @@ INT X11DRV_GetClipboardFormatName(UINT wFormat, LPWSTR retStr, INT maxlen)
/************************************************************************** /**************************************************************************
* AcquireClipboard (X11DRV.@) * AcquireClipboard (X11DRV.@)
*/ */
void X11DRV_AcquireClipboard(HWND hWndClipWindow) int X11DRV_AcquireClipboard(HWND hWndClipWindow)
{ {
DWORD procid;
Window owner;
Display *display = thread_display(); Display *display = thread_display();
TRACE(" %p\n", hWndClipWindow);
/* /*
* Acquire X selection if we don't already own it. * It's important that the selection get acquired from the thread
* Note that we only acquire the selection if it hasn't been already * that owns the clipboard window. The primary reason is that we know
* acquired by us, and ignore the fact that another X window may be * it is running a message loop and therefore can process the
* asserting ownership. The reason for this is we need *any* top level * X selection events.
* X window to hold selection ownership. The actual clipboard data requests
* are made via GetClipboardData from EVENT_SelectionRequest and this
* ensures that the real HWND owner services the request.
* If the owning X window gets destroyed the selection ownership is
* re-cycled to another top level X window in X11DRV_CLIPBOARD_ResetOwner.
*
*/ */
if (!(selectionAcquired == (S_PRIMARY | S_CLIPBOARD))) if (hWndClipWindow &&
GetCurrentThreadId() != GetWindowThreadProcessId(hWndClipWindow, &procid))
{ {
Window owner; if (procid != GetCurrentProcessId())
{
if (!hWndClipWindow) WARN("Setting clipboard owner to other process is not supported\n");
hWndClipWindow = GetActiveWindow(); hWndClipWindow = NULL;
}
hWndClipWindow = GetAncestor(hWndClipWindow, GA_ROOT); else
if (GetCurrentThreadId() != GetWindowThreadProcessId(hWndClipWindow, NULL))
{ {
TRACE("Thread %lx is acquiring selection with thread %lx's window %p\n", TRACE("Thread %lx is acquiring selection with thread %lx's window %p\n",
GetCurrentThreadId(), GetCurrentThreadId(),
GetWindowThreadProcessId(hWndClipWindow, NULL), GetWindowThreadProcessId(hWndClipWindow, NULL), hWndClipWindow);
hWndClipWindow);
if (!SendMessageW(hWndClipWindow, WM_X11DRV_ACQUIRE_SELECTION, 0, 0)) return SendMessageW(hWndClipWindow, WM_X11DRV_ACQUIRE_SELECTION, 0, 0);
ERR("Failed to acquire selection\n");
return;
} }
}
owner = X11DRV_get_whole_window(hWndClipWindow); owner = thread_selection_wnd();
wine_tsx11_lock(); wine_tsx11_lock();
/* Grab PRIMARY selection if not owned */
if (use_primary_selection && !(selectionAcquired & S_PRIMARY))
XSetSelectionOwner(display, XA_PRIMARY, owner, CurrentTime);
/* Grab CLIPBOARD selection if not owned */ selectionAcquired = 0;
if (!(selectionAcquired & S_CLIPBOARD)) selectionWindow = 0;
XSetSelectionOwner(display, x11drv_atom(CLIPBOARD), owner, CurrentTime);
if (use_primary_selection && XGetSelectionOwner(display,XA_PRIMARY) == owner) /* Grab PRIMARY selection if not owned */
selectionAcquired |= S_PRIMARY; if (use_primary_selection)
XSetSelectionOwner(display, XA_PRIMARY, owner, CurrentTime);
if (XGetSelectionOwner(display,x11drv_atom(CLIPBOARD)) == owner) /* Grab CLIPBOARD selection if not owned */
selectionAcquired |= S_CLIPBOARD; XSetSelectionOwner(display, x11drv_atom(CLIPBOARD), owner, CurrentTime);
wine_tsx11_unlock();
if (selectionAcquired) if (use_primary_selection && XGetSelectionOwner(display, XA_PRIMARY) == owner)
{ selectionAcquired |= S_PRIMARY;
selectionWindow = owner;
TRACE("Grabbed X selection, owner=(%08x)\n", (unsigned) owner); if (XGetSelectionOwner(display,x11drv_atom(CLIPBOARD)) == owner)
} selectionAcquired |= S_CLIPBOARD;
}
else wine_tsx11_unlock();
if (selectionAcquired)
{ {
ERR("Received request to acquire selection but process is already owner=(%08x)\n", (unsigned) selectionWindow); selectionWindow = owner;
TRACE("Grabbed X selection, owner=(%08x)\n", (unsigned) owner);
} }
return 1;
} }
...@@ -2610,96 +2612,42 @@ BOOL X11DRV_GetClipboardData(UINT wFormat, HANDLE16* phData16, HANDLE* phData32) ...@@ -2610,96 +2612,42 @@ BOOL X11DRV_GetClipboardData(UINT wFormat, HANDLE16* phData16, HANDLE* phData32)
/************************************************************************** /**************************************************************************
* ResetSelectionOwner (X11DRV.@) * ResetSelectionOwner
* *
* Called from DestroyWindow() to prevent X selection from being lost when * Called when the thread owning the selection is destroyed and we need to
* a top level window is destroyed, by switching ownership to another top * preserve the selection ownership. We look for another top level window
* level window. * in this process and send it a message to acquire the selection.
* Any top level window can own the selection. See X11DRV_CLIPBOARD_Acquire
* for a more detailed description of this.
*/ */
void X11DRV_ResetSelectionOwner(HWND hwnd, BOOL bFooBar) void X11DRV_ResetSelectionOwner(void)
{ {
Display *display = thread_display(); HWND hwnd;
HWND hWndClipOwner = 0; DWORD procid;
HWND tmp;
Window XWnd = X11DRV_get_whole_window(hwnd);
BOOL bLostSelection = FALSE;
Window selectionPrevWindow;
/* There is nothing to do if we don't own the selection,
* or if the X window which currently owns the selection is different
* from the one passed in.
*/
if (!selectionAcquired || XWnd != selectionWindow
|| selectionWindow == None )
return;
if ((bFooBar && XWnd) || (!bFooBar && !XWnd))
return;
hWndClipOwner = GetClipboardOwner();
TRACE("clipboard owner = %p, selection window = %08x\n",
hWndClipOwner, (unsigned)selectionWindow);
/* now try to salvage current selection from being destroyed by X */
TRACE("checking %08x\n", (unsigned) XWnd);
selectionPrevWindow = selectionWindow; TRACE("\n");
selectionWindow = None;
if (!(tmp = GetWindow(hwnd, GW_HWNDNEXT))) if (!selectionAcquired || thread_selection_wnd() != selectionWindow)
tmp = GetWindow(hwnd, GW_HWNDFIRST); return;
if (tmp && tmp != hwnd) selectionAcquired = S_NOSELECTION;
selectionWindow = X11DRV_get_whole_window(tmp); selectionWindow = 0;
if (selectionWindow != None) hwnd = GetWindow(GetDesktopWindow(), GW_CHILD);
do
{ {
/* We must pretend that we don't own the selection while making the switch if (GetCurrentThreadId() != GetWindowThreadProcessId(hwnd, &procid))
* since a SelectionClear event will be sent to the last owner.
* If there is no owner X11DRV_CLIPBOARD_ReleaseSelection will do nothing.
*/
int saveSelectionState = selectionAcquired;
selectionAcquired = S_NOSELECTION;
TRACE("\tswitching selection from %08x to %08x\n",
(unsigned)selectionPrevWindow, (unsigned)selectionWindow);
wine_tsx11_lock();
/* Assume ownership for the PRIMARY and CLIPBOARD selection */
if (saveSelectionState & S_PRIMARY)
XSetSelectionOwner(display, XA_PRIMARY, selectionWindow, CurrentTime);
XSetSelectionOwner(display, x11drv_atom(CLIPBOARD), selectionWindow, CurrentTime);
/* Restore the selection masks */
selectionAcquired = saveSelectionState;
/* Lose the selection if something went wrong */
if (((saveSelectionState & S_PRIMARY) &&
(XGetSelectionOwner(display, XA_PRIMARY) != selectionWindow)) ||
(XGetSelectionOwner(display, x11drv_atom(CLIPBOARD)) != selectionWindow))
{ {
bLostSelection = TRUE; if (GetCurrentProcessId() == procid)
{
if (SendMessageW(hwnd, WM_X11DRV_ACQUIRE_SELECTION, 0, 0))
return;
}
} }
wine_tsx11_unlock(); } while ((hwnd = GetWindow(hwnd, GW_HWNDNEXT)) != NULL);
}
else
{
bLostSelection = TRUE;
}
if (bLostSelection) WARN("Failed to find another thread to take selection ownership. Clipboard data will be lost.\n");
{
TRACE("Lost the selection!\n");
X11DRV_CLIPBOARD_ReleaseOwnership(); X11DRV_CLIPBOARD_ReleaseOwnership();
selectionAcquired = S_NOSELECTION; X11DRV_EmptyClipboard(FALSE);
selectionWindow = 0;
}
} }
...@@ -3091,7 +3039,6 @@ END: ...@@ -3091,7 +3039,6 @@ END:
*/ */
void X11DRV_SelectionRequest( HWND hWnd, XEvent *event ) void X11DRV_SelectionRequest( HWND hWnd, XEvent *event )
{ {
if (!hWnd) return;
X11DRV_HandleSelectionRequest( hWnd, &event->xselectionrequest, FALSE ); X11DRV_HandleSelectionRequest( hWnd, &event->xselectionrequest, FALSE );
} }
...@@ -3102,7 +3049,6 @@ void X11DRV_SelectionRequest( HWND hWnd, XEvent *event ) ...@@ -3102,7 +3049,6 @@ void X11DRV_SelectionRequest( HWND hWnd, XEvent *event )
void X11DRV_SelectionClear( HWND hWnd, XEvent *xev ) void X11DRV_SelectionClear( HWND hWnd, XEvent *xev )
{ {
XSelectionClearEvent *event = &xev->xselectionclear; XSelectionClearEvent *event = &xev->xselectionclear;
if (!hWnd) return;
if (event->selection == XA_PRIMARY || event->selection == x11drv_atom(CLIPBOARD)) if (event->selection == XA_PRIMARY || event->selection == x11drv_atom(CLIPBOARD))
X11DRV_CLIPBOARD_ReleaseSelection( event->selection, event->window, hWnd, event->time ); X11DRV_CLIPBOARD_ReleaseSelection( event->selection, event->window, hWnd, event->time );
} }
...@@ -937,8 +937,7 @@ LRESULT X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ) ...@@ -937,8 +937,7 @@ LRESULT X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
switch(msg) switch(msg)
{ {
case WM_X11DRV_ACQUIRE_SELECTION: case WM_X11DRV_ACQUIRE_SELECTION:
X11DRV_AcquireClipboard( hwnd ); return X11DRV_AcquireClipboard( hwnd );
return 0;
case WM_X11DRV_DELETE_WINDOW: case WM_X11DRV_DELETE_WINDOW:
return SendMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 ); return SendMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
default: default:
......
...@@ -98,7 +98,6 @@ ...@@ -98,7 +98,6 @@
@ cdecl MsgWaitForMultipleObjectsEx(long ptr long long long) X11DRV_MsgWaitForMultipleObjectsEx @ cdecl MsgWaitForMultipleObjectsEx(long ptr long long long) X11DRV_MsgWaitForMultipleObjectsEx
@ cdecl RegisterClipboardFormat(wstr) X11DRV_RegisterClipboardFormat @ cdecl RegisterClipboardFormat(wstr) X11DRV_RegisterClipboardFormat
@ cdecl ReleaseDC(long long long) X11DRV_ReleaseDC @ cdecl ReleaseDC(long long long) X11DRV_ReleaseDC
@ cdecl ResetSelectionOwner(long long) X11DRV_ResetSelectionOwner
@ cdecl ScrollDC(long long long ptr ptr long ptr) X11DRV_ScrollDC @ cdecl ScrollDC(long long long ptr ptr long ptr) X11DRV_ScrollDC
@ cdecl SetClipboardData(long long long long) X11DRV_SetClipboardData @ cdecl SetClipboardData(long long long long) X11DRV_SetClipboardData
@ cdecl SetFocus(long) X11DRV_SetFocus @ cdecl SetFocus(long) X11DRV_SetFocus
......
...@@ -662,7 +662,8 @@ extern void invalidate_dce( HWND hwnd, const RECT *rect ); ...@@ -662,7 +662,8 @@ extern void invalidate_dce( HWND hwnd, const RECT *rect );
extern XContext winContext; extern XContext winContext;
extern void X11DRV_InitClipboard(void); extern void X11DRV_InitClipboard(void);
extern void X11DRV_AcquireClipboard(HWND hWndClipWindow); extern int X11DRV_AcquireClipboard(HWND hWndClipWindow);
extern void X11DRV_ResetSelectionOwner(void);
extern void X11DRV_SetFocus( HWND hwnd ); extern void X11DRV_SetFocus( HWND hwnd );
extern Cursor X11DRV_GetCursor( Display *display, struct tagCURSORICONINFO *ptr ); extern Cursor X11DRV_GetCursor( Display *display, struct tagCURSORICONINFO *ptr );
extern void X11DRV_InitKeyboard(void); extern void X11DRV_InitKeyboard(void);
......
...@@ -457,6 +457,7 @@ static void thread_detach(void) ...@@ -457,6 +457,7 @@ static void thread_detach(void)
if (data) if (data)
{ {
X11DRV_ResetSelectionOwner();
CloseHandle( data->display_fd ); CloseHandle( data->display_fd );
wine_tsx11_lock(); wine_tsx11_lock();
XCloseDisplay( data->display ); XCloseDisplay( data->display );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment