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)
GET_USER_FUNC(RegisterClipboardFormat);
GET_USER_FUNC(GetClipboardFormatName);
GET_USER_FUNC(EndClipboardUpdate);
GET_USER_FUNC(ResetSelectionOwner);
GET_USER_FUNC(ChangeDisplaySettingsEx);
GET_USER_FUNC(EnumDisplaySettingsEx);
GET_USER_FUNC(CreateDesktopWindow);
......@@ -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)
......@@ -281,10 +281,6 @@ static UINT nulldrv_RegisterClipboardFormat( LPCWSTR name )
return 0;
}
static void nulldrv_ResetSelectionOwner( HWND hwnd, BOOL flag )
{
}
static BOOL nulldrv_SetClipboardData( UINT format, HANDLE16 h16, HANDLE h32, BOOL owner )
{
return FALSE;
......@@ -438,7 +434,6 @@ static const USER_DRIVER null_driver =
nulldrv_GetClipboardFormatName,
nulldrv_IsClipboardFormatAvailable,
nulldrv_RegisterClipboardFormat,
nulldrv_ResetSelectionOwner,
nulldrv_SetClipboardData,
/* display modes */
nulldrv_ChangeDisplaySettingsEx,
......@@ -563,9 +558,9 @@ static void loaderdrv_SetScreenSaveActive( BOOL 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)
......@@ -608,11 +603,6 @@ static UINT loaderdrv_RegisterClipboardFormat( LPCWSTR 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 )
{
return load_driver()->pSetClipboardData( format, h16, h32, owner );
......@@ -754,7 +744,6 @@ static const USER_DRIVER lazy_load_driver =
loaderdrv_GetClipboardFormatName,
loaderdrv_IsClipboardFormatAvailable,
loaderdrv_RegisterClipboardFormat,
loaderdrv_ResetSelectionOwner,
loaderdrv_SetClipboardData,
/* display modes */
loaderdrv_ChangeDisplaySettingsEx,
......
......@@ -122,7 +122,7 @@ typedef struct tagUSER_DRIVER {
BOOL (*pGetScreenSaveActive)(void);
void (*pSetScreenSaveActive)(BOOL);
/* clipboard functions */
void (*pAcquireClipboard)(HWND); /* Acquire selection */
INT (*pAcquireClipboard)(HWND); /* Acquire selection */
BOOL (*pCountClipboardFormats)(void); /* Count available clipboard formats */
void (*pEmptyClipboard)(BOOL); /* Empty clipboard data */
void (*pEndClipboardUpdate)(void); /* End clipboard update */
......@@ -131,7 +131,6 @@ typedef struct tagUSER_DRIVER {
INT (*pGetClipboardFormatName)(UINT, LPWSTR, UINT); /* Get a clipboard format name */
BOOL (*pIsClipboardFormatAvailable)(UINT); /* Check if specified format is available */
UINT (*pRegisterClipboardFormat)(LPCWSTR); /* Register a clipboard format */
void (*pResetSelectionOwner)(HWND, BOOL);
BOOL (*pSetClipboardData)(UINT, HANDLE16, HANDLE, BOOL); /* Set specified selection data */
/* display modes */
LONG (*pChangeDisplaySettingsEx)(LPCWSTR,LPDEVMODEW,HWND,DWORD,LPVOID);
......
......@@ -1292,7 +1292,6 @@ static void WIN_SendDestroyMsg( HWND hwnd )
if (hwnd == info.hwndCaret) DestroyCaret();
if (hwnd == info.hwndActive) WINPOS_ActivateOtherWindow( hwnd );
}
USER_Driver->pResetSelectionOwner( hwnd, TRUE );
/*
* Send the WM_DESTROY to the window.
......@@ -1358,8 +1357,6 @@ BOOL WINAPI DestroyWindow( HWND hwnd )
if (!IsWindow(hwnd)) return TRUE;
USER_Driver->pResetSelectionOwner( hwnd, FALSE ); /* before the window is unmapped */
/* Hide the window */
if (GetWindowLongW( hwnd, GWL_STYLE ) & WS_VISIBLE)
{
......
......@@ -334,9 +334,14 @@ static Window thread_selection_wnd(void)
if (!w)
{
XSetWindowAttributes attr;
attr.event_mask = (ExposureMask | KeyPressMask | KeyReleaseMask | PointerMotionMask |
ButtonPressMask | ButtonReleaseMask | EnterWindowMask);
wine_tsx11_lock();
w = XCreateWindow(thread_display(), root_window, 0, 0, 1, 1, 0, screen_depth,
InputOutput, CopyFromParent, 0, NULL);
InputOutput, CopyFromParent, CWEventMask, &attr);
wine_tsx11_unlock();
if (w)
......@@ -2304,70 +2309,67 @@ INT X11DRV_GetClipboardFormatName(UINT wFormat, LPWSTR retStr, INT maxlen)
/**************************************************************************
* AcquireClipboard (X11DRV.@)
*/
void X11DRV_AcquireClipboard(HWND hWndClipWindow)
int X11DRV_AcquireClipboard(HWND hWndClipWindow)
{
DWORD procid;
Window owner;
Display *display = thread_display();
TRACE(" %p\n", hWndClipWindow);
/*
* Acquire X selection if we don't already own it.
* Note that we only acquire the selection if it hasn't been already
* acquired by us, and ignore the fact that another X window may be
* asserting ownership. The reason for this is we need *any* top level
* 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.
*
* It's important that the selection get acquired from the thread
* that owns the clipboard window. The primary reason is that we know
* it is running a message loop and therefore can process the
* X selection events.
*/
if (!(selectionAcquired == (S_PRIMARY | S_CLIPBOARD)))
if (hWndClipWindow &&
GetCurrentThreadId() != GetWindowThreadProcessId(hWndClipWindow, &procid))
{
Window owner;
if (!hWndClipWindow)
hWndClipWindow = GetActiveWindow();
hWndClipWindow = GetAncestor(hWndClipWindow, GA_ROOT);
if (GetCurrentThreadId() != GetWindowThreadProcessId(hWndClipWindow, NULL))
if (procid != GetCurrentProcessId())
{
WARN("Setting clipboard owner to other process is not supported\n");
hWndClipWindow = NULL;
}
else
{
TRACE("Thread %lx is acquiring selection with thread %lx's window %p\n",
GetCurrentThreadId(),
GetWindowThreadProcessId(hWndClipWindow, NULL),
hWndClipWindow);
if (!SendMessageW(hWndClipWindow, WM_X11DRV_ACQUIRE_SELECTION, 0, 0))
ERR("Failed to acquire selection\n");
return;
GetWindowThreadProcessId(hWndClipWindow, NULL), hWndClipWindow);
return SendMessageW(hWndClipWindow, WM_X11DRV_ACQUIRE_SELECTION, 0, 0);
}
}
owner = X11DRV_get_whole_window(hWndClipWindow);
owner = thread_selection_wnd();
wine_tsx11_lock();
/* Grab PRIMARY selection if not owned */
if (use_primary_selection && !(selectionAcquired & S_PRIMARY))
XSetSelectionOwner(display, XA_PRIMARY, owner, CurrentTime);
wine_tsx11_lock();
/* Grab CLIPBOARD selection if not owned */
if (!(selectionAcquired & S_CLIPBOARD))
XSetSelectionOwner(display, x11drv_atom(CLIPBOARD), owner, CurrentTime);
selectionAcquired = 0;
selectionWindow = 0;
if (use_primary_selection && XGetSelectionOwner(display,XA_PRIMARY) == owner)
selectionAcquired |= S_PRIMARY;
/* Grab PRIMARY selection if not owned */
if (use_primary_selection)
XSetSelectionOwner(display, XA_PRIMARY, owner, CurrentTime);
if (XGetSelectionOwner(display,x11drv_atom(CLIPBOARD)) == owner)
selectionAcquired |= S_CLIPBOARD;
wine_tsx11_unlock();
/* Grab CLIPBOARD selection if not owned */
XSetSelectionOwner(display, x11drv_atom(CLIPBOARD), owner, CurrentTime);
if (selectionAcquired)
{
selectionWindow = owner;
TRACE("Grabbed X selection, owner=(%08x)\n", (unsigned) owner);
}
}
else
if (use_primary_selection && XGetSelectionOwner(display, XA_PRIMARY) == owner)
selectionAcquired |= S_PRIMARY;
if (XGetSelectionOwner(display,x11drv_atom(CLIPBOARD)) == owner)
selectionAcquired |= S_CLIPBOARD;
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)
/**************************************************************************
* ResetSelectionOwner (X11DRV.@)
* ResetSelectionOwner
*
* Called from DestroyWindow() to prevent X selection from being lost when
* a top level window is destroyed, by switching ownership to another top
* level window.
* Any top level window can own the selection. See X11DRV_CLIPBOARD_Acquire
* for a more detailed description of this.
* Called when the thread owning the selection is destroyed and we need to
* preserve the selection ownership. We look for another top level window
* in this process and send it a message to acquire the selection.
*/
void X11DRV_ResetSelectionOwner(HWND hwnd, BOOL bFooBar)
void X11DRV_ResetSelectionOwner(void)
{
Display *display = thread_display();
HWND hWndClipOwner = 0;
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);
HWND hwnd;
DWORD procid;
selectionPrevWindow = selectionWindow;
selectionWindow = None;
TRACE("\n");
if (!(tmp = GetWindow(hwnd, GW_HWNDNEXT)))
tmp = GetWindow(hwnd, GW_HWNDFIRST);
if (!selectionAcquired || thread_selection_wnd() != selectionWindow)
return;
if (tmp && tmp != hwnd)
selectionWindow = X11DRV_get_whole_window(tmp);
selectionAcquired = S_NOSELECTION;
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
* 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))
if (GetCurrentThreadId() != GetWindowThreadProcessId(hwnd, &procid))
{
bLostSelection = TRUE;
if (GetCurrentProcessId() == procid)
{
if (SendMessageW(hwnd, WM_X11DRV_ACQUIRE_SELECTION, 0, 0))
return;
}
}
wine_tsx11_unlock();
}
else
{
bLostSelection = TRUE;
}
} while ((hwnd = GetWindow(hwnd, GW_HWNDNEXT)) != NULL);
if (bLostSelection)
{
TRACE("Lost the selection!\n");
WARN("Failed to find another thread to take selection ownership. Clipboard data will be lost.\n");
X11DRV_CLIPBOARD_ReleaseOwnership();
selectionAcquired = S_NOSELECTION;
selectionWindow = 0;
}
X11DRV_CLIPBOARD_ReleaseOwnership();
X11DRV_EmptyClipboard(FALSE);
}
......@@ -3091,7 +3039,6 @@ END:
*/
void X11DRV_SelectionRequest( HWND hWnd, XEvent *event )
{
if (!hWnd) return;
X11DRV_HandleSelectionRequest( hWnd, &event->xselectionrequest, FALSE );
}
......@@ -3102,7 +3049,6 @@ void X11DRV_SelectionRequest( HWND hWnd, XEvent *event )
void X11DRV_SelectionClear( HWND hWnd, XEvent *xev )
{
XSelectionClearEvent *event = &xev->xselectionclear;
if (!hWnd) return;
if (event->selection == XA_PRIMARY || event->selection == x11drv_atom(CLIPBOARD))
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 )
switch(msg)
{
case WM_X11DRV_ACQUIRE_SELECTION:
X11DRV_AcquireClipboard( hwnd );
return 0;
return X11DRV_AcquireClipboard( hwnd );
case WM_X11DRV_DELETE_WINDOW:
return SendMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
default:
......
......@@ -98,7 +98,6 @@
@ cdecl MsgWaitForMultipleObjectsEx(long ptr long long long) X11DRV_MsgWaitForMultipleObjectsEx
@ cdecl RegisterClipboardFormat(wstr) X11DRV_RegisterClipboardFormat
@ 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 SetClipboardData(long long long long) X11DRV_SetClipboardData
@ cdecl SetFocus(long) X11DRV_SetFocus
......
......@@ -662,7 +662,8 @@ extern void invalidate_dce( HWND hwnd, const RECT *rect );
extern XContext winContext;
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 Cursor X11DRV_GetCursor( Display *display, struct tagCURSORICONINFO *ptr );
extern void X11DRV_InitKeyboard(void);
......
......@@ -457,6 +457,7 @@ static void thread_detach(void)
if (data)
{
X11DRV_ResetSelectionOwner();
CloseHandle( data->display_fd );
wine_tsx11_lock();
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