XQuartz: pbproxy: return the TARGETS list as a 32-bit list to fix a bug that was in the original.

Add TRACE() calls where appropriate to try to figure out why we are losing CLIPBOARD at times, after transferring PRIMARY to the NSPasteboard.

Use the new pbproxy_clipboard_to_pasteboard where appropriate.
This commit is contained in:
George Peter Staplin 2008-09-16 15:21:18 -06:00
parent 37361567b6
commit 40190675a6
2 changed files with 246 additions and 72 deletions

View File

@ -91,6 +91,7 @@ struct propdata {
- (void) handle_selection:(Atom)selection type:(Atom)type propdata:(struct propdata *)pdata;
- (void) claim_clipboard;
- (void) set_clipboard_manager;
- (void) own_clipboard;
@end

View File

@ -34,8 +34,6 @@
#include <stdlib.h>
#include <X11/Xatom.h>
#include <unistd.h> /*GPS may not be needed now */
// These will be set by X11Controller.m once this is integrated into a server thread
BOOL pbproxy_active = YES;
BOOL pbproxy_primary_on_grab = NO; // This is provided as an option for people who want it and has issues that won't ever be addressed to make it *always* work
@ -69,6 +67,8 @@ is_incr_type (XSelectionEvent *e)
unsigned long numitems = 0UL, bytesleft = 0UL;
unsigned char *chunk;
TRACE ();
if (Success != XGetWindowProperty (x_dpy, e->requestor, e->property,
/*offset*/ 0L, /*length*/ 4UL,
/*Delete*/ False,
@ -92,6 +92,8 @@ find_preferred (struct propdata *pdata)
size_t i;
Bool png = False, utf8 = False, string = False;
TRACE ();
if (pdata->length % sizeof (a))
{
fprintf(stderr, "Atom list is not a multiple of the size of an atom!\n");
@ -150,6 +152,8 @@ get_property(Window win, Atom property, struct propdata *pdata, Bool delete, Ato
size_t buflen = 0, chunkbytesize = 0;
int format;
TRACE ();
if(None == property)
return True;
@ -243,6 +247,8 @@ read_prop_32 (Window id, Atom prop, int *nitems_ret)
*/
- (void) release_pending
{
TRACE ();
free_propdata (&pending.propdata);
pending.requestor = None;
pending.selection = None;
@ -255,6 +261,8 @@ read_prop_32 (Window id, Atom prop, int *nitems_ret)
unsigned char *newdata;
size_t newlength;
TRACE ();
if (requestor != pending.requestor)
{
[self release_pending];
@ -330,6 +338,8 @@ read_prop_32 (Window id, Atom prop, int *nitems_ret)
{
Window w;
TRACE ();
w = XGetSelectionOwner (x_dpy, atoms->primary);
if (None != w)
@ -350,6 +360,8 @@ read_prop_32 (Window id, Atom prop, int *nitems_ret)
*/
- (void) set_clipboard_manager
{
TRACE ();
if (None != XGetSelectionOwner (x_dpy, atoms->clipboard_manager))
{
fprintf (stderr, "A clipboard manager is already running!\n"
@ -369,19 +381,22 @@ read_prop_32 (Window id, Atom prop, int *nitems_ret)
{
TRACE ();
DB ("e->selection %s\n", XGetAtomName (x_dpy, e->selection));
if (atoms->clipboard == e->selection)
{
/*
* We lost ownership of the CLIPBOARD.
*/
[self claim_clipboard];
}
else if (atoms->clipboard_manager == e->selection)
{
/*TODO/HMM What should we do here? */
/* Another CLIPBOARD_MANAGER has set itself as owner.
* a) we can call [self set_clipboard_manager] here and risk a war.
* b) we can print a message and exit.
* b) we can print a message and exit. Ideally we would popup a message box.
*/
fprintf (stderr, "error: another clipboard manager was started!\n");
exit (EXIT_FAILURE);
@ -395,6 +410,11 @@ read_prop_32 (Window id, Atom prop, int *nitems_ret)
{
Window owner;
TRACE ();
if (NO == pbproxy_clipboard_to_pasteboard)
return;
owner = XGetSelectionOwner (x_dpy, atoms->clipboard);
if (None == owner)
{
@ -403,14 +423,7 @@ read_prop_32 (Window id, Atom prop, int *nitems_ret)
* Set pbproxy's _selection_window as the owner, and continue.
*/
DB ("No clipboard owner.\n");
do
{
XSetSelectionOwner (x_dpy, atoms->clipboard, _selection_window,
CurrentTime);
} while (_selection_window != XGetSelectionOwner (x_dpy,
atoms->clipboard));
[self own_clipboard];
return;
}
@ -419,10 +432,29 @@ read_prop_32 (Window id, Atom prop, int *nitems_ret)
request_atom = atoms->targets;
XConvertSelection (x_dpy, atoms->clipboard, atoms->targets,
atoms->clipboard, _selection_window, CurrentTime);
XFlush (x_dpy);
/* Now we will get a SelectionNotify event in the future. */
}
/* Greedily acquire the clipboard. */
- (void) own_clipboard
{
TRACE ();
/* We should perhaps have a boundary limit on the number of iterations... */
do
{
XSetSelectionOwner (x_dpy, atoms->clipboard, _selection_window,
CurrentTime);
} while (_selection_window != XGetSelectionOwner (x_dpy,
atoms->clipboard));
}
/* The preference should be for UTF8_STRING before the XA_STRING*/
/* This should NOT be used for Atom transfers, because it uses 8 bits. */
/* This was previously used for Atom transfers (incorrectly). */
static Atom
convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
{
@ -454,20 +486,23 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
}
}
/* FIXME: handle COMPOUND_TEXT target */
/*gstaplin: should we [data release]? */
[data release];
return ret;
}
- (void) request_event:(XSelectionRequestEvent *)e
/*
* This responds to a TARGETS request.
* The result is a list of a Atoms that correspond to the types available
* for a selection.
* For instance an application might provide a UTF8_STRING and a STRING
* (in Latin-1 encoding). The requestor can then make the choice based on
* the targets list.
*/
- (void) send_targets:(XSelectionRequestEvent *)e
{
/* Someone's asking us for the data on the pasteboard */
XEvent reply;
NSString *data;
Atom target;
TRACE ();
long list[2];
reply.xselection.type = SelectionNotify;
reply.xselection.selection = e->selection;
@ -476,30 +511,40 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
reply.xselection.time = e->time;
reply.xselection.property = None;
target = e->target;
if (target == atoms->targets)
{
/* This is where we respond to the TARGETS request. */
long data[2];
data[0] = atoms->utf8_string;
data[1] = XA_STRING;
/*TODO add handling for when the data can be represented as an image. */
XChangeProperty (x_dpy, e->requestor, e->property, target,
8, PropModeReplace, (unsigned char *) &data,
sizeof (data));
reply.xselection.property = e->property;
/*
Todo
if (clipboard_data is an image) {
list[0] = atoms->image_jpeg; or some such thing.
}
else if (target == atoms->multiple)
{
if (e->property != None)
...
*/
list[0] = atoms->utf8_string;
list[1] = XA_STRING;
XChangeProperty (x_dpy, e->requestor, e->property, e->target,
32, PropModeReplace, (unsigned char *) list,
sizeof (list) / sizeof (Atom));
reply.xselection.property = e->property;
/*
* We are supposed to use an empty event mask, and not propagate
* the event, according to the ICCCM.
*/
XSendEvent (x_dpy, e->requestor, False, 0, &reply);
}
/*TODO finish this - it's flawed. */
- (void) send_multiple:(XSelectionRequestEvent *)e
{
#if 0
XEvent reply;
int i, nitems;
unsigned long *atoms;
if (None == e->property)
return;
atoms = read_prop_32 (e->requestor, e->property, &nitems);
if (atoms != NULL)
@ -518,24 +563,103 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
nitems);
XFree (atoms);
}
}
#endif
}
data = [_pasteboard stringForType:NSStringPboardType];
if (data != nil)
- (void) send_string:(XSelectionRequestEvent *)e utf8:(BOOL)utf8
{
reply.xselection.property = convert_1 (e, data, target, e->property);
XEvent reply;
NSArray *pbtypes;
TRACE ();
reply.xselection.type = SelectionNotify;
reply.xselection.selection = e->selection;
reply.xselection.target = e->target;
reply.xselection.requestor = e->requestor;
reply.xselection.time = e->time;
reply.xselection.property = None;
pbtypes = [_pasteboard types];
if ([pbtypes containsObject: NSStringPboardType])
{
NSString *data = [_pasteboard stringForType:NSStringPboardType];
if (nil != data)
{
const char *bytes;
NSUInteger length;
if (utf8) {
bytes = [data UTF8String];
/*
* We don't want the UTF-8 string length here.
* We want the length in bytes.
*/
length = [data lengthOfBytesUsingEncoding:NSASCIIStringEncoding];
} else {
bytes = [data cStringUsingEncoding:NSISOLatin1StringEncoding];
length = [data lengthOfBytesUsingEncoding:NSASCIIStringEncoding];
}
XChangeProperty (x_dpy, e->requestor, e->property, e->target,
8, PropModeReplace, (unsigned char *) bytes, length);
reply.xselection.property = e->property;
[data release];
}
}
/* Always send a response, even if the property value is None. */
XSendEvent (x_dpy, e->requestor, False, 0, &reply);
}
- (void) request_event:(XSelectionRequestEvent *)e
{
/* Someone's asking us for the data on the pasteboard */
TRACE ();
/* TODO We should also keep track of the time of the selection, and
* according to the ICCCM "refuse the request" if the event timestamp
* is before we owned it.
* What should we base the time on? How can we get the current time just
* before an XSetSelectionOwner? Is it the server's time, or the clients?
* According to the XSelectionRequestEvent manual page, the Time value
* may be set to CurrentTime or a time, so that makes it a bit different.
* Perhaps we should just punt and ignore races.
*/
if (e->target == atoms->targets)
{
/* The paste requestor wants to know what TARGETS we support. */
[self send_targets:e];
}
else if (e->target == atoms->multiple)
{
[self send_multiple:e];
}
else if (e->target == atoms->utf8_string)
{
[self send_string:e utf8:YES];
}
else if (e->target == atoms->string)
{
[self send_string:e utf8:NO];
}
else
{
//[self send_null:e];
}
}
/* This handles the events resulting from an XConvertSelection request. */
- (void) notify_event:(XSelectionEvent *)e
{
Atom type;
struct propdata pdata;
TRACE ();
[self release_pending];
DB ("notify_event\n");
@ -551,7 +675,6 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
* This is an INCR-style transfer, which means that we
* will get the data after a series of PropertyNotify events.
*/
DB ("is INCR\n");
if (get_property (e->requestor, e->property, &pdata, /*Delete*/ True, &type))
@ -573,6 +696,9 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
/* We have the complete selection data.*/
[self handle_selection: e->selection type:type propdata:&pdata];
if (pbproxy_clipboard_to_pasteboard && e->selection == atoms->clipboard)
[self own_clipboard];
}
}
@ -582,10 +708,18 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
struct propdata pdata;
Atom type;
TRACE ();
if (None != pending.requestor && PropertyNewValue == e->state)
{
DB ("pending.requestor 0x%lx\n", pending.requestor);
if (get_property (e->window, e->atom, &pdata, /*Delete*/ True, &type))
{
if (pbproxy_clipboard_to_pasteboard && pending.selection == atoms->clipboard)
[self own_clipboard];
[self release_pending];
return;
}
@ -593,6 +727,9 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
{
/* We completed the transfer. */
[self handle_selection: pending.selection type: type propdata: &pending.propdata];
if (pbproxy_clipboard_to_pasteboard && pending.selection == atoms->clipboard)
[self own_clipboard];
pending.propdata = null_propdata;
pending.requestor = None;
pending.selection = None;
@ -607,7 +744,11 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
- (void) handle_targets: (Atom)selection propdata:(struct propdata *)pdata
{
/* Find a type we can handle and prefer from the list of ATOMs. */
Atom preferred = find_preferred (pdata);
Atom preferred;
TRACE ();
preferred = find_preferred (pdata);
if (None == preferred)
{
@ -624,6 +765,8 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
_selection_window, CurrentTime);
}
/*TODO I think this should convert to a standard NSPasteboard format,
* such as TIFF or PICT with a NSBitmapImageRep class. */
/* This handles the image type of selection (typically in CLIPBOARD). */
- (void) handle_image: (struct propdata *)pdata extension:(NSString *)fileext
{
@ -632,6 +775,8 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
NSUInteger length;
NSData *data;
TRACE ();
pbtype = NSCreateFileContentsPboardType (fileext);
if (nil == pbtype)
{
@ -674,22 +819,48 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
/* This handles the UTF8_STRING type of selection. */
- (void) handle_utf8_string: (struct propdata *)pdata
{
NSString *string = [[NSString alloc] initWithBytes:pdata->data length:pdata->length encoding:NSUTF8StringEncoding];
NSString *string;
NSArray *pbtypes;
TRACE ();
string = [[NSString alloc] initWithBytes:pdata->data length:pdata->length encoding:NSUTF8StringEncoding];
if (nil == string)
return;
pbtypes = [NSArray arrayWithObject:NSStringPboardType];
if (nil != pbtypes)
{
[_pasteboard declareTypes:pbtypes owner:self];
[_pasteboard setString:string forType:NSStringPboardType];
[pbtypes release];
}
[string release];
}
/* This handles the XA_STRING type, which should be in Latin-1. */
- (void) handle_string: (struct propdata *)pdata
{
NSString *string = [[NSString alloc] initWithBytes:pdata->data length:pdata->length encoding:NSISOLatin1StringEncoding];
NSString *string;
NSArray *pbtypes;
string = [[NSString alloc] initWithBytes:pdata->data length:pdata->length encoding:NSISOLatin1StringEncoding];
if (nil == string)
return;
pbtypes = [NSArray arrayWithObject:NSStringPboardType];
if (nil != pbtypes)
{
[_pasteboard declareTypes:pbtypes owner:self];
[_pasteboard setString:string forType:NSStringPboardType];
[pbtypes release];
}
[string release];
}
@ -724,6 +895,9 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
if (selection == atoms->clipboard && pdata->data)
{
/* This may not be used.
* We should really pull from the data in the NSPasteboard.
*/
free_propdata(&request_data.propdata);
request_data.propdata = *pdata;
request_data.type = type;
@ -745,8 +919,6 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
{
TRACE ();
DB ("PB changed owner");
/* Right now we don't care with this. */
}
@ -806,3 +978,4 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
}
@end