From ea0fd4d5d1ca47f1c6a272112fe44846576071ab Mon Sep 17 00:00:00 2001 From: Noel Borthwick Date: Thu, 23 Sep 1999 16:14:44 +0000 Subject: [PATCH] Added clipboard server. --- Makefile.in | 2 + windows/x11drv/.cvsignore | 1 + windows/x11drv/Makefile.in | 7 + windows/x11drv/wineclipsrv.c | 985 +++++++++++++++++++++++++++++++++++ 4 files changed, 995 insertions(+) create mode 100644 windows/x11drv/wineclipsrv.c diff --git a/Makefile.in b/Makefile.in index 9ca6f595c3..3ddf0e8291 100644 --- a/Makefile.in +++ b/Makefile.in @@ -258,6 +258,8 @@ install_lib: dummy $(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 f3c7a7c5da..4f940bf9c7 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 e037ca3b1e..afc90d3c11 100644 --- a/windows/x11drv/Makefile.in +++ b/windows/x11drv/Makefile.in @@ -16,6 +16,13 @@ C_SRCS = \ 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 0000000000..63276f7d30 --- /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 +#include +#include +#include +#include + +/* 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[] = ""; /* 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)); +}