summaryrefslogtreecommitdiff
path: root/hw/xquartz/pbproxy/x-selection.m
diff options
context:
space:
mode:
Diffstat (limited to 'hw/xquartz/pbproxy/x-selection.m')
-rw-r--r--hw/xquartz/pbproxy/x-selection.m305
1 files changed, 239 insertions, 66 deletions
diff --git a/hw/xquartz/pbproxy/x-selection.m b/hw/xquartz/pbproxy/x-selection.m
index c2d2bcdb2..49f74554a 100644
--- a/hw/xquartz/pbproxy/x-selection.m
+++ b/hw/xquartz/pbproxy/x-selection.m
@@ -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");
@@ -149,6 +151,8 @@ get_property(Window win, Atom property, struct propdata *pdata, Bool delete, Ato
unsigned char *buf = NULL, *chunk = NULL;
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"
@@ -368,20 +380,23 @@ read_prop_32 (Window id, Atom prop, int *nitems_ret)
- (void) clear_event:(XSelectionClearEvent *)e
{
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,80 +486,170 @@ 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;
reply.xselection.target = e->target;
reply.xselection.requestor = e->requestor;
reply.xselection.time = e->time;
- reply.xselection.property = None;
+ reply.xselection.property = None;
+
+ /*
+ Todo
+ if (clipboard_data is an image) {
+ list[0] = atoms->image_jpeg; or some such thing.
+ }
+ ...
+ */
+
+ 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);
+}
- 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 finish this - it's flawed. */
+- (void) send_multiple:(XSelectionRequestEvent *)e
+{
+#if 0
+ XEvent reply;
+ int i, nitems;
+ unsigned long *atoms;
- /*TODO add handling for when the data can be represented as an image. */
+ if (None == e->property)
+ return;
- XChangeProperty (x_dpy, e->requestor, e->property, target,
- 8, PropModeReplace, (unsigned char *) &data,
- sizeof (data));
- reply.xselection.property = e->property;
- }
- else if (target == atoms->multiple)
+ atoms = read_prop_32 (e->requestor, e->property, &nitems);
+
+ if (atoms != NULL)
{
- if (e->property != None)
- {
- int i, nitems;
- unsigned long *atoms;
+ data = [_pasteboard stringForType:NSStringPboardType];
+
+ for (i = 0; i < nitems; i += 2)
+ {
+ Atom target = atoms[i], prop = atoms[i+1];
+
+ atoms[i+1] = convert_1 (e, data, target, prop);
+ }
- atoms = read_prop_32 (e->requestor, e->property, &nitems);
+ XChangeProperty (x_dpy, e->requestor, e->property, target,
+ 32, PropModeReplace, (unsigned char *) atoms,
+ nitems);
+ XFree (atoms);
+ }
+#endif
+}
- if (atoms != NULL)
- {
- data = [_pasteboard stringForType:NSStringPboardType];
+- (void) send_string:(XSelectionRequestEvent *)e utf8:(BOOL)utf8
+{
+ XEvent reply;
+ NSArray *pbtypes;
+
+ TRACE ();
- for (i = 0; i < nitems; i += 2)
- {
- Atom target = atoms[i], prop = atoms[i+1];
+ 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;
- atoms[i+1] = convert_1 (e, data, target, prop);
- }
+ pbtypes = [_pasteboard types];
- XChangeProperty (x_dpy, e->requestor, e->property, target,
- 32, PropModeReplace, (unsigned char *) atoms,
- nitems);
- XFree (atoms);
+ 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 ();
- data = [_pasteboard stringForType:NSStringPboardType];
- if (data != nil)
+ /* 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)
{
- reply.xselection.property = convert_1 (e, data, target, e->property);
+ /* 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];
}
-
- XSendEvent (x_dpy, e->requestor, False, 0, &reply);
}
/* This handles the events resulting from an XConvertSelection request. */
@@ -536,6 +658,8 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
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,8 +744,12 @@ 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;
- [_pasteboard setString:string forType:NSStringPboardType];
+ 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;
- [_pasteboard setString:string forType:NSStringPboardType];
+ 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
+