Enabled the persistent clipboard server.

This commit is contained in:
Francis Beaudet 1999-10-24 20:22:24 +00:00 committed by Alexandre Julliard
parent 7bf36ad35d
commit 56ab55d374
7 changed files with 366 additions and 131 deletions

View file

@ -146,7 +146,7 @@ static void OLEClipbrd_Destroy(OLEClipbrd* ptrToDestroy);
static HWND OLEClipbrd_CreateWindow(); static HWND OLEClipbrd_CreateWindow();
static void OLEClipbrd_DestroyWindow(HWND hwnd); static void OLEClipbrd_DestroyWindow(HWND hwnd);
LRESULT CALLBACK OLEClipbrd_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); LRESULT CALLBACK OLEClipbrd_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static HRESULT OLEClipbrd_RenderFormat(LPFORMATETC pFormatetc); static HRESULT OLEClipbrd_RenderFormat( IDataObject *pIDataObject, LPFORMATETC pFormatetc );
static HGLOBAL OLEClipbrd_GlobalDupMem( HGLOBAL hGlobalSrc ); static HGLOBAL OLEClipbrd_GlobalDupMem( HGLOBAL hGlobalSrc );
/* /*
@ -473,6 +473,7 @@ HRESULT WINAPI OleFlushClipboard()
FORMATETC rgelt; FORMATETC rgelt;
HRESULT hr = S_OK; HRESULT hr = S_OK;
BOOL bClipboardOpen = FALSE; BOOL bClipboardOpen = FALSE;
IDataObject* pIDataObjectSrc = NULL;
TRACE("()\n"); TRACE("()\n");
@ -487,6 +488,13 @@ HRESULT WINAPI OleFlushClipboard()
if (!theOleClipboard->pIDataObjectSrc) if (!theOleClipboard->pIDataObjectSrc)
return S_OK; return S_OK;
/*
* Addref and save the source data object we are holding on to temporarily,
* since it will be released when we empty the clipboard.
*/
pIDataObjectSrc = theOleClipboard->pIDataObjectSrc;
IDataObject_AddRef(pIDataObjectSrc);
/* /*
* Open the Windows clipboard * Open the Windows clipboard
*/ */
@ -503,7 +511,7 @@ HRESULT WINAPI OleFlushClipboard()
* Render all HGLOBAL formats supported by the source into * Render all HGLOBAL formats supported by the source into
* the windows clipboard. * the windows clipboard.
*/ */
if ( FAILED( hr = IDataObject_EnumFormatEtc( (IDataObject*)&(theOleClipboard->lpvtbl1), if ( FAILED( hr = IDataObject_EnumFormatEtc( pIDataObjectSrc,
DATADIR_GET, DATADIR_GET,
&penumFormatetc) )) &penumFormatetc) ))
{ {
@ -522,7 +530,7 @@ HRESULT WINAPI OleFlushClipboard()
/* /*
* Render the clipboard data * Render the clipboard data
*/ */
if ( FAILED(OLEClipbrd_RenderFormat( &rgelt )) ) if ( FAILED(OLEClipbrd_RenderFormat( pIDataObjectSrc, &rgelt )) )
continue; continue;
} }
} }
@ -530,13 +538,9 @@ HRESULT WINAPI OleFlushClipboard()
IEnumFORMATETC_Release(penumFormatetc); IEnumFORMATETC_Release(penumFormatetc);
/* /*
* Release the data object we are holding on to * Release the source data object we are holding on to
*/ */
if ( theOleClipboard->pIDataObjectSrc ) IDataObject_Release(pIDataObjectSrc);
{
IDataObject_Release(theOleClipboard->pIDataObjectSrc);
theOleClipboard->pIDataObjectSrc = NULL;
}
CLEANUP: CLEANUP:
@ -795,7 +799,7 @@ LRESULT CALLBACK OLEClipbrd_WndProc
* Render the clipboard data. * Render the clipboard data.
* (We must have a source data object or we wouldn't be in this WndProc) * (We must have a source data object or we wouldn't be in this WndProc)
*/ */
OLEClipbrd_RenderFormat( &rgelt ); OLEClipbrd_RenderFormat( (IDataObject*)&(theOleClipboard->lpvtbl1), &rgelt );
break; break;
} }
@ -835,7 +839,7 @@ LRESULT CALLBACK OLEClipbrd_WndProc
/* /*
* Render the clipboard data. * Render the clipboard data.
*/ */
if ( FAILED(OLEClipbrd_RenderFormat( &rgelt )) ) if ( FAILED(OLEClipbrd_RenderFormat( (IDataObject*)&(theOleClipboard->lpvtbl1), &rgelt )) )
continue; continue;
TRACE("(): WM_RENDERALLFORMATS(cfFormat=%d)\n", rgelt.cfFormat); TRACE("(): WM_RENDERALLFORMATS(cfFormat=%d)\n", rgelt.cfFormat);
@ -891,14 +895,13 @@ LRESULT CALLBACK OLEClipbrd_WndProc
* source data object. * source data object.
* Note: This function assumes it is passed an HGLOBAL format to render. * Note: This function assumes it is passed an HGLOBAL format to render.
*/ */
static HRESULT OLEClipbrd_RenderFormat(LPFORMATETC pFormatetc) static HRESULT OLEClipbrd_RenderFormat(IDataObject *pIDataObject, LPFORMATETC pFormatetc)
{ {
STGMEDIUM medium; STGMEDIUM medium;
HGLOBAL hDup; HGLOBAL hDup;
HRESULT hr = S_OK; HRESULT hr = S_OK;
if ( FAILED(hr = IDataObject_GetData((IDataObject*)&(theOleClipboard->lpvtbl1), if ( FAILED(hr = IDataObject_GetData(pIDataObject, pFormatetc, &medium)) )
pFormatetc, &medium)) )
{ {
WARN("() : IDataObject_GetData failed to render clipboard data! (%lx)\n", hr); WARN("() : IDataObject_GetData failed to render clipboard data! (%lx)\n", hr);
return hr; return hr;

View file

@ -488,7 +488,7 @@ static LPWINE_CLIPFORMAT CLIPBOARD_RenderText( UINT wFormat )
TRACE("\tconverting from '%s' to '%s', %i chars\n", TRACE("\tconverting from '%s' to '%s', %i chars\n",
lpSource->Name, lpTarget->Name, size); lpSource->Name, lpTarget->Name, size);
lpTarget->hData32 = GlobalAlloc(GMEM_ZEROINIT, size); lpTarget->hData32 = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE, size);
lpstrT = (LPSTR)GlobalLock(lpTarget->hData32); lpstrT = (LPSTR)GlobalLock(lpTarget->hData32);
if( lpstrT ) if( lpstrT )
@ -791,7 +791,8 @@ HANDLE16 WINAPI GetClipboardData16( UINT16 wFormat )
} }
/* Convert between 32 -> 16 bit data, if necessary */ /* Convert between 32 -> 16 bit data, if necessary */
if( lpRender->hData32 && !lpRender->hData16 ) if( lpRender->hData32 && !lpRender->hData16
&& CLIPBOARD_IsMemoryObject(wFormat) )
{ {
int size; int size;
if( lpRender->wFormatID == CF_METAFILEPICT ) if( lpRender->wFormatID == CF_METAFILEPICT )
@ -857,7 +858,8 @@ HANDLE WINAPI GetClipboardData( UINT wFormat )
} }
/* Convert between 16 -> 32 bit data, if necessary */ /* Convert between 16 -> 32 bit data, if necessary */
if( lpRender->hData16 && !lpRender->hData32 ) if( lpRender->hData16 && !lpRender->hData32
&& CLIPBOARD_IsMemoryObject(wFormat) )
{ {
int size; int size;
if( lpRender->wFormatID == CF_METAFILEPICT ) if( lpRender->wFormatID == CF_METAFILEPICT )

View file

@ -171,11 +171,6 @@ static void USER_AppExit( HINSTANCE16 hInstance )
* but does nothing); * but does nothing);
*/ */
/* TODO: Start up persistant WINE X clipboard server process which will
* take ownership of the X selection and continue to service selection
* requests from other apps.
*/
/* ModuleUnload() in "Internals" */ /* ModuleUnload() in "Internals" */
hInstance = GetExePtr( hInstance ); hInstance = GetExePtr( hInstance );

View file

@ -1,4 +1,4 @@
DEFS = @DLLFLAGS@ -D__WINE__ DEFS = @DLLFLAGS@ -D__WINE__ -DBINDIR="\"$(bindir)\""
TOPSRCDIR = @top_srcdir@ TOPSRCDIR = @top_srcdir@
TOPOBJDIR = ../.. TOPOBJDIR = ../..
SRCDIR = @srcdir@ SRCDIR = @srcdir@
@ -23,8 +23,6 @@ all: $(MODULE).o $(PROGRAMS)
wineclipsrv: wineclipsrv.c wineclipsrv: wineclipsrv.c
$(CC) $(ALLCFLAGS) -o wineclipsrv $(SRCDIR)/wineclipsrv.c $(X_LIBS) $(XLIB) $(LIBS) $(CC) $(ALLCFLAGS) -o wineclipsrv $(SRCDIR)/wineclipsrv.c $(X_LIBS) $(XLIB) $(LIBS)
all: $(MODULE).o
@MAKE_RULES@ @MAKE_RULES@
### Dependencies: ### Dependencies:

View file

@ -49,13 +49,15 @@
#ifndef X_DISPLAY_MISSING #ifndef X_DISPLAY_MISSING
#include <errno.h>
#include <X11/Xatom.h> #include <X11/Xatom.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include "ts_xlib.h" #include "ts_xlib.h"
#include "wine/winuser16.h" #include "wine/winuser16.h"
#include "clipboard.h" #include "clipboard.h"
#include "debugtools.h"
#include "message.h" #include "message.h"
#include "win.h" #include "win.h"
#include "windef.h" #include "windef.h"
@ -63,6 +65,8 @@
#include "bitmap.h" #include "bitmap.h"
#include "commctrl.h" #include "commctrl.h"
#include "heap.h" #include "heap.h"
#include "options.h"
#include "debugtools.h"
DEFAULT_DEBUG_CHANNEL(clipboard) DEFAULT_DEBUG_CHANNEL(clipboard)
@ -76,13 +80,14 @@ DEFAULT_DEBUG_CHANNEL(clipboard)
static char _CLIPBOARD[] = "CLIPBOARD"; /* CLIPBOARD atom name */ static char _CLIPBOARD[] = "CLIPBOARD"; /* CLIPBOARD atom name */
static char FMT_PREFIX[] = "<WCF>"; /* Prefix for windows specific formats */ static char FMT_PREFIX[] = "<WCF>"; /* Prefix for windows specific formats */
static int selectionAcquired = 0; /* Contains the current selection masks */ static int selectionAcquired = 0; /* Contains the current selection masks */
static Window selectionWindow = None; /* The top level X window which owns the selection */ static Window selectionWindow = None; /* The top level X window which owns the selection */
static Window selectionPrevWindow = None; /* The last X window that owned the selection */ static Window selectionPrevWindow = None; /* The last X window that owned the selection */
static Window PrimarySelectionOwner = None; /* The window which owns the primary selection */ static Window PrimarySelectionOwner = None; /* The window which owns the primary selection */
static Window ClipboardSelectionOwner = None; /* The window which owns the clipboard selection */ static Window ClipboardSelectionOwner = None; /* The window which owns the clipboard selection */
static unsigned long cSelectionTargets = 0; /* Number of target formats reported by TARGETS selection */ static unsigned long cSelectionTargets = 0; /* Number of target formats reported by TARGETS selection */
static Atom selectionCacheSrc = XA_PRIMARY; /* The selection source from which the clipboard cache was filled */ static Atom selectionCacheSrc = XA_PRIMARY; /* The selection source from which the clipboard cache was filled */
static HANDLE selectionClearEvent = NULL; /* Synchronization object used to block until server is started */
/* /*
* Dynamic pointer arrays to manage destruction of Pixmap resources * Dynamic pointer arrays to manage destruction of Pixmap resources
@ -205,6 +210,106 @@ BOOL X11DRV_CLIPBOARD_IsNativeProperty(Atom prop)
} }
/**************************************************************************
* X11DRV_CLIPBOARD_LaunchServer
* Launches the clipboard server. This is called from X11DRV_CLIPBOARD_ResetOwner
* when the selection can no longer be recyled to another top level window.
* In order to make the selection persist after Wine shuts down a server
* process is launched which services subsequent selection requests.
*/
BOOL X11DRV_CLIPBOARD_LaunchServer()
{
int iWndsLocks;
/* If persistant selection has been disabled in the .winerc Clipboard section,
* don't launch the server
*/
if ( !PROFILE_GetWineIniInt("Clipboard", "PersistentSelection", 1) )
return FALSE;
/* Start up persistant WINE X clipboard server process which will
* take ownership of the X selection and continue to service selection
* requests from other apps.
*/
selectionWindow = selectionPrevWindow;
if ( !fork() )
{
/* NOTE: This code only executes in the context of the child process
* Do note make any Wine specific calls here.
*/
int dbgClasses = 0;
char selMask[8], dbgClassMask[8], clearSelection[8];
sprintf(selMask, "%d", selectionAcquired);
/* Build the debug class mask to pass to the server, by inheriting
* the settings for the clipboard debug channel.
*/
dbgClasses |= __GET_DEBUGGING(__DBCL_FIXME, dbch_clipboard) ? 1 : 0;
dbgClasses |= __GET_DEBUGGING(__DBCL_ERR, dbch_clipboard) ? 2 : 0;
dbgClasses |= __GET_DEBUGGING(__DBCL_WARN, dbch_clipboard) ? 4 : 0;
dbgClasses |= __GET_DEBUGGING(__DBCL_TRACE, dbch_clipboard) ? 8 : 0;
sprintf(dbgClassMask, "%d", dbgClasses);
/* Get the clear selection preference */
sprintf(clearSelection, "%d",
PROFILE_GetWineIniInt("Clipboard", "ClearAllSelections", 0));
/* Exec the clipboard server passing it the selection and debug class masks */
execl( BINDIR "/wineclipsvr", "wineclipsvr",
selMask, dbgClassMask, clearSelection, NULL );
execlp( "wineclipsvr", "wineclipsvr", selMask, dbgClassMask, clearSelection, NULL );
execl( "./windows/x11drv/wineclipsvr", "wineclipsvr",
selMask, dbgClassMask, clearSelection, NULL );
/* Exec Failed! */
perror("Could not start Wine clipboard server");
exit( 1 ); /* Exit the child process */
}
/* Wait until the clipboard server acquires the selection.
* We must release the windows lock to enable Wine to process
* selection messages in response to the servers requests.
*/
iWndsLocks = WIN_SuspendWndsLock();
/* We must wait until the server finishes acquiring the selection,
* before proceeding, otherwise the window which owns the selection
* will be destroyed prematurely!
* Create a non-signalled, auto-reset event which will be set by
* X11DRV_CLIPBOARD_ReleaseSelection, and wait until this gets
* signalled before proceeding.
*/
if ( !(selectionClearEvent = CreateEventA(NULL, FALSE, FALSE, NULL)) )
ERR("Could not create wait object. Clipboard server won't start!\n");
else
{
/* Make the event object's handle global */
selectionClearEvent = ConvertToGlobalHandle(selectionClearEvent);
/* Wait until we lose the selection, timing out after a minute */
TRACE("Waiting for clipboard server to acquire selection\n");
if ( WaitForSingleObject( selectionClearEvent, 60000 ) != WAIT_OBJECT_0 )
TRACE("Server could not acquire selection, or a time out occured!\n");
else
TRACE("Server successfully acquired selection\n");
/* Release the event */
CloseHandle(selectionClearEvent);
selectionClearEvent = NULL;
}
WIN_RestoreWndsLock(iWndsLocks);
return TRUE;
}
/************************************************************************** /**************************************************************************
* X11DRV_CLIPBOARD_CacheDataFormats * X11DRV_CLIPBOARD_CacheDataFormats
* *
@ -567,14 +672,14 @@ END:
* Release an XA_PRIMARY or XA_CLIPBOARD selection that we own, in response * Release an XA_PRIMARY or XA_CLIPBOARD selection that we own, in response
* to a SelectionClear event. * to a SelectionClear event.
* This can occur in response to another client grabbing the X selection. * This can occur in response to another client grabbing the X selection.
* If the XA_CLIPBOARD selection is lost we relinquish XA_PRIMARY as well. * If the XA_CLIPBOARD selection is lost, we relinquish XA_PRIMARY as well.
*/ */
void X11DRV_CLIPBOARD_ReleaseSelection(Atom selType, Window w, HWND hwnd) void X11DRV_CLIPBOARD_ReleaseSelection(Atom selType, Window w, HWND hwnd)
{ {
Atom xaClipboard = TSXInternAtom(display, "CLIPBOARD", False); Atom xaClipboard = TSXInternAtom(display, "CLIPBOARD", False);
int clearAllSelections = PROFILE_GetWineIniInt("Clipboard", "ClearAllSelections", 0);
/* w is the window that lost selection, /* w is the window that lost the selection
*
* selectionPrevWindow is nonzero if CheckSelection() was called. * selectionPrevWindow is nonzero if CheckSelection() was called.
*/ */
@ -585,12 +690,15 @@ void X11DRV_CLIPBOARD_ReleaseSelection(Atom selType, Window w, HWND hwnd)
{ {
if( w == selectionWindow || selectionPrevWindow == None) if( w == selectionWindow || selectionPrevWindow == None)
{ {
/* alright, we really lost it */ /* If we're losing the CLIPBOARD selection, or if the preferences in .winerc
* dictate that *all* selections should be cleared on loss of a selection,
if ( selType == xaClipboard ) /* completely give up the selection */ * we must give up all the selections we own.
*/
if ( clearAllSelections || (selType == xaClipboard) )
{ {
TRACE("Lost CLIPBOARD selection\n"); /* completely give up the selection */
TRACE("Lost CLIPBOARD (+PRIMARY) selection\n");
/* We are completely giving up the selection. /* We are completely giving up the selection.
* Make sure we can open the windows clipboard first. */ * Make sure we can open the windows clipboard first. */
@ -607,24 +715,23 @@ void X11DRV_CLIPBOARD_ReleaseSelection(Atom selType, Window w, HWND hwnd)
return; return;
} }
selectionPrevWindow = selectionWindow; /* We really lost CLIPBOARD but want to voluntarily lose PRIMARY */
if ( (selType == xaClipboard)
&& (selectionAcquired & S_PRIMARY) )
{
XSetSelectionOwner(display, XA_PRIMARY, None, CurrentTime);
}
/* We really lost PRIMARY but want to voluntarily lose CLIPBOARD */
if ( (selType == XA_PRIMARY)
&& (selectionAcquired & S_CLIPBOARD) )
{
XSetSelectionOwner(display, xaClipboard, None, CurrentTime);
}
selectionWindow = None; selectionWindow = None;
PrimarySelectionOwner = ClipboardSelectionOwner = 0; PrimarySelectionOwner = ClipboardSelectionOwner = 0;
/* Voluntarily give up the PRIMARY selection if we still own it */
if ( selectionAcquired & S_PRIMARY )
{
XEvent xe;
TRACE("Releasing XA_PRIMARY selection\n");
TSXSetSelectionOwner(display, XA_PRIMARY, None, CurrentTime);
/* Wait until SelectionClear is processed */
if( selectionPrevWindow )
while( !XCheckTypedWindowEvent( display, selectionPrevWindow,
SelectionClear, &xe ) );
}
/* Empty the windows clipboard. /* Empty the windows clipboard.
* We should pretend that we still own the selection BEFORE calling * We should pretend that we still own the selection BEFORE calling
* EmptyClipboard() since otherwise this has the side effect of * EmptyClipboard() since otherwise this has the side effect of
@ -633,12 +740,13 @@ void X11DRV_CLIPBOARD_ReleaseSelection(Atom selType, Window w, HWND hwnd)
*/ */
selectionAcquired = (S_PRIMARY | S_CLIPBOARD); selectionAcquired = (S_PRIMARY | S_CLIPBOARD);
EmptyClipboard(); EmptyClipboard();
selectionAcquired = S_NOSELECTION;
CloseClipboard(); CloseClipboard();
/* Give up ownership of the windows clipboard */ /* Give up ownership of the windows clipboard */
CLIPBOARD_ReleaseOwner(); CLIPBOARD_ReleaseOwner();
/* Reset the selection flags now that we are done */
selectionAcquired = S_NOSELECTION;
} }
else if ( selType == XA_PRIMARY ) /* Give up only PRIMARY selection */ else if ( selType == XA_PRIMARY ) /* Give up only PRIMARY selection */
{ {
@ -664,6 +772,13 @@ void X11DRV_CLIPBOARD_ReleaseSelection(Atom selType, Window w, HWND hwnd)
} }
} }
/* Signal to a selectionClearEvent listener if the selection is completely lost */
if (selectionClearEvent && !selectionAcquired)
{
TRACE("Lost all selections, signalling to selectionClearEvent listener\n");
SetEvent(selectionClearEvent);
}
selectionPrevWindow = None; selectionPrevWindow = None;
} }
@ -996,21 +1111,6 @@ void X11DRV_CLIPBOARD_ResetOwner(WND *pWnd, BOOL bFooBar)
TRACE("clipboard owner = %04x, selection window = %08x\n", TRACE("clipboard owner = %04x, selection window = %08x\n",
hWndClipOwner, (unsigned)selectionWindow); hWndClipOwner, (unsigned)selectionWindow);
#if(0)
/* Check if all formats are already in the clipboard cache */
if( !CLIPBOARD_IsCacheRendered() )
{
SendMessage16(hWndClipOwner,WM_RENDERALLFORMATS,0,0L);
/* check if all formats were rendered */
if ( !CLIPBOARD_IsCacheRendered() )
{
ERR("\tCould not render all formats\n");
CLIPBOARD_ReleaseOwner();
}
}
#endif
/* now try to salvage current selection from being destroyed by X */ /* now try to salvage current selection from being destroyed by X */
TRACE("\tchecking %08x\n", (unsigned) XWnd); TRACE("\tchecking %08x\n", (unsigned) XWnd);
@ -1041,7 +1141,10 @@ void X11DRV_CLIPBOARD_ResetOwner(WND *pWnd, BOOL bFooBar)
TSXSetSelectionOwner(display, XA_PRIMARY, selectionWindow, CurrentTime); TSXSetSelectionOwner(display, XA_PRIMARY, selectionWindow, CurrentTime);
TSXSetSelectionOwner(display, xaClipboard, selectionWindow, CurrentTime); TSXSetSelectionOwner(display, xaClipboard, selectionWindow, CurrentTime);
/* Restore the selection masks */
selectionAcquired = saveSelectionState;
/* Lose the selection if something went wrong */ /* Lose the selection if something went wrong */
if ( ( (saveSelectionState & S_PRIMARY) && if ( ( (saveSelectionState & S_PRIMARY) &&
(TSXGetSelectionOwner(display, XA_PRIMARY) != selectionWindow) ) (TSXGetSelectionOwner(display, XA_PRIMARY) != selectionWindow) )
@ -1053,7 +1156,6 @@ void X11DRV_CLIPBOARD_ResetOwner(WND *pWnd, BOOL bFooBar)
else else
{ {
/* Update selection state */ /* Update selection state */
selectionAcquired = saveSelectionState;
if (saveSelectionState & S_PRIMARY) if (saveSelectionState & S_PRIMARY)
PrimarySelectionOwner = selectionWindow; PrimarySelectionOwner = selectionWindow;
@ -1069,26 +1171,33 @@ void X11DRV_CLIPBOARD_ResetOwner(WND *pWnd, BOOL bFooBar)
END: END:
if (bLostSelection) if (bLostSelection)
{ {
/* Empty the windows clipboard. /* Launch the clipboard server if the selection can no longer be recyled
* We should pretend that we still own the selection BEFORE calling * to another top level window. */
* EmptyClipboard() since otherwise this has the side effect of
* triggering X11DRV_CLIPBOARD_Acquire() and causing the X selection if ( !X11DRV_CLIPBOARD_LaunchServer() )
* to be re-acquired by us! {
*/ /* Empty the windows clipboard if the server was not launched.
* We should pretend that we still own the selection BEFORE calling
* EmptyClipboard() since otherwise this has the side effect of
* triggering X11DRV_CLIPBOARD_Acquire() and causing the X selection
* to be re-acquired by us!
*/
TRACE("\tLost the selection! Emptying the clipboard...\n");
OpenClipboard(NULL);
selectionAcquired = (S_PRIMARY | S_CLIPBOARD);
EmptyClipboard();
CloseClipboard();
/* Give up ownership of the windows clipboard */
CLIPBOARD_ReleaseOwner();
}
TRACE("\tLost the selection! Emptying the clipboard...\n"); selectionAcquired = S_NOSELECTION;
ClipboardSelectionOwner = PrimarySelectionOwner = 0;
OpenClipboard(NULL); selectionWindow = 0;
selectionAcquired = (S_PRIMARY | S_CLIPBOARD);
EmptyClipboard();
selectionAcquired = S_NOSELECTION;
CloseClipboard();
/* Give up ownership of the windows clipboard */
CLIPBOARD_ReleaseOwner();
ClipboardSelectionOwner = PrimarySelectionOwner = 0;
selectionWindow = 0;
} }
} }

View file

@ -3,50 +3,123 @@
* *
* Copyright 1999 Noel Borthwick * Copyright 1999 Noel Borthwick
* *
* USAGE:
* wineclipsrv [selection_mask] [debugClass_mask] [clearAllSelections]
*
* The optional selection-mask argument is a bit mask of the selection
* types to be acquired. Currently two selections are supported:
* 1. PRIMARY (mask value 1)
* 2. CLIPBOARD (mask value 2).
*
* debugClass_mask is a bit mask of all debugging classes for which messages
* are to be output. The standard Wine debug class set FIXME(1), ERR(2),
* WARN(4) and TRACE(8) are supported.
*
* If clearAllSelections == 1 *all* selections are lost whenever a SelectionClear
* event is received.
*
* If no arguments are supplied the server aquires all selections. (mask value 3)
* and defaults to output of only FIXME(1) and ERR(2) messages. The default for
* clearAllSelections is 0.
*
* NOTES: * NOTES:
* This file contains the implementation for the Clipboard server *
* The Wine Clipboard Server is a standalone XLib application whose
* purpose is to manage the X selection when Wine exits.
* The server itself is started automatically with the appropriate
* selection masks, whenever Wine exits after acquiring the PRIMARY and/or
* CLIPBOARD selection. (See X11DRV_CLIPBOARD_ResetOwner)
* When the server starts, it first proceeds to capture the selection data from
* Wine and then takes over the selection ownership. It does this by querying
* the current selection owner(of the specified selections) for the TARGETS
* selection target. It then proceeds to cache all the formats exposed by
* TARGETS. If the selection does not support the TARGETS target, or if no
* target formats are exposed, the server simply exits.
* Once the cache has been filled, the server then actually acquires ownership
* of the respective selection and begins fielding selection requests.
* Selection requests are serviced from the cache. If a selection is lost the
* server flushes its internal cache, destroying all data previously saved.
* Once ALL selections have been lost the server terminates.
* *
* TODO: * TODO:
*
*/ */
#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/Xutil.h> #include <X11/Xutil.h>
#include <X11/Xos.h> #include <X11/Xos.h>
#include <X11/Xatom.h> #include <X11/Xatom.h>
#include <stdio.h>
/* Lightweight debug definitions */ /*
* Lightweight debug definitions for Wine Clipboard Server.
* The standard FIXME, ERR, WARN & TRACE classes are supported
* without debug channels.
* The standard defines NO_TRACE_MSGS and NO_DEBUG_MSGS will compile out
* TRACE, WARN and ERR and FIXME message displays.
*/
/* Internal definitions (do not use these directly) */
enum __DEBUG_CLASS { __DBCL_FIXME, __DBCL_ERR, __DBCL_WARN, __DBCL_TRACE, __DBCL_COUNT };
extern char __debug_msg_enabled[__DBCL_COUNT];
extern const char * const debug_cl_name[__DBCL_COUNT];
#define DEBUG_CLASS_COUNT __DBCL_COUNT
#define __GET_DEBUGGING(dbcl) (__debug_msg_enabled[(dbcl)])
#define __SET_DEBUGGING(dbcl,on) (__debug_msg_enabled[(dbcl)] = (on))
#define __DPRINTF(dbcl) \
(!__GET_DEBUGGING(dbcl) || \
(printf("%s:%s:%s ", debug_cl_name[(dbcl)], progname, __FUNCTION__),0)) \
? 0 : printf
#define __DPRINTF(dbname) (printf("%s:%s:%s ", dbname, progname, __FUNCTION__),0) ? 0 : printf
#define __DUMMY_DPRINTF 1 ? (void)0 : (void)((int (*)(char *, ...)) NULL) #define __DUMMY_DPRINTF 1 ? (void)0 : (void)((int (*)(char *, ...)) NULL)
/* use configure to allow user to compile out debugging messages */
#ifndef NO_TRACE_MSGS #ifndef NO_TRACE_MSGS
#define TRACE __DPRINTF("TRACE") #define TRACE __DPRINTF(__DBCL_TRACE)
#else #else
#define TRACE __DUMMY_DPRINTF #define TRACE __DUMMY_DPRINTF
#endif /* NO_TRACE_MSGS */ #endif /* NO_TRACE_MSGS */
#ifndef NO_DEBUG_MSGS #ifndef NO_DEBUG_MSGS
#define WARN __DPRINTF("WARN") #define WARN __DPRINTF(__DBCL_WARN)
#define FIXME __DPRINTF("FIXME") #define FIXME __DPRINTF(__DBCL_FIXME)
#else #else
#define WARN __DUMMY_DPRINTF #define WARN __DUMMY_DPRINTF
#define FIXME __DUMMY_DPRINTF #define FIXME __DUMMY_DPRINTF
#endif /* NO_DEBUG_MSGS */ #endif /* NO_DEBUG_MSGS */
#define ERR __DPRINTF("ERROR") /* define error macro regardless of what is configured */
#define ERR __DPRINTF(__DBCL_ERR)
#define TRUE 1 #define TRUE 1
#define FALSE 0 #define FALSE 0
typedef int BOOL; typedef int BOOL;
/* Internal definitions for debugging messages(do not use these directly) */
const char * const debug_cl_name[] = { "fixme", "err", "warn", "trace" };
char __debug_msg_enabled[DEBUG_CLASS_COUNT] = {1, 1, 0, 0};
/* Selection masks */ /* Selection masks */
#define S_NOSELECTION 0 #define S_NOSELECTION 0
#define S_PRIMARY 1 #define S_PRIMARY 1
#define S_CLIPBOARD 2 #define S_CLIPBOARD 2
/* Debugging class masks */
#define C_FIXME 1
#define C_ERR 2
#define C_WARN 4
#define C_TRACE 8
/* /*
* Global variables * Global variables
@ -62,10 +135,10 @@ static char *g_szOutOfMemory = "Insufficient memory!\n";
/* X selection context info */ /* X selection context info */
static char _CLIPBOARD[] = "CLIPBOARD"; /* CLIPBOARD atom name */ static char _CLIPBOARD[] = "CLIPBOARD"; /* CLIPBOARD atom name */
static char FMT_PREFIX[] = "<WCF>"; /* Prefix for windows specific formats */
static int g_selectionToAcquire = 0; /* Masks for the selection to be acquired */ static int g_selectionToAcquire = 0; /* Masks for the selection to be acquired */
static int g_selectionAcquired = 0; /* Contains the current selection masks */ static int g_selectionAcquired = 0; /* Contains the current selection masks */
static int g_clearAllSelections = 0; /* If TRUE *all* selections are lost on SelectionClear */
/* Selection cache */ /* Selection cache */
typedef struct tag_CACHEENTRY typedef struct tag_CACHEENTRY
{ {
@ -119,7 +192,6 @@ void getGC(Window win, GC *gc);
void main(int argc, char **argv) void main(int argc, char **argv)
{ {
XEvent event; XEvent event;
unsigned int width, height; /* window size */
if ( !Init(argc, argv) ) if ( !Init(argc, argv) )
exit(0); exit(0);
@ -130,6 +202,8 @@ void main(int argc, char **argv)
*/ */
if ( AcquireSelection() == S_NOSELECTION ) if ( AcquireSelection() == S_NOSELECTION )
TerminateServer(0); TerminateServer(0);
TRACE("Clipboard server running...\n");
/* Start an X event loop */ /* Start an X event loop */
while (1) while (1)
@ -243,8 +317,23 @@ BOOL Init(int argc, char **argv)
g_selectionToAcquire = atoi(argv[1]); g_selectionToAcquire = atoi(argv[1]);
else else
g_selectionToAcquire = S_PRIMARY | S_CLIPBOARD; g_selectionToAcquire = S_PRIMARY | S_CLIPBOARD;
/* Set the debugging class state from the command line argument */
if (argc > 2)
{
int dbgClasses = atoi(argv[2]);
TRACE("Clipboard server running...\n"); __SET_DEBUGGING(__DBCL_FIXME, dbgClasses & C_FIXME);
__SET_DEBUGGING(__DBCL_ERR, dbgClasses & C_ERR);
__SET_DEBUGGING(__DBCL_WARN, dbgClasses & C_WARN);
__SET_DEBUGGING(__DBCL_TRACE, dbgClasses & C_TRACE);
}
/* Set the "ClearSelections" state from the command line argument */
if (argc > 3)
g_clearAllSelections = atoi(argv[3]);
return TRUE;
} }
@ -287,27 +376,39 @@ int AcquireSelection()
{ {
TRACE("Acquiring PRIMARY selection...\n"); TRACE("Acquiring PRIMARY selection...\n");
g_cPrimaryTargets = CacheDataFormats( XA_PRIMARY, &g_pPrimaryCache ); g_cPrimaryTargets = CacheDataFormats( XA_PRIMARY, &g_pPrimaryCache );
if (g_cPrimaryTargets) TRACE("Cached %ld formats...\n", g_cPrimaryTargets);
XSetSelectionOwner(g_display, XA_PRIMARY, g_win, CurrentTime);
else
TRACE("No PRIMARY targets - ownership not acquired.\n");
} }
if (g_selectionToAcquire & S_CLIPBOARD) if (g_selectionToAcquire & S_CLIPBOARD)
{ {
TRACE("Acquiring CLIPBOARD selection...\n"); TRACE("Acquiring CLIPBOARD selection...\n");
g_cClipboardTargets = CacheDataFormats( xaClipboard, &g_pClipboardCache ); g_cClipboardTargets = CacheDataFormats( xaClipboard, &g_pClipboardCache );
TRACE("Cached %ld formats...\n", g_cClipboardTargets);
if (g_cClipboardTargets)
XSetSelectionOwner(g_display, xaClipboard, g_win, CurrentTime);
else
TRACE("No CLIPBOARD targets - ownership not acquired.\n");
} }
/* Remember the acquired selections */ /*
if( XGetSelectionOwner(g_display,XA_PRIMARY) == g_win ) * Now that we have cached the data, we proceed to acquire the selections
*/
if (g_cPrimaryTargets)
{
/* Acquire the PRIMARY selection */
while (XGetSelectionOwner(g_display,XA_PRIMARY) != g_win)
XSetSelectionOwner(g_display, XA_PRIMARY, g_win, CurrentTime);
g_selectionAcquired |= S_PRIMARY; g_selectionAcquired |= S_PRIMARY;
if( XGetSelectionOwner(g_display,xaClipboard) == g_win ) }
else
TRACE("No PRIMARY targets - ownership not acquired.\n");
if (g_cClipboardTargets)
{
/* Acquire the CLIPBOARD selection */
while (XGetSelectionOwner(g_display,xaClipboard) != g_win)
XSetSelectionOwner(g_display, xaClipboard, g_win, CurrentTime);
g_selectionAcquired |= S_CLIPBOARD; g_selectionAcquired |= S_CLIPBOARD;
}
else
TRACE("No CLIPBOARD targets - ownership not acquired.\n");
return g_selectionAcquired; return g_selectionAcquired;
} }
@ -398,7 +499,7 @@ int CacheDataFormats( Atom SelectionSrc, PCACHEENTRY *ppCache )
/* Populate the cache entry */ /* Populate the cache entry */
if (!FillCacheEntry( SelectionSrc, targetList[i], &((*ppCache)[i]))) if (!FillCacheEntry( SelectionSrc, targetList[i], &((*ppCache)[i])))
ERR("Failed to fill cache entry!"); ERR("Failed to fill cache entry!\n");
XFree(itemFmtName); XFree(itemFmtName);
} }
@ -453,8 +554,11 @@ BOOL FillCacheEntry( Atom SelectionSrc, Atom target, PCACHEENTRY pCacheEntry )
reqType = xe.xselection.target; reqType = xe.xselection.target;
if(prop == None) if(prop == None)
{
TRACE("\tOwner failed to convert selection!\n");
return bRet; return bRet;
}
TRACE("\tretrieving property %s from window %ld into %s\n", TRACE("\tretrieving property %s from window %ld into %s\n",
XGetAtomName(g_display,reqType), (long)w, XGetAtomName(g_display,prop) ); XGetAtomName(g_display,reqType), (long)w, XGetAtomName(g_display,prop) );
@ -600,7 +704,7 @@ void EmptyCache(PCACHEENTRY pCache, int nItems)
} }
else else
{ {
TRACE("Freeing %s (0x%x)...\n", TRACE("Freeing %s (%p)...\n",
XGetAtomName(g_display, pCache[i].target), pCache[i].pData); XGetAtomName(g_display, pCache[i].target), pCache[i].pData);
/* Free the cached data item (allocated by X) */ /* Free the cached data item (allocated by X) */
@ -621,8 +725,10 @@ void EmptyCache(PCACHEENTRY pCache, int nItems)
*/ */
void EVENT_ProcessEvent( XEvent *event ) void EVENT_ProcessEvent( XEvent *event )
{ {
// TRACE(" event %s for Window %08lx\n", event_names[event->type], event->xany.window ); /*
TRACE(" event %s for Window %08lx\n", event_names[event->type], event->xany.window );
*/
switch (event->type) switch (event->type)
{ {
case Expose: case Expose:
@ -652,7 +758,7 @@ void EVENT_ProcessEvent( XEvent *event )
break; break;
case PropertyNotify: case PropertyNotify:
EVENT_PropertyNotify( (XPropertyEvent *)event ); // EVENT_PropertyNotify( (XPropertyEvent *)event );
break; break;
default: /* ignore all other events */ default: /* ignore all other events */
@ -771,7 +877,6 @@ void EVENT_SelectionRequest( XSelectionRequestEvent *event, BOOL bIsMultiple )
XSelectionEvent result; XSelectionEvent result;
Atom rprop = None; Atom rprop = None;
Window request = event->requestor; Window request = event->requestor;
BOOL couldOpen = FALSE;
Atom xaMultiple = XInternAtom(g_display, "MULTIPLE", False); Atom xaMultiple = XInternAtom(g_display, "MULTIPLE", False);
PCACHEENTRY pCacheEntry = NULL; PCACHEENTRY pCacheEntry = NULL;
void *pData = NULL; void *pData = NULL;
@ -803,7 +908,7 @@ void EVENT_SelectionRequest( XSelectionRequestEvent *event, BOOL bIsMultiple )
} }
/* Update the X property */ /* Update the X property */
TRACE("\tUpdating property %s...", XGetAtomName(g_display, rprop)); TRACE("\tUpdating property %s...\n", XGetAtomName(g_display, rprop));
/* If we have a request for a pixmap, return a duplicate */ /* If we have a request for a pixmap, return a duplicate */
@ -853,17 +958,36 @@ void EVENT_SelectionClear( XSelectionClearEvent *event )
TRACE("()\n"); TRACE("()\n");
if (event->selection == XA_PRIMARY) /* If we're losing the CLIPBOARD selection, or if the preferences in .winerc
* dictate that *all* selections should be cleared on loss of a selection,
* we must give up all the selections we own.
*/
if ( g_clearAllSelections || (event->selection == xaClipboard) )
{
TRACE("Lost CLIPBOARD (+PRIMARY) selection\n");
/* We really lost CLIPBOARD but want to voluntarily lose PRIMARY */
if ( (event->selection == xaClipboard)
&& (g_selectionAcquired & S_PRIMARY) )
{
XSetSelectionOwner(g_display, XA_PRIMARY, None, CurrentTime);
}
/* We really lost PRIMARY but want to voluntarily lose CLIPBOARD */
if ( (event->selection == XA_PRIMARY)
&& (g_selectionAcquired & S_CLIPBOARD) )
{
XSetSelectionOwner(g_display, xaClipboard, None, CurrentTime);
}
g_selectionAcquired = S_NOSELECTION; /* Clear the selection masks */
}
else if (event->selection == XA_PRIMARY)
{ {
g_selectionAcquired &= ~S_PRIMARY; /* Clear the PRIMARY flag */
TRACE("Lost PRIMARY selection...\n"); TRACE("Lost PRIMARY selection...\n");
g_selectionAcquired &= ~S_PRIMARY; /* Clear the PRIMARY flag */
} }
else if (event->selection == xaClipboard)
{
g_selectionAcquired &= ~S_CLIPBOARD; /* Clear the CLIPBOARD flag */
TRACE("Lost CLIPBOARD selection...\n");
}
/* Once we lose all our selections we have nothing more to do */ /* Once we lose all our selections we have nothing more to do */
if (g_selectionAcquired == S_NOSELECTION) if (g_selectionAcquired == S_NOSELECTION)
TerminateServer(1); TerminateServer(1);
@ -915,7 +1039,7 @@ Pixmap DuplicatePixmap(Pixmap pixmap)
unsigned border_width; /* Unused */ unsigned border_width; /* Unused */
unsigned int depth, width, height; unsigned int depth, width, height;
TRACE("\t() Pixmap=%ul\n", pixmap); TRACE("\t() Pixmap=%ld\n", (long)pixmap);
/* Get the Pixmap dimensions and bit depth */ /* Get the Pixmap dimensions and bit depth */
if ( 0 == XGetGeometry(g_display, pixmap, &root, &x, &y, &width, &height, if ( 0 == XGetGeometry(g_display, pixmap, &root, &x, &y, &width, &height,
@ -933,7 +1057,7 @@ Pixmap DuplicatePixmap(Pixmap pixmap)
XDestroyImage(xi); XDestroyImage(xi);
TRACE("\t() New Pixmap=%ul\n", newPixmap); TRACE("\t() New Pixmap=%ld\n", (long)newPixmap);
return newPixmap; return newPixmap;
} }

View file

@ -158,5 +158,9 @@ Startup=
;InitialColumns=80 ;InitialColumns=80
;TerminalType=nxterm ;TerminalType=nxterm
[Clipboard]
ClearAllSelections=0
PersistentSelection=1
# </wineconf> # </wineconf>