Added clipboard server.
diff --git a/Makefile.in b/Makefile.in
index 9ca6f59..3ddf0e8 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -258,6 +258,8 @@
$(INSTALL_DATA) $(LIB_TARGET) $(libdir); \
if [ $(LIB_TARGET) = libwine.so.1.0 ]; then $(LDCONFIG); fi \
fi
+ [ -d $(bindir) ] || $(MKDIR) $(bindir)
+ $(INSTALL_PROGRAM) windows/x11drv/wineclipsrv $(bindir)/wineclipsrv
uninstall_lib: dummy
cd $(libdir); $(RM) $(LIB_TARGET) libwine.a libwine.so wine.sym
diff --git a/windows/x11drv/.cvsignore b/windows/x11drv/.cvsignore
index f3c7a7c..4f940bf 100644
--- a/windows/x11drv/.cvsignore
+++ b/windows/x11drv/.cvsignore
@@ -1 +1,2 @@
Makefile
+wineclipsrv
diff --git a/windows/x11drv/Makefile.in b/windows/x11drv/Makefile.in
index e037ca3..afc90d3 100644
--- a/windows/x11drv/Makefile.in
+++ b/windows/x11drv/Makefile.in
@@ -16,6 +16,13 @@
mouse.c \
wnd.c
+PROGRAMS = wineclipsrv
+
+all: $(MODULE).o $(PROGRAMS)
+
+wineclipsrv: wineclipsrv.c
+ $(CC) $(ALLCFLAGS) -o wineclipsrv $(SRCDIR)/wineclipsrv.c $(X_LIBS) $(XLIB) $(LIBS)
+
all: $(MODULE).o
@MAKE_RULES@
diff --git a/windows/x11drv/wineclipsrv.c b/windows/x11drv/wineclipsrv.c
new file mode 100644
index 0000000..63276f7
--- /dev/null
+++ b/windows/x11drv/wineclipsrv.c
@@ -0,0 +1,985 @@
+/*
+ * Wine Clipboard Server
+ *
+ * Copyright 1999 Noel Borthwick
+ *
+ * NOTES:
+ * This file contains the implementation for the Clipboard server
+ *
+ * TODO:
+ *
+ */
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xos.h>
+#include <X11/Xatom.h>
+#include <stdio.h>
+
+/* Lightweight debug definitions */
+
+#define __DPRINTF(dbname) (printf("%s:%s:%s ", dbname, progname, __FUNCTION__),0) ? 0 : printf
+#define __DUMMY_DPRINTF 1 ? (void)0 : (void)((int (*)(char *, ...)) NULL)
+
+#ifndef NO_TRACE_MSGS
+ #define TRACE __DPRINTF("TRACE")
+#else
+ #define TRACE __DUMMY_DPRINTF
+#endif /* NO_TRACE_MSGS */
+
+#ifndef NO_DEBUG_MSGS
+ #define WARN __DPRINTF("WARN")
+ #define FIXME __DPRINTF("FIXME")
+#else
+ #define WARN __DUMMY_DPRINTF
+ #define FIXME __DUMMY_DPRINTF
+#endif /* NO_DEBUG_MSGS */
+
+#define ERR __DPRINTF("ERROR")
+
+
+#define TRUE 1
+#define FALSE 0
+typedef int BOOL;
+
+/* Selection masks */
+
+#define S_NOSELECTION 0
+#define S_PRIMARY 1
+#define S_CLIPBOARD 2
+
+
+/*
+ * Global variables
+ */
+
+static Display *g_display = NULL;
+static int screen_num;
+static char *progname; /* name this program was invoked by */
+static Window g_win = 0; /* the hidden clipboard server window */
+static GC g_gc = 0;
+
+static char *g_szOutOfMemory = "Insufficient memory!\n";
+
+/* X selection context info */
+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_selectionAcquired = 0; /* Contains the current selection masks */
+
+/* Selection cache */
+typedef struct tag_CACHEENTRY
+{
+ Atom target;
+ Atom type;
+ int nFormat;
+ int nElements;
+ void *pData;
+} CACHEENTRY, *PCACHEENTRY;
+
+static PCACHEENTRY g_pPrimaryCache = NULL; /* Primary selection cache */
+static PCACHEENTRY g_pClipboardCache = NULL; /* Clipboard selection cache */
+static unsigned long g_cPrimaryTargets = 0; /* Number of TARGETS reported by PRIMARY selection */
+static unsigned long g_cClipboardTargets = 0; /* Number of TARGETS reported by CLIPBOARD selection */
+
+/* Event names */
+static const char * const event_names[] =
+{
+ "", "", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
+ "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
+ "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
+ "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
+ "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
+ "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
+ "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
+ "ClientMessage", "MappingNotify"
+};
+
+
+/*
+ * Prototypes
+ */
+
+BOOL Init(int argc, char **argv);
+void TerminateServer( int ret );
+int AcquireSelection();
+int CacheDataFormats( Atom SelectionSrc, PCACHEENTRY *ppCache );
+void EmptyCache(PCACHEENTRY pCache, int nItems);
+BOOL FillCacheEntry( Atom SelectionSrc, Atom target, PCACHEENTRY pCacheEntry );
+BOOL LookupCacheItem( Atom selection, Atom target, PCACHEENTRY *ppCacheEntry );
+void EVENT_ProcessEvent( XEvent *event );
+Atom EVENT_SelectionRequest_MULTIPLE( XSelectionRequestEvent *pevent );
+void EVENT_SelectionRequest( XSelectionRequestEvent *event, BOOL bIsMultiple );
+void EVENT_SelectionClear( XSelectionClearEvent *event );
+void EVENT_PropertyNotify( XPropertyEvent *event );
+Pixmap DuplicatePixmap(Pixmap pixmap);
+void TextOut(Window win, GC gc, char *pStr);
+void getGC(Window win, GC *gc);
+
+
+void main(int argc, char **argv)
+{
+ XEvent event;
+ unsigned int width, height; /* window size */
+
+ if ( !Init(argc, argv) )
+ exit(0);
+
+ /* Acquire the selection after retrieving all clipboard data
+ * owned by the current selection owner. If we were unable to
+ * Acquire any selection, terminate right away.
+ */
+ if ( AcquireSelection() == S_NOSELECTION )
+ TerminateServer(0);
+
+ /* Start an X event loop */
+ while (1)
+ {
+ XNextEvent(g_display, &event);
+
+ EVENT_ProcessEvent( &event );
+ }
+}
+
+
+/**************************************************************************
+ * Init()
+ * Initialize the clipboard server
+ */
+BOOL Init(int argc, char **argv)
+{
+ unsigned int width, height; /* window size */
+ unsigned int border_width = 4; /* four pixels */
+ unsigned int display_width, display_height;
+ char *window_name = "Wine Clipboard Server";
+ XSizeHints *size_hints = NULL;
+ XWMHints *wm_hints = NULL;
+ XClassHint *class_hints = NULL;
+ XTextProperty windowName;
+ char *display_name = NULL;
+
+ progname = argv[0];
+
+ if (!(size_hints = XAllocSizeHints()))
+ {
+ ERR(g_szOutOfMemory);
+ return 0;
+ }
+ if (!(wm_hints = XAllocWMHints()))
+ {
+ ERR(g_szOutOfMemory);
+ return 0;
+ }
+ if (!(class_hints = XAllocClassHint()))
+ {
+ ERR(g_szOutOfMemory);
+ return 0;
+ }
+
+ /* connect to X server */
+ if ( (g_display=XOpenDisplay(display_name)) == NULL )
+ {
+ ERR( "cannot connect to X server %s\n", XDisplayName(display_name));
+ return 0;
+ }
+
+ /* get screen size from display structure macro */
+ screen_num = DefaultScreen(g_display);
+ display_width = DisplayWidth(g_display, screen_num);
+ display_height = DisplayHeight(g_display, screen_num);
+
+ /* size window with enough room for text */
+ width = display_width/3, height = display_height/4;
+
+ /* create opaque window */
+ g_win = XCreateSimpleWindow(g_display, RootWindow(g_display,screen_num),
+ 0, 0, width, height, border_width, BlackPixel(g_display,
+ screen_num), WhitePixel(g_display,screen_num));
+
+
+ /* Set size hints for window manager. The window manager may
+ * override these settings. */
+
+ /* x, y, width, and height hints are now taken from
+ * the actual settings of the window when mapped. Note
+ * that PPosition and PSize must be specified anyway. */
+
+ size_hints->flags = PPosition | PSize | PMinSize;
+ size_hints->min_width = 300;
+ size_hints->min_height = 200;
+
+ /* These calls store window_name into XTextProperty structures
+ * and sets the other fields properly. */
+ if (XStringListToTextProperty(&window_name, 1, &windowName) == 0)
+ {
+ ERR( "structure allocation for windowName failed.\n");
+ TerminateServer(-1);
+ }
+
+ wm_hints->initial_state = NormalState;
+ wm_hints->input = True;
+ wm_hints->flags = StateHint | InputHint;
+
+ class_hints->res_name = progname;
+ class_hints->res_class = "WineClipSrv";
+
+ XSetWMProperties(g_display, g_win, &windowName, NULL,
+ argv, argc, size_hints, wm_hints,
+ class_hints);
+
+ /* Select event types wanted */
+ XSelectInput(g_display, g_win, ExposureMask | KeyPressMask |
+ ButtonPressMask | StructureNotifyMask | PropertyChangeMask );
+
+ /* create GC for text and drawing */
+ getGC(g_win, &g_gc);
+
+ /* Display window */
+ /* XMapWindow(g_display, g_win); */
+
+ /* Set the selections to be acquired from the command line argument.
+ * If none specified, default to all selections we understand.
+ */
+ if (argc > 1)
+ g_selectionToAcquire = atoi(argv[1]);
+ else
+ g_selectionToAcquire = S_PRIMARY | S_CLIPBOARD;
+
+ TRACE("Clipboard server running...\n");
+}
+
+
+/**************************************************************************
+ * TerminateServer()
+ */
+void TerminateServer( int ret )
+{
+ TRACE("Terminating Wine clipboard server...\n");
+
+ /* Free Primary and Clipboard selection caches */
+ EmptyCache(g_pPrimaryCache, g_cPrimaryTargets);
+ EmptyCache(g_pClipboardCache, g_cClipboardTargets);
+
+ if (g_gc)
+ XFreeGC(g_display, g_gc);
+
+ if (g_display)
+ XCloseDisplay(g_display);
+
+ exit(ret);
+}
+
+
+/**************************************************************************
+ * AcquireSelection()
+ *
+ * Acquire the selection after retrieving all clipboard data owned by
+ * the current selection owner.
+ */
+int AcquireSelection()
+{
+ Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
+
+ /*
+ * For all selections we need to acquire, get a list of all targets
+ * supplied by the current selection owner.
+ */
+ if (g_selectionToAcquire & S_PRIMARY)
+ {
+ TRACE("Acquiring PRIMARY selection...\n");
+ g_cPrimaryTargets = CacheDataFormats( XA_PRIMARY, &g_pPrimaryCache );
+ if (g_cPrimaryTargets)
+ XSetSelectionOwner(g_display, XA_PRIMARY, g_win, CurrentTime);
+ else
+ TRACE("No PRIMARY targets - ownership not acquired.\n");
+ }
+ if (g_selectionToAcquire & S_CLIPBOARD)
+ {
+ TRACE("Acquiring CLIPBOARD selection...\n");
+ g_cClipboardTargets = CacheDataFormats( xaClipboard, &g_pClipboardCache );
+
+ 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 )
+ g_selectionAcquired |= S_PRIMARY;
+ if( XGetSelectionOwner(g_display,xaClipboard) == g_win )
+ g_selectionAcquired |= S_CLIPBOARD;
+
+ return g_selectionAcquired;
+}
+
+/**************************************************************************
+ * CacheDataFormats
+ *
+ * Allocates and caches the list of data formats available from the current selection.
+ * This queries the selection owner for the TARGETS property and saves all
+ * reported property types.
+ */
+int CacheDataFormats( Atom SelectionSrc, PCACHEENTRY *ppCache )
+{
+ XEvent xe;
+ Atom aTargets;
+ Atom atype=AnyPropertyType;
+ int aformat;
+ unsigned long remain;
+ unsigned long cSelectionTargets = 0;
+ Atom* targetList=NULL;
+ Window ownerSelection = 0;
+
+ if (!ppCache)
+ return 0;
+ *ppCache = NULL;
+
+ /* Get the selection owner */
+ ownerSelection = XGetSelectionOwner(g_display, SelectionSrc);
+ if ( ownerSelection == None )
+ return cSelectionTargets;
+
+ /*
+ * Query the selection owner for the TARGETS property
+ */
+ aTargets = XInternAtom(g_display, "TARGETS", False);
+
+ TRACE("Requesting TARGETS selection for '%s' (owner=%08x)...\n",
+ XGetAtomName(g_display, SelectionSrc), (unsigned)ownerSelection );
+
+ XConvertSelection(g_display, SelectionSrc, aTargets,
+ XInternAtom(g_display, "SELECTION_DATA", False),
+ g_win, CurrentTime);
+
+ /*
+ * Wait until SelectionNotify is received
+ */
+ while( TRUE )
+ {
+ if( XCheckTypedWindowEvent(g_display, g_win, SelectionNotify, &xe) )
+ if( xe.xselection.selection == SelectionSrc )
+ break;
+ }
+
+ /* Verify that the selection returned a valid TARGETS property */
+ if ( (xe.xselection.target != aTargets)
+ || (xe.xselection.property == None) )
+ {
+ TRACE("\tCould not retrieve TARGETS\n");
+ return cSelectionTargets;
+ }
+
+ /* Read the TARGETS property contents */
+ if(XGetWindowProperty(g_display, xe.xselection.requestor, xe.xselection.property,
+ 0, 0x3FFF, True, AnyPropertyType/*XA_ATOM*/, &atype, &aformat,
+ &cSelectionTargets, &remain, (unsigned char**)&targetList) != Success)
+ TRACE("\tCouldn't read TARGETS property\n");
+ else
+ {
+ TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
+ XGetAtomName(g_display,atype),aformat,cSelectionTargets, remain);
+ /*
+ * The TARGETS property should have returned us a list of atoms
+ * corresponding to each selection target format supported.
+ */
+ if( (atype == XA_ATOM || atype == aTargets) && aformat == 32 )
+ {
+ int i;
+
+ /* Allocate the selection cache */
+ *ppCache = (PCACHEENTRY)calloc(cSelectionTargets, sizeof(CACHEENTRY));
+
+ /* Cache these formats in the selection cache */
+ for (i = 0; i < cSelectionTargets; i++)
+ {
+ char *itemFmtName = XGetAtomName(g_display, targetList[i]);
+
+ TRACE("\tAtom# %d: '%s'\n", i, itemFmtName);
+
+ /* Populate the cache entry */
+ if (!FillCacheEntry( SelectionSrc, targetList[i], &((*ppCache)[i])))
+ ERR("Failed to fill cache entry!");
+
+ XFree(itemFmtName);
+ }
+ }
+
+ /* Free the list of targets */
+ XFree(targetList);
+ }
+
+ return cSelectionTargets;
+}
+
+/***********************************************************************
+ * FillCacheEntry
+ *
+ * Populates the specified cache entry
+ */
+BOOL FillCacheEntry( Atom SelectionSrc, Atom target, PCACHEENTRY pCacheEntry )
+{
+ XEvent xe;
+ Window w;
+ Atom prop, reqType;
+ Atom atype=AnyPropertyType;
+ int aformat;
+ unsigned long nitems,remain,itemSize;
+ long lRequestLength;
+ unsigned char* val=NULL;
+ BOOL bRet = FALSE;
+
+ TRACE("Requesting %s selection from %s...\n",
+ XGetAtomName(g_display, target),
+ XGetAtomName(g_display, SelectionSrc) );
+
+ /* Ask the selection owner to convert the selection to the target format */
+ XConvertSelection(g_display, SelectionSrc, target,
+ XInternAtom(g_display, "SELECTION_DATA", False),
+ g_win, CurrentTime);
+
+ /* wait until SelectionNotify is received */
+ while( TRUE )
+ {
+ if( XCheckTypedWindowEvent(g_display, g_win, SelectionNotify, &xe) )
+ if( xe.xselection.selection == SelectionSrc )
+ break;
+ }
+
+ /* Now proceed to retrieve the actual converted property from
+ * the SELECTION_DATA atom */
+
+ w = xe.xselection.requestor;
+ prop = xe.xselection.property;
+ reqType = xe.xselection.target;
+
+ if(prop == None)
+ return bRet;
+
+ TRACE("\tretrieving property %s from window %ld into %s\n",
+ XGetAtomName(g_display,reqType), (long)w, XGetAtomName(g_display,prop) );
+
+ /*
+ * First request a zero length in order to figure out the request size.
+ */
+ if(XGetWindowProperty(g_display,w,prop,0,0,False, AnyPropertyType/*reqType*/,
+ &atype, &aformat, &nitems, &itemSize, &val) != Success)
+ {
+ WARN("\tcouldn't get property size\n");
+ return bRet;
+ }
+
+ /* Free zero length return data if any */
+ if ( val )
+ {
+ XFree(val);
+ val = NULL;
+ }
+
+ TRACE("\tretrieving %ld bytes...\n", itemSize * aformat/8);
+ lRequestLength = (itemSize * aformat/8)/4 + 1;
+
+ /*
+ * Retrieve the actual property in the required X format.
+ */
+ if(XGetWindowProperty(g_display,w,prop,0,lRequestLength,False,AnyPropertyType/*reqType*/,
+ &atype, &aformat, &nitems, &remain, &val) != Success)
+ {
+ WARN("\tcouldn't read property\n");
+ return bRet;
+ }
+
+ TRACE("\tType %s,Format %d,nitems %ld,remain %ld,value %s\n",
+ atype ? XGetAtomName(g_display,atype) : NULL, aformat,nitems,remain,val);
+
+ if (remain)
+ {
+ WARN("\tCouldn't read entire property- selection may be too large! Remain=%ld\n", remain);
+ goto END;
+ }
+
+ /*
+ * Populate the cache entry
+ */
+ pCacheEntry->target = target;
+ pCacheEntry->type = atype;
+ pCacheEntry->nFormat = aformat;
+ pCacheEntry->nElements = nitems;
+
+ if (atype == XA_PIXMAP)
+ {
+ Pixmap *pPixmap = (Pixmap *)val;
+ Pixmap newPixmap = DuplicatePixmap( *pPixmap );
+ pPixmap = (Pixmap*)calloc(1, sizeof(Pixmap));
+ *pPixmap = newPixmap;
+ pCacheEntry->pData = pPixmap;
+ }
+ else
+ pCacheEntry->pData = val;
+
+END:
+ /* Delete the property on the window now that we are done
+ * This will send a PropertyNotify event to the selection owner. */
+ XDeleteProperty(g_display,w,prop);
+
+ return TRUE;
+}
+
+
+/***********************************************************************
+ * LookupCacheItem
+ *
+ * Lookup a target atom in the cache and get the matching cache entry
+ */
+BOOL LookupCacheItem( Atom selection, Atom target, PCACHEENTRY *ppCacheEntry )
+{
+ int i;
+ int nCachetargets = 0;
+ PCACHEENTRY pCache = NULL;
+ Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
+
+ /* Locate the cache to be used based on the selection type */
+ if ( selection == XA_PRIMARY )
+ {
+ pCache = g_pPrimaryCache;
+ nCachetargets = g_cPrimaryTargets;
+ }
+ else if ( selection == xaClipboard )
+ {
+ pCache = g_pClipboardCache;
+ nCachetargets = g_cClipboardTargets;
+ }
+
+ if (!pCache || !ppCacheEntry)
+ return FALSE;
+
+ *ppCacheEntry = NULL;
+
+ /* Look for the target item in the cache */
+ for (i = 0; i < nCachetargets; i++)
+ {
+ if (pCache[i].target == target)
+ {
+ *ppCacheEntry = &pCache[i];
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/***********************************************************************
+ * EmptyCache
+ *
+ * Empties the specified cache
+ */
+void EmptyCache(PCACHEENTRY pCache, int nItems)
+{
+ int i;
+
+ if (!pCache)
+ return;
+
+ /* Release all items in the cache */
+ for (i = 0; i < nItems; i++)
+ {
+ if (pCache[i].target && pCache[i].pData)
+ {
+ /* If we have a Pixmap, free it first */
+ if (pCache[i].target == XA_PIXMAP || pCache[i].target == XA_BITMAP)
+ {
+ Pixmap *pPixmap = (Pixmap *)pCache[i].pData;
+
+ TRACE("Freeing %s (handle=%ld)...\n",
+ XGetAtomName(g_display, pCache[i].target), *pPixmap);
+
+ XFreePixmap(g_display, *pPixmap);
+
+ /* Free the cached data item (allocated by us) */
+ free(pCache[i].pData);
+ }
+ else
+ {
+ TRACE("Freeing %s (0x%x)...\n",
+ XGetAtomName(g_display, pCache[i].target), pCache[i].pData);
+
+ /* Free the cached data item (allocated by X) */
+ XFree(pCache[i].pData);
+ }
+ }
+ }
+
+ /* Destroy the cache */
+ free(pCache);
+}
+
+
+/***********************************************************************
+ * EVENT_ProcessEvent
+ *
+ * Process an X event.
+ */
+void EVENT_ProcessEvent( XEvent *event )
+{
+// TRACE(" event %s for Window %08lx\n", event_names[event->type], event->xany.window );
+
+ switch (event->type)
+ {
+ case Expose:
+ /* don't draw the window */
+ if (event->xexpose.count != 0)
+ break;
+
+ /* Output something */
+ TextOut(g_win, g_gc, "Click here to terminate");
+ break;
+
+ case ConfigureNotify:
+ break;
+
+ case ButtonPress:
+ /* fall into KeyPress (no break) */
+ case KeyPress:
+ TerminateServer(1);
+ break;
+
+ case SelectionRequest:
+ EVENT_SelectionRequest( (XSelectionRequestEvent *)event, FALSE );
+ break;
+
+ case SelectionClear:
+ EVENT_SelectionClear( (XSelectionClearEvent*)event );
+ break;
+
+ case PropertyNotify:
+ EVENT_PropertyNotify( (XPropertyEvent *)event );
+ break;
+
+ default: /* ignore all other events */
+ break;
+
+ } /* end switch */
+
+}
+
+
+/***********************************************************************
+ * EVENT_SelectionRequest_MULTIPLE
+ * Service a MULTIPLE selection request event
+ * rprop contains a list of (target,property) atom pairs.
+ * The first atom names a target and the second names a property.
+ * The effect is as if we have received a sequence of SelectionRequest events
+ * (one for each atom pair) except that:
+ * 1. We reply with a SelectionNotify only when all the requested conversions
+ * have been performed.
+ * 2. If we fail to convert the target named by an atom in the MULTIPLE property,
+ * we replace the atom in the property by None.
+ */
+Atom EVENT_SelectionRequest_MULTIPLE( XSelectionRequestEvent *pevent )
+{
+ Atom rprop;
+ Atom atype=AnyPropertyType;
+ int aformat;
+ unsigned long remain;
+ Atom* targetPropList=NULL;
+ unsigned long cTargetPropList = 0;
+/* Atom xAtomPair = XInternAtom(g_display, "ATOM_PAIR", False); */
+
+ /* 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.
+ */
+ rprop = pevent->property;
+ if( rprop == None )
+ rprop = pevent->target;
+ if (!rprop)
+ goto END;
+
+ /* Read the MULTIPLE property contents. This should contain a list of
+ * (target,property) atom pairs.
+ */
+ if(XGetWindowProperty(g_display, pevent->requestor, rprop,
+ 0, 0x3FFF, False, AnyPropertyType, &atype, &aformat,
+ &cTargetPropList, &remain, (unsigned char**)&targetPropList) != Success)
+ TRACE("\tCouldn't read MULTIPLE property\n");
+ else
+ {
+ TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
+ XGetAtomName(g_display,atype),aformat,cTargetPropList,remain);
+
+ /*
+ * Make sure we got what we expect.
+ * NOTE: According to the X-ICCCM Version 2.0 documentation the property sent
+ * in a MULTIPLE selection request should be of type ATOM_PAIR.
+ * However some X apps(such as XPaint) are not compliant with this and return
+ * a user defined atom in atype when XGetWindowProperty is called.
+ * The data *is* an atom pair but is not denoted as such.
+ */
+ if(aformat == 32 /* atype == xAtomPair */ )
+ {
+ int i;
+
+ /* Iterate through the ATOM_PAIR list and execute a SelectionRequest
+ * for each (target,property) pair */
+
+ for (i = 0; i < cTargetPropList; i+=2)
+ {
+ char *targetName = XGetAtomName(g_display, targetPropList[i]);
+ char *propName = XGetAtomName(g_display, targetPropList[i+1]);
+ XSelectionRequestEvent event;
+
+ TRACE("MULTIPLE(%d): Target='%s' Prop='%s'\n", i/2, targetName, propName);
+ XFree(targetName);
+ XFree(propName);
+
+ /* We must have a non "None" property to service a MULTIPLE target atom */
+ if ( !targetPropList[i+1] )
+ {
+ TRACE("\tMULTIPLE(%d): Skipping target with empty property!", i);
+ continue;
+ }
+
+ /* Set up an XSelectionRequestEvent for this (target,property) pair */
+ memcpy( &event, pevent, sizeof(XSelectionRequestEvent) );
+ event.target = targetPropList[i];
+ event.property = targetPropList[i+1];
+
+ /* Fire a SelectionRequest, informing the handler that we are processing
+ * a MULTIPLE selection request event.
+ */
+ EVENT_SelectionRequest( &event, TRUE );
+ }
+ }
+
+ /* Free the list of targets/properties */
+ XFree(targetPropList);
+ }
+
+END:
+ return rprop;
+}
+
+
+/***********************************************************************
+ * EVENT_SelectionRequest
+ * Process an event selection request event.
+ * The bIsMultiple flag is used to signal when EVENT_SelectionRequest is called
+ * recursively while servicing a "MULTIPLE" selection target.
+ *
+ */
+void EVENT_SelectionRequest( XSelectionRequestEvent *event, BOOL bIsMultiple )
+{
+ XSelectionEvent result;
+ Atom rprop = None;
+ Window request = event->requestor;
+ BOOL couldOpen = FALSE;
+ Atom xaMultiple = XInternAtom(g_display, "MULTIPLE", False);
+ PCACHEENTRY pCacheEntry = NULL;
+ void *pData = NULL;
+ Pixmap pixmap;
+
+ /* 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.
+ */
+ rprop = event->property;
+ if( rprop == None )
+ rprop = event->target;
+
+ TRACE("Request for %s in selection %s\n",
+ XGetAtomName(g_display, event->target), XGetAtomName(g_display, event->selection));
+
+ /* Handle MULTIPLE requests - rprop contains a list of (target, property) atom pairs */
+ if(event->target == xaMultiple)
+ {
+ /* MULTIPLE selection request - will call us back recursively */
+ rprop = EVENT_SelectionRequest_MULTIPLE( event );
+ goto END;
+ }
+
+ /* Lookup the requested target property in the cache */
+ if ( !LookupCacheItem(event->selection, event->target, &pCacheEntry) )
+ {
+ TRACE("Item not available in cache!\n");
+ goto END;
+ }
+
+ /* Update the X property */
+ TRACE("\tUpdating property %s...", XGetAtomName(g_display, rprop));
+
+ /* If we have a request for a pixmap, return a duplicate */
+
+ if(event->target == XA_PIXMAP || event->target == XA_BITMAP)
+ {
+ Pixmap *pPixmap = (Pixmap *)pCacheEntry->pData;
+ pixmap = DuplicatePixmap( *pPixmap );
+ pData = &pixmap;
+ }
+ else
+ pData = pCacheEntry->pData;
+
+ XChangeProperty(g_display, request, rprop,
+ pCacheEntry->type, pCacheEntry->nFormat, PropModeReplace,
+ (unsigned char *)pData, pCacheEntry->nElements);
+
+END:
+ if( rprop == None)
+ TRACE("\tRequest ignored\n");
+
+ /* reply to sender
+ * SelectionNotify should be sent only at the end of a MULTIPLE request
+ */
+ if ( !bIsMultiple )
+ {
+ result.type = SelectionNotify;
+ result.display = g_display;
+ result.requestor = request;
+ result.selection = event->selection;
+ result.property = rprop;
+ result.target = event->target;
+ result.time = event->time;
+ TRACE("Sending SelectionNotify event...\n");
+ XSendEvent(g_display,event->requestor,False,NoEventMask,(XEvent*)&result);
+ }
+}
+
+
+/***********************************************************************
+ * EVENT_SelectionClear
+ * We receive this event when another client grabs the X selection.
+ * If we lost both PRIMARY and CLIPBOARD we must terminate.
+ */
+void EVENT_SelectionClear( XSelectionClearEvent *event )
+{
+ Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
+
+ TRACE("()\n");
+
+ if (event->selection == XA_PRIMARY)
+ {
+ g_selectionAcquired &= ~S_PRIMARY; /* Clear the PRIMARY flag */
+ TRACE("Lost PRIMARY selection...\n");
+ }
+ 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 */
+ if (g_selectionAcquired == S_NOSELECTION)
+ TerminateServer(1);
+}
+
+/***********************************************************************
+ * EVENT_PropertyNotify
+ * We use this to release resources like Pixmaps when a selection
+ * client no longer needs them.
+ */
+void EVENT_PropertyNotify( XPropertyEvent *event )
+{
+ TRACE("()\n");
+
+ /* Check if we have any resources to free */
+
+ switch(event->state)
+ {
+ case PropertyDelete:
+ {
+ TRACE("\tPropertyDelete for atom %s on window %ld\n",
+ XGetAtomName(event->display, event->atom), (long)event->window);
+
+ /* FreeResources( event->atom ); */
+ break;
+ }
+
+ case PropertyNewValue:
+ {
+ TRACE("\tPropertyNewValue for atom %s on window %ld\n\n",
+ XGetAtomName(event->display, event->atom), (long)event->window);
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+/***********************************************************************
+ * DuplicatePixmap
+ */
+Pixmap DuplicatePixmap(Pixmap pixmap)
+{
+ Pixmap newPixmap;
+ XImage *xi;
+ Window root;
+ int x,y; /* Unused */
+ unsigned border_width; /* Unused */
+ unsigned int depth, width, height;
+
+ TRACE("\t() Pixmap=%ul\n", pixmap);
+
+ /* Get the Pixmap dimensions and bit depth */
+ if ( 0 == XGetGeometry(g_display, pixmap, &root, &x, &y, &width, &height,
+ &border_width, &depth) )
+ return 0;
+
+ TRACE("\tPixmap properties: width=%d, height=%d, depth=%d\n",
+ width, height, depth);
+
+ newPixmap = XCreatePixmap(g_display, g_win, width, height, depth);
+
+ xi = XGetImage(g_display, pixmap, 0, 0, width, height, AllPlanes, XYPixmap);
+
+ XPutImage(g_display, newPixmap, g_gc, xi, 0, 0, 0, 0, width, height);
+
+ XDestroyImage(xi);
+
+ TRACE("\t() New Pixmap=%ul\n", newPixmap);
+ return newPixmap;
+}
+
+/***********************************************************************
+ * getGC
+ * Get a GC to use for drawing
+ */
+void getGC(Window win, GC *gc)
+{
+ unsigned long valuemask = 0; /* ignore XGCvalues and use defaults */
+ XGCValues values;
+ unsigned int line_width = 6;
+ int line_style = LineOnOffDash;
+ int cap_style = CapRound;
+ int join_style = JoinRound;
+ int dash_offset = 0;
+ static char dash_list[] = {12, 24};
+ int list_length = 2;
+
+ /* Create default Graphics Context */
+ *gc = XCreateGC(g_display, win, valuemask, &values);
+
+ /* specify black foreground since default window background is
+ * white and default foreground is undefined. */
+ XSetForeground(g_display, *gc, BlackPixel(g_display,screen_num));
+
+ /* set line attributes */
+ XSetLineAttributes(g_display, *gc, line_width, line_style,
+ cap_style, join_style);
+
+ /* set dashes */
+ XSetDashes(g_display, *gc, dash_offset, dash_list, list_length);
+}
+
+
+/***********************************************************************
+ * TextOut
+ */
+void TextOut(Window win, GC gc, char *pStr)
+{
+ int y_offset, x_offset;
+
+ y_offset = 10;
+ x_offset = 2;
+
+ /* output text, centered on each line */
+ XDrawString(g_display, win, gc, x_offset, y_offset, pStr,
+ strlen(pStr));
+}