winex11: Run a single clipboard manager thread per window station, inside the explorer process.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2016-09-26 20:27:40 +09:00
parent a46d736012
commit a52d09198d
4 changed files with 229 additions and 233 deletions

View File

@ -87,7 +87,6 @@
#include "wine/list.h" #include "wine/list.h"
#include "wine/debug.h" #include "wine/debug.h"
#include "wine/unicode.h" #include "wine/unicode.h"
#include "wine/server.h"
WINE_DEFAULT_DEBUG_CHANNEL(clipboard); WINE_DEFAULT_DEBUG_CHANNEL(clipboard);
@ -97,25 +96,17 @@ WINE_DEFAULT_DEBUG_CHANNEL(clipboard);
#define SELECTION_UPDATE_DELAY 2000 /* delay between checks of the X11 selection */ #define SELECTION_UPDATE_DELAY 2000 /* delay between checks of the X11 selection */
/* Selection masks */
#define S_NOSELECTION 0
#define S_PRIMARY 1
#define S_CLIPBOARD 2
typedef BOOL (*EXPORTFUNC)( Display *display, Window win, Atom prop, Atom target, HANDLE handle ); typedef BOOL (*EXPORTFUNC)( Display *display, Window win, Atom prop, Atom target, HANDLE handle );
typedef HANDLE (*IMPORTFUNC)( Atom type, const void *data, size_t size ); typedef HANDLE (*IMPORTFUNC)( Atom type, const void *data, size_t size );
typedef struct clipboard_format struct clipboard_format
{ {
struct list entry; struct list entry;
UINT id; UINT id;
Atom atom; Atom atom;
IMPORTFUNC import; IMPORTFUNC import;
EXPORTFUNC export; EXPORTFUNC export;
} WINE_CLIPFORMAT, *LPWINE_CLIPFORMAT; };
static int selectionAcquired = 0; /* Contains the current selection masks */
static Window selectionWindow = None; /* The top level X window which owns the selection */
static HANDLE import_data( Atom type, const void *data, size_t size ); static HANDLE import_data( Atom type, const void *data, size_t size );
static HANDLE import_enhmetafile( Atom type, const void *data, size_t size ); static HANDLE import_enhmetafile( Atom type, const void *data, size_t size );
@ -202,36 +193,16 @@ static struct list format_list = LIST_INIT( format_list );
#define NB_BUILTIN_FORMATS (sizeof(builtin_formats) / sizeof(builtin_formats[0])) #define NB_BUILTIN_FORMATS (sizeof(builtin_formats) / sizeof(builtin_formats[0]))
#define GET_ATOM(prop) (((prop) < FIRST_XATOM) ? (Atom)(prop) : X11DRV_Atoms[(prop) - FIRST_XATOM]) #define GET_ATOM(prop) (((prop) < FIRST_XATOM) ? (Atom)(prop) : X11DRV_Atoms[(prop) - FIRST_XATOM])
static DWORD clipboard_thread_id;
static HWND clipboard_hwnd;
static BOOL is_clipboard_owner;
static Window selection_window;
static Window import_window;
static Atom current_selection;
static ULONG64 last_clipboard_update;
static struct clipboard_format **current_x11_formats; static struct clipboard_format **current_x11_formats;
static unsigned int nb_current_x11_formats; static unsigned int nb_current_x11_formats;
/**************************************************************************
* Internal Clipboard implementation methods
**************************************************************************/
static Window thread_selection_wnd(void)
{
struct x11drv_thread_data *thread_data = x11drv_init_thread_data();
Window w = thread_data->selection_wnd;
if (!w)
{
w = XCreateWindow(thread_data->display, root_window, 0, 0, 1, 1, 0, CopyFromParent,
InputOnly, CopyFromParent, 0, NULL);
if (w)
{
thread_data->selection_wnd = w;
XSelectInput(thread_data->display, w, PropertyChangeMask);
}
else
FIXME("Failed to create window. Fetching selection data will fail.\n");
}
return w;
}
static const char *debugstr_format( UINT id ) static const char *debugstr_format( UINT id )
{ {
WCHAR buffer[256]; WCHAR buffer[256];
@ -445,15 +416,6 @@ static void register_x11_formats( const Atom *atoms, UINT size )
} }
/**************************************************************************
* X11DRV_InitClipboard
*/
void X11DRV_InitClipboard(void)
{
register_builtin_formats();
}
/************************************************************************** /**************************************************************************
* put_property * put_property
* *
@ -1135,19 +1097,21 @@ void X11DRV_CLIPBOARD_ImportSelection( Display *display, Window win, Atom select
/************************************************************************** /**************************************************************************
* render_format * render_format
*/ */
BOOL render_format( Display *display, Window win, Atom selection, UINT id ) static void render_format( UINT id )
{ {
Display *display = thread_display();
unsigned int i; unsigned int i;
HANDLE handle = 0; HANDLE handle = 0;
if (!current_selection) return;
for (i = 0; i < nb_current_x11_formats; i++) for (i = 0; i < nb_current_x11_formats; i++)
{ {
if (current_x11_formats[i]->id != id) continue; if (current_x11_formats[i]->id != id) continue;
handle = import_selection( display, win, selection, current_x11_formats[i] ); handle = import_selection( display, import_window, current_selection, current_x11_formats[i] );
if (handle) SetClipboardData( id, handle ); if (handle) SetClipboardData( id, handle );
break; break;
} }
return handle != 0;
} }
@ -1630,7 +1594,7 @@ static BOOL export_selection( Display *display, Window win, Atom prop, Atom targ
ret = format->export( display, win, prop, target, 0 ); ret = format->export( display, win, prop, target, 0 );
break; break;
} }
if (!open && !(open = OpenClipboard( 0 ))) if (!open && !(open = OpenClipboard( clipboard_hwnd )))
{ {
ERR( "failed to open clipboard for %s\n", debugstr_xatom( target )); ERR( "failed to open clipboard for %s\n", debugstr_xatom( target ));
return FALSE; return FALSE;
@ -1875,159 +1839,211 @@ static BOOL X11DRV_CLIPBOARD_ReadProperty( Display *display, Window w, Atom prop
/************************************************************************** /**************************************************************************
* X11DRV_CLIPBOARD_ReleaseSelection * acquire_selection
* *
* Release XA_CLIPBOARD and XA_PRIMARY in response to a SelectionClear event. * Acquire the X11 selection when the Win32 clipboard has changed.
*/ */
static void X11DRV_CLIPBOARD_ReleaseSelection(Display *display, Atom selType, Window w, HWND hwnd, Time time) static void acquire_selection( Display *display )
{ {
/* w is the window that lost the selection if (selection_window) XDestroyWindow( display, selection_window );
selection_window = XCreateWindow( display, root_window, 0, 0, 1, 1, 0, CopyFromParent,
InputOutput, CopyFromParent, 0, NULL );
if (!selection_window) return;
XSetSelectionOwner( display, x11drv_atom(CLIPBOARD), selection_window, CurrentTime );
if (use_primary_selection) XSetSelectionOwner( display, XA_PRIMARY, selection_window, CurrentTime );
TRACE( "win %lx\n", selection_window );
}
/**************************************************************************
* release_selection
*
* Release the X11 selection when some other X11 app has grabbed it.
*/ */
TRACE("event->window = %08x (selectionWindow = %08x) selectionAcquired=0x%08x\n", static void release_selection( Display *display, Time time )
(unsigned)w, (unsigned)selectionWindow, (unsigned)selectionAcquired);
if (selectionAcquired && (w == selectionWindow))
{ {
/* completely give up the selection */ assert( selection_window );
TRACE("Lost CLIPBOARD (+PRIMARY) selection\n");
if ((selType == x11drv_atom(CLIPBOARD)) && (selectionAcquired & S_PRIMARY)) TRACE( "win %lx\n", selection_window );
{
TRACE("Lost clipboard. Check if we need to release PRIMARY\n");
if (selectionWindow == XGetSelectionOwner(display, XA_PRIMARY)) /* release PRIMARY if we still own it */
{ if (use_primary_selection && XGetSelectionOwner( display, XA_PRIMARY ) == selection_window)
TRACE("We still own PRIMARY. Releasing PRIMARY.\n");
XSetSelectionOwner( display, XA_PRIMARY, None, time ); XSetSelectionOwner( display, XA_PRIMARY, None, time );
XDestroyWindow( display, selection_window );
selection_window = 0;
} }
else
TRACE("We no longer own PRIMARY\n");
} /**************************************************************************
else if ((selType == XA_PRIMARY) && (selectionAcquired & S_CLIPBOARD)) * request_selection_contents
*
* Retrieve the contents of the X11 selection when it's owned by an X11 app.
*/
static void request_selection_contents( Display *display )
{ {
TRACE("Lost PRIMARY. Check if we need to release CLIPBOARD\n"); struct clipboard_format *targets = find_x11_format( x11drv_atom(TARGETS) );
struct clipboard_format *string = find_x11_format( XA_STRING );
if (selectionWindow == XGetSelectionOwner(display,x11drv_atom(CLIPBOARD))) assert( targets );
assert( string );
current_selection = 0;
if (use_primary_selection && XGetSelectionOwner( display, XA_PRIMARY ))
{ {
TRACE("We still own CLIPBOARD. Releasing CLIPBOARD.\n"); if (import_selection( display, import_window, XA_PRIMARY, targets ))
XSetSelectionOwner(display, x11drv_atom(CLIPBOARD), None, time); current_selection = XA_PRIMARY;
}
else else
TRACE("We no longer own CLIPBOARD\n"); import_selection( display, import_window, XA_PRIMARY, string );
} }
else if (XGetSelectionOwner( display, x11drv_atom(CLIPBOARD) ))
selectionWindow = None; {
if (import_selection( display, import_window, x11drv_atom(CLIPBOARD), targets ))
/* Reset the selection flags now that we are done */ current_selection = x11drv_atom(CLIPBOARD);
selectionAcquired = S_NOSELECTION; else
import_selection( display, import_window, x11drv_atom(CLIPBOARD), string );
} }
} }
/************************************************************************** /**************************************************************************
* X11DRV Clipboard Exports * grab_win32_clipboard
**************************************************************************/ *
* Grab the Win32 clipboard when an X11 app has grabbed the X11 selection,
* and fill it with the selection contents.
static void selection_acquire(void) */
static BOOL grab_win32_clipboard( Display *display )
{ {
Window owner; if (!OpenClipboard( clipboard_hwnd )) return FALSE;
Display *display; EmptyClipboard();
is_clipboard_owner = TRUE;
owner = thread_selection_wnd(); last_clipboard_update = GetTickCount64();
display = thread_display(); request_selection_contents( display );
CloseClipboard();
selectionAcquired = 0; return TRUE;
selectionWindow = 0;
/* Grab PRIMARY selection if not owned */
if (use_primary_selection)
XSetSelectionOwner(display, XA_PRIMARY, owner, CurrentTime);
/* Grab CLIPBOARD selection if not owned */
XSetSelectionOwner(display, x11drv_atom(CLIPBOARD), owner, CurrentTime);
if (use_primary_selection && XGetSelectionOwner(display, XA_PRIMARY) == owner)
selectionAcquired |= S_PRIMARY;
if (XGetSelectionOwner(display,x11drv_atom(CLIPBOARD)) == owner)
selectionAcquired |= S_CLIPBOARD;
if (selectionAcquired)
{
selectionWindow = owner;
TRACE("Grabbed X selection, owner=(%08x)\n", (unsigned) owner);
}
} }
static DWORD WINAPI selection_thread_proc(LPVOID p)
/**************************************************************************
* update_clipboard
*
* Periodically update the clipboard while the selection is owned by an X11 app.
*/
BOOL update_clipboard( HWND hwnd )
{ {
HANDLE event = p; if (hwnd != clipboard_hwnd) return TRUE;
if (!is_clipboard_owner) return TRUE;
TRACE("\n"); if (GetTickCount64() - last_clipboard_update <= SELECTION_UPDATE_DELAY) return TRUE;
return grab_win32_clipboard( thread_display() );
selection_acquire();
SetEvent(event);
while (selectionAcquired)
{
MsgWaitForMultipleObjectsEx(0, NULL, INFINITE, QS_SENDMESSAGE, 0);
} }
/**************************************************************************
* clipboard_wndproc
*
* Window procedure for the clipboard manager.
*/
static LRESULT CALLBACK clipboard_wndproc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
{
switch (msg)
{
case WM_NCCREATE:
return TRUE;
case WM_CLIPBOARDUPDATE:
if (is_clipboard_owner) break; /* ignore our own changes */
acquire_selection( thread_init_display() );
break;
case WM_RENDERFORMAT:
render_format( wp );
break;
case WM_DESTROYCLIPBOARD:
TRACE( "WM_DESTROYCLIPBOARD: lost ownership\n" );
is_clipboard_owner = FALSE;
break;
}
return DefWindowProcW( hwnd, msg, wp, lp );
}
/**************************************************************************
* wait_clipboard_mutex
*
* Make sure that there's only one clipboard thread per window station.
*/
static BOOL wait_clipboard_mutex(void)
{
static const WCHAR prefix[] = {'_','_','w','i','n','e','_','c','l','i','p','b','o','a','r','d','_'};
WCHAR buffer[MAX_PATH + sizeof(prefix) / sizeof(WCHAR)];
HANDLE mutex;
memcpy( buffer, prefix, sizeof(prefix) );
if (!GetUserObjectInformationW( GetProcessWindowStation(), UOI_NAME,
buffer + sizeof(prefix) / sizeof(WCHAR),
sizeof(buffer) - sizeof(prefix), NULL ))
{
ERR( "failed to get winstation name\n" );
return FALSE;
}
mutex = CreateMutexW( NULL, TRUE, buffer );
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
TRACE( "waiting for mutex %s\n", debugstr_w( buffer ));
WaitForSingleObject( mutex, INFINITE );
}
return TRUE;
}
/**************************************************************************
* clipboard_thread
*
* Thread running inside the desktop process to manage the clipboard
*/
static DWORD WINAPI clipboard_thread( void *arg )
{
static const WCHAR clipboard_classname[] = {'_','_','w','i','n','e','_','c','l','i','p','b','o','a','r','d','_','m','a','n','a','g','e','r',0};
XSetWindowAttributes attr;
WNDCLASSW class;
MSG msg;
Display *display = thread_init_display();
if (!wait_clipboard_mutex()) return 0;
attr.event_mask = PropertyChangeMask;
import_window = XCreateWindow( display, root_window, 0, 0, 1, 1, 0, CopyFromParent,
InputOutput, CopyFromParent, CWEventMask, &attr );
if (!import_window)
{
ERR( "failed to create import window\n" );
return 0; return 0;
} }
/************************************************************************** memset( &class, 0, sizeof(class) );
* X11DRV_AcquireClipboard class.lpfnWndProc = clipboard_wndproc;
*/ class.lpszClassName = clipboard_classname;
void X11DRV_AcquireClipboard(HWND hWndClipWindow)
{
DWORD procid;
HANDLE selectionThread;
TRACE(" %p\n", hWndClipWindow); if (!RegisterClassW( &class ) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS)
/*
* 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 (hWndClipWindow &&
GetCurrentThreadId() != GetWindowThreadProcessId(hWndClipWindow, &procid))
{ {
if (procid != GetCurrentProcessId()) ERR( "could not register clipboard window class err %u\n", GetLastError() );
{ return 0;
WARN("Setting clipboard owner to other process is not supported\n");
hWndClipWindow = NULL;
} }
else if (!(clipboard_hwnd = CreateWindowW( clipboard_classname, NULL, 0, 0, 0, 0, 0,
HWND_MESSAGE, 0, 0, NULL )))
{ {
TRACE("Thread %x is acquiring selection with thread %x's window %p\n", ERR( "failed to create clipboard window err %u\n", GetLastError() );
GetCurrentThreadId(), return 0;
GetWindowThreadProcessId(hWndClipWindow, NULL), hWndClipWindow);
SendMessageW(hWndClipWindow, WM_X11DRV_ACQUIRE_SELECTION, 0, 0);
return;
}
} }
if (hWndClipWindow) clipboard_thread_id = GetCurrentThreadId();
{ AddClipboardFormatListener( clipboard_hwnd );
selection_acquire(); register_builtin_formats();
} grab_win32_clipboard( display );
else
{
HANDLE event = CreateEventW(NULL, FALSE, FALSE, NULL);
selectionThread = CreateThread(NULL, 0, selection_thread_proc, event, 0, NULL);
if (selectionThread) TRACE( "clipboard thread %04x running\n", GetCurrentThreadId() );
{ while (GetMessageW( &msg, 0, 0, 0 )) DispatchMessageW( &msg );
WaitForSingleObject(event, INFINITE); return 0;
CloseHandle(selectionThread);
}
CloseHandle(event);
}
} }
@ -2038,51 +2054,17 @@ void CDECL X11DRV_UpdateClipboard(void)
{ {
static ULONG last_update; static ULONG last_update;
ULONG now; ULONG now;
DWORD_PTR ret;
if (selectionAcquired) return; if (GetCurrentThreadId() == clipboard_thread_id) return;
now = GetTickCount(); now = GetTickCount();
if ((int)(now - last_update) <= SELECTION_UPDATE_DELAY) return; if ((int)(now - last_update) <= SELECTION_UPDATE_DELAY) return;
if (SendMessageTimeoutW( GetClipboardOwner(), WM_X11DRV_UPDATE_CLIPBOARD, 0, 0,
SMTO_ABORTIFHUNG, 5000, &ret ) && ret)
last_update = now; last_update = now;
} }
/**************************************************************************
* ResetSelectionOwner
*
* 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(void)
{
HWND hwnd;
DWORD procid;
TRACE("\n");
if (!selectionAcquired || thread_selection_wnd() != selectionWindow)
return;
selectionAcquired = S_NOSELECTION;
selectionWindow = 0;
hwnd = GetWindow(GetDesktopWindow(), GW_CHILD);
do
{
if (GetCurrentThreadId() != GetWindowThreadProcessId(hwnd, &procid))
{
if (GetCurrentProcessId() == procid)
{
if (SendMessageW(hwnd, WM_X11DRV_ACQUIRE_SELECTION, 0, 0))
return;
}
}
} while ((hwnd = GetWindow(hwnd, GW_HWNDNEXT)) != NULL);
WARN("Failed to find another thread to take selection ownership. Clipboard data will be lost.\n");
}
/*********************************************************************** /***********************************************************************
* X11DRV_HandleSelectionRequest * X11DRV_HandleSelectionRequest
*/ */
@ -2095,12 +2077,13 @@ BOOL X11DRV_SelectionRequest( HWND hwnd, XEvent *xev )
X11DRV_expect_error( display, is_window_error, NULL ); X11DRV_expect_error( display, is_window_error, NULL );
/* TRACE( "got request on %lx for selection %s target %s win %lx prop %s\n",
* We can only handle the selection request if : event->owner, debugstr_xatom( event->selection ), debugstr_xatom( event->target ),
* The selection is PRIMARY or CLIPBOARD, AND we can successfully open the clipboard. event->requestor, debugstr_xatom( event->property ));
*/
if (((event->selection != XA_PRIMARY) && (event->selection != x11drv_atom(CLIPBOARD)))) if (event->owner != selection_window) goto done;
goto done; if ((event->selection != x11drv_atom(CLIPBOARD)) &&
(!use_primary_selection || event->selection != XA_PRIMARY)) goto done;
/* If the specified property is None the requestor is an obsolete client. /* If the specified property is None the requestor is an obsolete client.
* We support these by using the specified target atom as the reply property. * We support these by using the specified target atom as the reply property.
@ -2131,11 +2114,27 @@ done:
/*********************************************************************** /***********************************************************************
* X11DRV_SelectionClear * X11DRV_SelectionClear
*/ */
BOOL X11DRV_SelectionClear( HWND hWnd, XEvent *xev ) BOOL X11DRV_SelectionClear( HWND hwnd, XEvent *xev )
{ {
XSelectionClearEvent *event = &xev->xselectionclear; XSelectionClearEvent *event = &xev->xselectionclear;
if (event->selection == XA_PRIMARY || event->selection == x11drv_atom(CLIPBOARD))
X11DRV_CLIPBOARD_ReleaseSelection( event->display, event->selection, if (event->window != selection_window) return FALSE;
event->window, hWnd, event->time ); if (event->selection != x11drv_atom(CLIPBOARD)) return FALSE;
release_selection( event->display, event->time );
grab_win32_clipboard( event->display );
return FALSE; return FALSE;
} }
/**************************************************************************
* X11DRV_InitClipboard
*/
void X11DRV_InitClipboard(void)
{
DWORD id;
HANDLE handle = CreateThread( NULL, 0, clipboard_thread, NULL, 0, &id );
if (handle) CloseHandle( handle );
else ERR( "failed to create clipboard thread\n" );
}

View File

@ -1794,6 +1794,7 @@ BOOL CDECL X11DRV_CreateWindow( HWND hwnd )
CWOverrideRedirect | CWEventMask, &attr ); CWOverrideRedirect | CWEventMask, &attr );
XFlush( data->display ); XFlush( data->display );
SetPropA( hwnd, clip_window_prop, (HANDLE)data->clip_window ); SetPropA( hwnd, clip_window_prop, (HANDLE)data->clip_window );
X11DRV_InitClipboard();
} }
return TRUE; return TRUE;
} }
@ -2660,9 +2661,8 @@ LRESULT CDECL X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
switch(msg) switch(msg)
{ {
case WM_X11DRV_ACQUIRE_SELECTION: case WM_X11DRV_UPDATE_CLIPBOARD:
X11DRV_AcquireClipboard( hwnd ); return update_clipboard( hwnd );
return 0;
case WM_X11DRV_SET_WIN_REGION: case WM_X11DRV_SET_WIN_REGION:
if ((data = get_win_data( hwnd ))) if ((data = get_win_data( hwnd )))
{ {

View File

@ -521,7 +521,7 @@ extern DWORD EVENT_x11_time_to_win32_time(Time time) DECLSPEC_HIDDEN;
/* X11 driver private messages, must be in the range 0x80001000..0x80001fff */ /* X11 driver private messages, must be in the range 0x80001000..0x80001fff */
enum x11drv_window_messages enum x11drv_window_messages
{ {
WM_X11DRV_ACQUIRE_SELECTION = 0x80001000, WM_X11DRV_UPDATE_CLIPBOARD = 0x80001000,
WM_X11DRV_SET_WIN_REGION, WM_X11DRV_SET_WIN_REGION,
WM_X11DRV_RESIZE_DESKTOP, WM_X11DRV_RESIZE_DESKTOP,
WM_X11DRV_SET_CURSOR, WM_X11DRV_SET_CURSOR,
@ -589,6 +589,7 @@ extern void set_window_visual( struct x11drv_win_data *data, const XVisualInfo *
extern void change_systray_owner( Display *display, Window systray_window ) DECLSPEC_HIDDEN; extern void change_systray_owner( Display *display, Window systray_window ) DECLSPEC_HIDDEN;
extern void update_systray_balloon_position(void) DECLSPEC_HIDDEN; extern void update_systray_balloon_position(void) DECLSPEC_HIDDEN;
extern HWND create_foreign_window( Display *display, Window window ) DECLSPEC_HIDDEN; extern HWND create_foreign_window( Display *display, Window window ) DECLSPEC_HIDDEN;
extern BOOL update_clipboard( HWND hwnd ) DECLSPEC_HIDDEN;
static inline void mirror_rect( const RECT *window_rect, RECT *rect ) static inline void mirror_rect( const RECT *window_rect, RECT *rect )
{ {
@ -606,8 +607,6 @@ extern XContext win_data_context DECLSPEC_HIDDEN;
extern XContext cursor_context DECLSPEC_HIDDEN; extern XContext cursor_context DECLSPEC_HIDDEN;
extern void X11DRV_InitClipboard(void) DECLSPEC_HIDDEN; extern void X11DRV_InitClipboard(void) DECLSPEC_HIDDEN;
extern void X11DRV_AcquireClipboard(HWND hWndClipWindow) DECLSPEC_HIDDEN;
extern void X11DRV_ResetSelectionOwner(void) DECLSPEC_HIDDEN;
extern void CDECL X11DRV_SetFocus( HWND hwnd ) DECLSPEC_HIDDEN; extern void CDECL X11DRV_SetFocus( HWND hwnd ) DECLSPEC_HIDDEN;
extern void set_window_cursor( Window window, HCURSOR handle ) DECLSPEC_HIDDEN; extern void set_window_cursor( Window window, HCURSOR handle ) DECLSPEC_HIDDEN;
extern void sync_window_cursor( Window window ) DECLSPEC_HIDDEN; extern void sync_window_cursor( Window window ) DECLSPEC_HIDDEN;

View File

@ -588,7 +588,6 @@ static BOOL process_attach(void)
if (use_xkb) use_xkb = XkbUseExtension( gdi_display, NULL, NULL ); if (use_xkb) use_xkb = XkbUseExtension( gdi_display, NULL, NULL );
#endif #endif
X11DRV_InitKeyboard( gdi_display ); X11DRV_InitKeyboard( gdi_display );
X11DRV_InitClipboard();
if (use_xim) use_xim = X11DRV_InitXIM( input_style ); if (use_xim) use_xim = X11DRV_InitXIM( input_style );
return TRUE; return TRUE;
@ -604,7 +603,6 @@ void CDECL X11DRV_ThreadDetach(void)
if (data) if (data)
{ {
X11DRV_ResetSelectionOwner();
if (data->xim) XCloseIM( data->xim ); if (data->xim) XCloseIM( data->xim );
if (data->font_set) XFreeFontSet( data->display, data->font_set ); if (data->font_set) XFreeFontSet( data->display, data->font_set );
XCloseDisplay( data->display ); XCloseDisplay( data->display );