blob: 63276f7d30798fe4a46a95c1d965e6aef7579f4c [file] [log] [blame]
/*
* 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));
}