mirror of
https://github.com/reactos/wine.git
synced 2025-02-01 09:42:14 +00:00
Added clipboard server.
This commit is contained in:
parent
37fd2d1c85
commit
ea0fd4d5d1
@ -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
|
||||
|
@ -1 +1,2 @@
|
||||
Makefile
|
||||
wineclipsrv
|
||||
|
@ -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@
|
||||
|
985
windows/x11drv/wineclipsrv.c
Normal file
985
windows/x11drv/wineclipsrv.c
Normal file
@ -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));
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user