Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 71 additions & 16 deletions Tools/xpbs.m
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
#if HAVE_XFIXES
#include <X11/extensions/Xfixes.h>
#endif
#include <sys/types.h>
#include <sys/select.h>
#include <sys/time.h>

/*
* Non-predefined atoms that are used in the X selection mechanism
Expand Down Expand Up @@ -321,6 +324,7 @@ @interface XDragPbOwner : XPbOwner
#if HAVE_XFIXES
static int xFixesEventBase;
#endif
static int xErrorHandler(Display *d, XErrorEvent *e);

@implementation XPbOwner

Expand All @@ -344,6 +348,12 @@ + (BOOL) initializePasteboard
return NO;
}

/* Direct synchronous X errors (e.g. from an over-eager XChangeProperty) to
* our handler so they are captured rather than terminating the server via
* the default Xlib error handler.
*/
XSetErrorHandler(xErrorHandler);

/*
* Set up atoms for use in X selection mechanism.
*/
Expand Down Expand Up @@ -1285,24 +1295,62 @@ - (void) xSelectionNotify: (XSelectionEvent*)xEvent
{
[md setCapacity: size * 10];
}
while (wait)
{
XNextEvent(xDisplay, &event);

if (event.type == PropertyNotify
&& event.xproperty.state == PropertyNewValue)
{
{
/* The X selection owner may die or stall mid-transfer. Without
* a timeout, XNextEvent below would block forever and gpbs would
* stop answering pasteboard requests, forcing it to be killed.
* So wait on the X connection descriptor for the next chunk with
* a deadline and abandon the transfer if nothing arrives.
*/
NSDate *limit = [NSDate dateWithTimeIntervalSinceNow: 20.0];
int xfd = XConnectionNumber(xDisplay);
BOOL timedOut = NO;

while (wait)
{
if (XQLength(xDisplay) == 0)
{
fd_set rfds;
struct timeval tv;
NSTimeInterval remain = [limit timeIntervalSinceNow];

if (remain <= 0.0)
{
NSDebugLLog(@"Pbs",
@"Timed out waiting for INCR data for %@", _name);
timedOut = YES;
break; /* Timeout */
}
FD_ZERO(&rfds);
FD_SET(xfd, &rfds);
tv.tv_sec = (long)remain;
tv.tv_usec = (long)((remain - (NSTimeInterval)tv.tv_sec)
* 1000000.0);
if (select(xfd + 1, &rfds, NULL, NULL, &tv) <= 0)
{
NSDebugLLog(@"Pbs",
@"Timed out waiting for INCR data for %@", _name);
timedOut = YES;
break; /* Timeout or error */
}
}

XNextEvent(xDisplay, &event);

if (event.type == PropertyNotify
&& event.xproperty.state == PropertyNewValue)
{
long length;

/* Getting the property data also deletes the property,
* telling the other end to send the next chunk.
* An empty chunk indicates end of transfer.
*/
if ((length = [self getSelectionData: xEvent
if ((length = [self getSelectionData: xEvent
type: &actual_type
size: size
into: md]) > 0)
{
{
if (GSDebugSet(@"INCR"))
{
char *name = XGetAtomName(xDisplay, actual_type);
Expand All @@ -1311,13 +1359,20 @@ - (void) xSelectionNotify: (XSelectionEvent*)xEvent
XFree(name);
}
count++;
}
else
{
wait = NO;
}
}
}
}
else
{
wait = NO;
}
}
}
if (timedOut)
{
/* Discard the partial data so a truncated transfer is not
* presented as complete pasteboard contents. */
[md setLength: 0];
}
}
if ([md length] == 0)
{
md = nil;
Expand Down