xserver: Branch 'xorg-server-1.4-apple'

Jeremy Huddleston jeremyhu at kemper.freedesktop.org
Sun Sep 14 09:23:23 PDT 2008


 hw/xquartz/pbproxy/main.m        |   37 +-
 hw/xquartz/pbproxy/pbproxy.h     |    9 
 hw/xquartz/pbproxy/x-input.m     |   30 +
 hw/xquartz/pbproxy/x-selection.h |   28 +
 hw/xquartz/pbproxy/x-selection.m |  603 +++++++++++++++++++++++++++------------
 5 files changed, 518 insertions(+), 189 deletions(-)

New commits:
commit 5c8b956f8f3f17e8d577d97cb66424954be72684
Author: Jeremy Huddleston <jeremyhu at freedesktop.org>
Date:   Sun Sep 14 09:23:17 2008 -0700

    XQuartz: pbproxy: First round of updates to pbproxy from George.

diff --git a/hw/xquartz/pbproxy/main.m b/hw/xquartz/pbproxy/main.m
index eaf14a7..f259726 100644
--- a/hw/xquartz/pbproxy/main.m
+++ b/hw/xquartz/pbproxy/main.m
@@ -14,14 +14,13 @@
 Display *x_dpy;
 int x_apple_wm_event_base, x_apple_wm_error_base;
 
-Atom x_atom_wm_state, x_atom_wm_protocols, x_atom_wm_delete_window;
-Atom x_atom_clipboard, x_atom_text, x_atom_utf8_string;
-Atom x_atom_targets, x_atom_multiple, x_atom_cstring;
+struct atom_list atom_list_inst;
+struct atom_list *atoms = &atom_list_inst;
 
 static int x_grab_count;
 static Bool x_grab_synced;
 
-static BOOL _is_active = YES;		/* FIXME: should query server */
+static BOOL _is_active = YES;		/* FIXME: should query server */ /*GPS why? Is there a race?*/
 
 static x_selection *_selection_object;
 
@@ -65,19 +64,24 @@ static void x_init (void) {
     }
     
     XSetIOErrorHandler (x_io_error_handler);
-    x_atom_clipboard = XInternAtom (x_dpy, "CLIPBOARD", False);
-    x_atom_text = XInternAtom (x_dpy, "TEXT", False);
-    x_atom_utf8_string = XInternAtom (x_dpy, "UTF8_STRING", False);
-    x_atom_targets = XInternAtom (x_dpy, "TARGETS", False);
-    x_atom_multiple = XInternAtom (x_dpy, "MULTIPLE", False);
-    x_atom_cstring = XInternAtom (x_dpy, "CSTRING", False);
-        
+    atoms->primary = XInternAtom (x_dpy, "PRIMARY", False);
+    atoms->clipboard = XInternAtom (x_dpy, "CLIPBOARD", False);
+    atoms->text = XInternAtom (x_dpy, "TEXT", False);
+    atoms->utf8_string = XInternAtom (x_dpy, "UTF8_STRING", False);
+    atoms->targets = XInternAtom (x_dpy, "TARGETS", False);
+    atoms->multiple = XInternAtom (x_dpy, "MULTIPLE", False);
+    atoms->cstring = XInternAtom (x_dpy, "CSTRING", False);
+    atoms->image_png = XInternAtom (x_dpy, "image/png", False);
+    atoms->incr = XInternAtom (x_dpy, "INCR", False);
+    atoms->atom = XInternAtom (x_dpy, "ATOM", False);
+    atoms->clipboard_manager = XInternAtom (x_dpy, "CLIPBOARD_MANAGER", False);
+
     if (!XAppleWMQueryExtension (x_dpy, &x_apple_wm_event_base,
                                  &x_apple_wm_error_base)) {
         fprintf (stderr, "can't open AppleWM server extension\n");
         exit (1);
     }
-        
+    
     XAppleWMSelectInput (x_dpy, AppleWMActivationNotifyMask |
                          AppleWMPasteboardNotifyMask);
     
@@ -85,15 +89,22 @@ static void x_init (void) {
     
     x_input_register ();
     x_input_run ();
+
+    [_selection_object set_clipboard_manager];
+    [_selection_object reclaim_clipboard];
 }
 
 static void x_shutdown (void) {
+    /*GPS: signal_handler() calls this, and I don't think these are async-signal safe. */
+    /*TODO use a socketpair() to trigger a cleanup.  This is totally unsafe according to Jordan. */
+
     [_selection_object release];
     _selection_object = nil;
 
+
     XCloseDisplay (x_dpy);
     x_dpy = NULL;
-    exit(0);
+    exit(0); //GPS this is almost certainly unsafe (sigaction(2) doesn't list it).
 }
 
 static void x_error_shutdown (void) {
diff --git a/hw/xquartz/pbproxy/pbproxy.h b/hw/xquartz/pbproxy/pbproxy.h
index ddadbb3..3682421 100644
--- a/hw/xquartz/pbproxy/pbproxy.h
+++ b/hw/xquartz/pbproxy/pbproxy.h
@@ -22,8 +22,13 @@ extern Time x_current_timestamp (void);
 
 extern Display *x_dpy;
 extern int x_apple_wm_event_base, x_apple_wm_error_base;
-extern Atom x_atom_clipboard, x_atom_text, x_atom_utf8_string;
-extern Atom x_atom_targets, x_atom_multiple, x_atom_cstring;
+
+struct atom_list {
+    Atom primary, clipboard, text, utf8_string, string, targets, multiple,
+	cstring, image_png, incr, atom, clipboard_manager;
+};
+
+extern struct atom_list *atoms;
 
 /* from x-input.m */
 extern void x_input_register (void);
diff --git a/hw/xquartz/pbproxy/x-input.m b/hw/xquartz/pbproxy/x-input.m
index 2e77250..f720af5 100644
--- a/hw/xquartz/pbproxy/x-input.m
+++ b/hw/xquartz/pbproxy/x-input.m
@@ -21,7 +21,13 @@ CFRunLoopSourceRef x_dpy_source;
 /* Timestamp when the X server last told us it's active */
 static Time last_activation_time;
 
+static FILE *getSelectionLog(void) {
+    return fopen("/tmp/selection.log", "a");
+}
+
 static void x_event_apple_wm_notify(XAppleWMNotifyEvent *e) {
+    FILE *fp = getSelectionLog();
+
     switch (e->type - x_apple_wm_event_base) {              
         case AppleWMActivationNotify:
             switch (e->kind) {
@@ -39,33 +45,47 @@ static void x_event_apple_wm_notify(XAppleWMNotifyEvent *e) {
             break;
             
         case AppleWMPasteboardNotify:
+	    fprintf(fp, "AppleWMPasteboardNotify\n");
+	   
             switch (e->kind) {
                 case AppleWMCopyToPasteboard:
                     [x_selection_object () x_copy:e->time];
             }
             break;
     }
+    fclose(fp);
 }
 
 void x_input_run (void) {
+    FILE *fp = getSelectionLog();
+
     while (XPending (x_dpy) != 0) {
-        XEvent e;
-        
+        XEvent e;       
+
         XNextEvent (x_dpy, &e);
         
         switch (e.type) {                
             case SelectionClear:
-                [x_selection_object () clear_event:&e.xselectionclear];
+		fprintf(fp, "SelectionClear\n");
+
+	        [x_selection_object () clear_event:&e.xselectionclear];
                 break;
                 
             case SelectionRequest:
+		fprintf(fp, "SelectionRequest\n");
                 [x_selection_object () request_event:&e.xselectionrequest];
                 break;
                 
             case SelectionNotify:
+		fprintf(fp, "SelectionNotify\n");
                 [x_selection_object () notify_event:&e.xselection];
                 break;
                 
+	    case PropertyNotify:
+		fprintf(fp, "PropertyNotify\n");
+		[x_selection_object () property_event:&e.xproperty];
+		break;
+
             default:
                 if (e.type - x_apple_wm_event_base >= 0
                     && e.type - x_apple_wm_event_base < AppleWMNumberEvents) {
@@ -73,7 +93,10 @@ void x_input_run (void) {
                 }
                 break;
         }
+
+	XFlush(x_dpy);
     }
+    fclose(fp);
 }
 
 static int add_input_socket (int sock, CFOptionFlags callback_types,
@@ -111,3 +134,4 @@ void x_input_register(void) {
         exit (1);
     }
 }
+
diff --git a/hw/xquartz/pbproxy/x-selection.h b/hw/xquartz/pbproxy/x-selection.h
index b31bf63..88408bf 100644
--- a/hw/xquartz/pbproxy/x-selection.h
+++ b/hw/xquartz/pbproxy/x-selection.h
@@ -34,6 +34,12 @@
 #include "pbproxy.h"
 #include <AppKit/NSPasteboard.h>
 
+/* This stores image data or text. */
+struct propdata {
+	unsigned char *data;
+	size_t length;
+};
+
 @interface x_selection : NSObject
 {
 @private
@@ -53,6 +59,24 @@
 
     /* When true, we're expecting a SelectionNotify event. */
     unsigned int _pending_notify :1;
+ 
+    Atom request_atom;
+    
+    struct {
+        struct propdata propdata;
+        Window requestor;
+        Atom selection;
+    } pending;
+ 
+    /* This may not be needed.*/
+    /* If we can have the Apple clipboard translate to PNG or JPEG we can
+     * do away with this.   Otherwise we could use libjpeg and libpng
+     * to convert some raw clipboard format to the proper format.
+     */
+    struct {
+	struct propdata propdata;
+	Atom type;
+    } request_data;
 }
 
 - (void) x_active:(Time)timestamp;
@@ -63,6 +87,10 @@
 - (void) clear_event:(XSelectionClearEvent *)e;
 - (void) request_event:(XSelectionRequestEvent *)e;
 - (void) notify_event:(XSelectionEvent *)e;
+- (void) property_event:(XPropertyEvent *)e;
+- (void) handle_selection:(Atom)selection type:(Atom)type propdata:(struct propdata *)pdata;
+- (void) reclaim_clipboard;
+- (void) set_clipboard_manager;
 
 @end
 
diff --git a/hw/xquartz/pbproxy/x-selection.m b/hw/xquartz/pbproxy/x-selection.m
index 5b2ba9c..e390dc0 100644
--- a/hw/xquartz/pbproxy/x-selection.m
+++ b/hw/xquartz/pbproxy/x-selection.m
@@ -30,12 +30,172 @@
 
 #import "x-selection.h"
 
+#include <stdio.h>
+#include <stdlib.h>
 #include <X11/Xatom.h>
 
-#include <unistd.h>
+#include <unistd.h> /*GPS may not be needed now */
 
 @implementation x_selection
 
+static struct propdata null_propdata = {NULL, 0};
+
+static void
+init_propdata (struct propdata *pdata)
+{
+    *pdata = null_propdata;
+}
+
+static void
+free_propdata (struct propdata *pdata)
+{
+    free (pdata->data);
+    *pdata = null_propdata;
+}
+
+/* Return True if this is an INCR-style transfer. */
+static Bool
+is_incr_type (XSelectionEvent *e)
+{
+    Atom seltype;
+    int format;
+    unsigned long numitems = 0UL, bytesleft = 0UL;
+    unsigned char *chunk;
+       
+    if (Success != XGetWindowProperty (x_dpy, e->requestor, e->property,
+				       /*offset*/ 0L, /*length*/ 4UL,
+				       /*Delete*/ False,
+				       AnyPropertyType, &seltype, &format,
+				       &numitems, &bytesleft, &chunk))
+    {
+	return False;
+    }
+
+    if(chunk)
+	XFree(chunk);
+
+    return (seltype == atoms->incr) ? True : False;
+}
+
+/* This finds the preferred type from a TARGETS list.*/
+static Atom 
+find_preferred (struct propdata *pdata)
+{
+    Atom a = None;
+    size_t i;
+    Bool png = False, utf8 = False, string = False;
+
+    if (pdata->length % sizeof (a))
+    {
+	fprintf(stderr, "Atom list is not a multiple of the size of an atom!\n");
+	return None;
+    }
+
+    for (i = 0; i < pdata->length; i += sizeof (a))
+    {
+	memcpy (&a, pdata->data + i, sizeof (a));
+	
+	if (a == atoms->image_png)
+	{
+	    png = True;
+	} 
+	else if (a == atoms->utf8_string)
+	{
+	    utf8 = True;
+        } 
+	else if (a == atoms->string)
+	{
+	    string = True;
+	}
+    }
+
+    /*We prefer PNG over strings, and UTF8 over a Latin-1 string.*/
+    if (png)
+	return atoms->image_png;
+
+    if (utf8)
+	return atoms->utf8_string;
+
+    if (string)
+	return atoms->string;
+
+    /* This is evidently something we don't know how to handle.*/
+    return None;
+}
+
+/*
+ * Return True if an error occurs.  Return False if pdata has data 
+ * and we finished. 
+ * The property is only deleted when bytesleft is 0 if delete is True.
+ */
+static Bool
+get_property(Window win, Atom property, struct propdata *pdata, Bool delete, Atom *type) 
+{
+    long offset = 0;
+    unsigned long numitems, bytesleft = 0;
+#ifdef TEST
+    /* This is used to test the growth handling. */
+    unsigned long length = 4UL;
+#else
+    unsigned long length = (100000UL + 3) / 4; 
+#endif
+    unsigned char *buf = NULL, *chunk = NULL;
+    size_t buflen = 0, chunkbytesize = 0;
+    int format;
+    
+    if(None == property)
+	return True;
+    
+    do {
+	unsigned long newbuflen;
+	unsigned char *newbuf;
+	
+	if (Success != XGetWindowProperty (x_dpy, win, property,
+					   offset, length, delete, 
+					   AnyPropertyType,
+					   type, &format, &numitems, 
+					   &bytesleft, &chunk)) {
+	    free (buf);
+	    return True;
+	}
+	
+#ifdef TEST
+	printf("format %d numitems %lu bytesleft %lu\n",
+	       format, numitems, bytesleft);
+	
+	printf("type %s\n", XGetAtomName(dis, *type));
+#endif
+	
+	/* Format is the number of bits. */
+	chunkbytesize = numitems * (format / 8);
+
+#ifdef TEST
+	printf("chunkbytesize %zu\n", chunkbytesize);
+#endif
+	
+	newbuflen = buflen + chunkbytesize;
+	newbuf = realloc (buf, newbuflen);
+	if (NULL == newbuf) {
+	    XFree (chunk);
+	    free (buf);
+	    return True;
+	}
+	
+	memcpy (newbuf + buflen, chunk, chunkbytesize);
+	XFree (chunk);
+	buf = newbuf;
+	buflen = newbuflen;
+	/* offset is a multiple of 32 bits*/
+	offset += chunkbytesize / 4;
+    } while (bytesleft > 0);
+    
+    pdata->data = buf;
+    pdata->length = buflen;
+
+    return /*success*/ False;
+}
+
+
 static unsigned long *
 read_prop_32 (Window id, Atom prop, int *nitems_ret)
 {
@@ -70,50 +230,55 @@ read_prop_32 (Window id, Atom prop, int *nitems_ret)
     return (unsigned long *) data;
 }
 
-float
-get_time (void)
+/* 
+ * This should be called after a selection has been copied, 
+ * or when the selection is unfinished before a transfer completes. 
+ */
+- (void) release_pending
 {
-  extern void Microseconds ();
-  UnsignedWide usec;
-  long long ll;
-
-  Microseconds (&usec);
-  ll = ((long long) usec.hi << 32) | usec.lo;
-
-  return ll / 1e6;
+    free_propdata (&pending.propdata);
+    pending.requestor = None;
+    pending.selection = None;
 }
 
-static Bool
-IfEventWithTimeout (Display *dpy, XEvent *e, int timeout,
-		    Bool (*pred) (Display *, XEvent *, XPointer),
-		    XPointer arg)
+/* Return True if an error occurs during an append.*/
+/* Return False if the append succeeds. */
+- (Bool) append_to_pending:(struct propdata *)pdata requestor:(Window)requestor
 {
-    float start = get_time ();
-    fd_set fds;
-    struct timeval tv;
-
-    do {
-	if (XCheckIfEvent (x_dpy, e, pred, arg))
-	    return True;
-
-	FD_ZERO (&fds);
-	FD_SET (ConnectionNumber (x_dpy), &fds);
-	tv.tv_usec = 0;
-	tv.tv_sec = timeout;
-
-	if (select (FD_SETSIZE, &fds, NULL, NULL, &tv) != 1)
-	    break;
+    unsigned char *newdata;
+    size_t newlength;
+    
+    if (requestor != pending.requestor)
+    {
+	[self release_pending];
+	pending.requestor = requestor;
+    }
+	
+    newlength = pending.propdata.length + pdata->length;
+    newdata = realloc(pending.propdata.data, newlength);
 
-    } while (start + timeout > get_time ());
+    if(NULL == newdata) 
+    {
+	perror("realloc propdata");
+	[self release_pending];
+        return True;
+    }
 
+    memcpy(newdata + pending.propdata.length, pdata->data, pdata->length);
+    pending.propdata.data = newdata;
+    pending.propdata.length = newlength;
+	
     return False;
 }
 
+
+
 /* Called when X11 becomes active (i.e. has key focus) */
 - (void) x_active:(Time)timestamp
 {
     TRACE ();
 
+#if 0
     if ([_pasteboard changeCount] != _my_last_change)
     {
 	if ([_pasteboard availableTypeFromArray: _known_types] != nil)
@@ -121,12 +286,13 @@ IfEventWithTimeout (Display *dpy, XEvent *e, int timeout,
 	    /* Pasteboard has data we should proxy; I think it makes
 	       sense to put it on both CLIPBOARD and PRIMARY */
 
-	    XSetSelectionOwner (x_dpy, x_atom_clipboard,
+	    XSetSelectionOwner (x_dpy, atoms->clipboard,
 				_selection_window, timestamp);
 	    XSetSelectionOwner (x_dpy, XA_PRIMARY,
 				_selection_window, timestamp);
 	}
     }
+#endif
 }
 
 /* Called when X11 loses key focus */
@@ -135,19 +301,20 @@ IfEventWithTimeout (Display *dpy, XEvent *e, int timeout,
     Window w;
 
     TRACE ();
-
+#if 0
     if (_proxied_selection == XA_PRIMARY)
       return;
 
-    w = XGetSelectionOwner (x_dpy, x_atom_clipboard);
+    w = XGetSelectionOwner (x_dpy, atoms->clipboard);
 
     if (w != None && w != _selection_window)
     {
 	/* An X client has the selection, proxy it to the pasteboard */
 
 	_my_last_change = [_pasteboard declareTypes:_known_types owner:self];
-	_proxied_selection = x_atom_clipboard;
+	_proxied_selection = atoms->clipboard;
     }
+#endif
 }
 
 /* Called when the Edit/Copy item on the main X11 menubar is selected
@@ -156,16 +323,16 @@ IfEventWithTimeout (Display *dpy, XEvent *e, int timeout,
 {
     Window w;
 
-    /* Lazily copies the PRIMARY selection to the pasteboard. */
-
-    w = XGetSelectionOwner (x_dpy, XA_PRIMARY);
+    w = XGetSelectionOwner (x_dpy, atoms->primary);
 
-    if (w != None && w != _selection_window)
+    if (None != w)
     {
-	XSetSelectionOwner (x_dpy, x_atom_clipboard,
-			    _selection_window, timestamp);
-	_my_last_change = [_pasteboard declareTypes:_known_types owner:self];
-	_proxied_selection = XA_PRIMARY;
+	request_atom = atoms->targets;
+
+	puts("Asking for targets");
+
+	XConvertSelection (x_dpy, atoms->primary, atoms->targets,
+			   atoms->primary, _selection_window, CurrentTime);
     }
     else
     {
@@ -173,16 +340,80 @@ IfEventWithTimeout (Display *dpy, XEvent *e, int timeout,
     }
 }
 
-
-/* X events */
+/*
+ *
+ */
+- (void) set_clipboard_manager
+{
+    if (None != XGetSelectionOwner (x_dpy, atoms->clipboard_manager))
+    {
+	fprintf (stderr, "A clipboard manager is already running!\n"
+		 "pbproxy can not continue!\n");
+	exit (EXIT_FAILURE);
+    }
 
+    XSetSelectionOwner (x_dpy, atoms->clipboard_manager, _selection_window,
+			CurrentTime);
+}
+
+/*
+ * This occurs when we previously owned a selection, 
+ * and then lost it from another client.
+ */
 - (void) clear_event:(XSelectionClearEvent *)e
 {
     TRACE ();
+ 
+    if (atoms->clipboard == e->selection)
+    {
+	/* 
+	 * We lost ownership of the CLIPBOARD.
+	 */
+	[self reclaim_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.
+	 */
+	fprintf (stderr, "error: another clipboard manager was started!\n"); 
+	exit (EXIT_FAILURE);
+    }
+}
+
+/* 
+ * We greedily acquire the clipboard after it changes, and on startup.
+ */
+- (void) reclaim_clipboard
+{
+    Window owner;
+    
+    owner = XGetSelectionOwner (x_dpy, atoms->clipboard);
+    if (None == owner)
+    {
+	/*
+	 * The owner probably died or we are just starting up pbproxy.
+	 * Set pbproxy's _selection_window as the owner, and continue.
+	 */
+	do 
+	{
+	    XSetSelectionOwner (x_dpy, atoms->clipboard, _selection_window,
+				CurrentTime);
+	} while (_selection_window != XGetSelectionOwner (x_dpy,
+							  atoms->clipboard));
 
-    /* Right now we don't care about this. */
+	return;
+    }
+    
+    request_atom = atoms->targets;
+    XConvertSelection (x_dpy, atoms->clipboard, atoms->targets,
+		       atoms->clipboard, _selection_window, CurrentTime);
+    /* Now we will get a SelectionNotify event in the future. */
 }
 
+/* The preference should be for UTF8_STRING before the XA_STRING*/
 static Atom
 convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
 {
@@ -191,12 +422,12 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
     if (data == nil)
 	return ret;
 
-    if (target == x_atom_text)
-	target = x_atom_utf8_string;
+    if (target == atoms->text)
+	target = atoms->utf8_string;
 
     if (target == XA_STRING
-	|| target == x_atom_cstring
-	|| target == x_atom_utf8_string)
+	|| target == atoms->cstring
+	|| target == atoms->utf8_string)
     {
 	const char *bytes;
 
@@ -221,7 +452,6 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
 - (void) request_event:(XSelectionRequestEvent *)e
 {
     /* Someone's asking us for the data on the pasteboard */
-
     XEvent reply;
     NSString *data;
     Atom target;
@@ -236,12 +466,13 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
     reply.xselection.property = None;
 
     target = e->target;
-
-    if (target == x_atom_targets)
+    
+    if (target == atoms->targets) 
     {
+	/* This is where we respond to the TARGETS request. */
 	long data[2];
 
-	data[0] = x_atom_utf8_string;
+	data[0] = atoms->utf8_string;
 	data[1] = XA_STRING;
 
 	XChangeProperty (x_dpy, e->requestor, e->property, target,
@@ -249,7 +480,7 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
 			 sizeof (data));
 	reply.xselection.property = e->property;
     }
-    else if (target == x_atom_multiple)
+    else if (target == atoms->multiple)
     {
 	if (e->property != None)
 	{
@@ -286,165 +517,181 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
     XSendEvent (x_dpy, e->requestor, False, 0, &reply);
 }
 
+/* This handles the events resulting from an XConvertSelection request. */
 - (void) notify_event:(XSelectionEvent *)e
 {
-    /* Someone sent us data we're waiting for. */
-
     Atom type;
-    int format, r, offset;
-    unsigned long nitems, bytes_after;
-    unsigned char *data, *buf;
-    NSString *string;
-
-    TRACE ();
+    struct propdata pdata;
+	
+    [self release_pending];
+    
+    puts ("NOTIFY EVENT");
+    if (None == e->property) {
+	puts("Nothing");
+	/* Nothing is selected. */
+	return;
+    }
 
-    if (e->target == x_atom_targets)
+    if (is_incr_type (e)) 
     {
-	/* Was trying to fetch the TARGETS property; it lists the
-	   formats supported by the selection owner. */
+	/*
+	 * This is an INCR-style transfer, which means that we 
+	 * will get the data after a series of PropertyNotify events.
+	 */
 
-	unsigned long *atoms;
-	int natoms;
-	int i, utf8_i = -1, string_i = -1;
+	puts("IS INCR");
 
-	if (e->property != None
-	    && (atoms = read_prop_32 (e->requestor,
-				      e->property, &natoms)) != NULL)
+	if (get_property (e->requestor, e->property, &pdata, /*Delete*/ True, &type)) 
 	{
-	    for (i = 0; i < natoms; i++)
-	    {
-		if (atoms[i] == XA_STRING)
-		    string_i = i;
-		else if (atoms[i] == x_atom_utf8_string)
-		    utf8_i = i;
-	    }
-	    XFree (atoms);
+	    return;
 	}
 
-	/* May as well try as STRING if nothing else, it can only
-	   fail, and it will help broken clients who don't support
-	   the TARGETS selection.. */
-
-	if (utf8_i >= 0)
-	    type = x_atom_utf8_string;
-	else
-	    type = XA_STRING;
+	free_propdata (&pdata);
 
-	XConvertSelection (x_dpy, e->selection, type,
-			   e->selection, e->requestor, e->time);
-	_pending_notify = YES;
-	return;
+	pending.requestor = e->requestor;
+	pending.selection = e->selection;
     }
+    else
+    {
+	if (get_property (e->requestor, e->property, &pdata, /*Delete*/ True, &type))
+	{
+	    return;
+	}
 
-    if (e->property == None)
-	return;				/* FIXME: notify pasteboard? */
-
-    /* Should be the data. Find out how big it is and what format it's in. */
-
-    r = XGetWindowProperty (x_dpy, e->requestor, e->property,
-			    0, 0, False, AnyPropertyType, &type,
-			    &format, &nitems, &bytes_after, &data);
-    if (r != Success)
-	return;
-
-    XFree (data);
-    if (type == None || format != 8)
-	return;
-
-    bytes_after += nitems;
-    
-    /* Read it into a buffer. */
+	puts("HANDLING NOW");
+	
+	/* We have the complete selection data.*/
+	[self handle_selection: e->selection type:type propdata:&pdata];
+    }
+}
 
-    buf = malloc (bytes_after + 1);
-    if (buf == NULL)
-	return;
+- (void) property_event:(XPropertyEvent *)e
+{
+    struct propdata pdata;
+    Atom type;
 
-    for (offset = 0; bytes_after > 0; offset += nitems)
+    if (None != pending.requestor && PropertyNewValue == e->state) 
     {
-	r = XGetWindowProperty (x_dpy, e->requestor, e->property,
-				offset / 4, (bytes_after / 4) + 1,
-				False, AnyPropertyType, &type,
-				&format, &nitems, &bytes_after, &data);
-	if (r != Success)
+	if (get_property (e->window, e->atom, &pdata, /*Delete*/ True, &type))
 	{
-	    free (buf);
 	    return;
 	}
 
-	memcpy (buf + offset, data, nitems);
-	XFree (data);
+	if (0 == pdata.length) 
+	{
+	    /* We completed the transfer. */
+	    [self handle_selection: pending.selection type: type propdata: &pending.propdata];
+	    pending.propdata = null_propdata;
+	    pending.requestor = None;
+	    pending.selection = None;
+	} 
+	else 
+	{
+	    [self append_to_pending: &pdata requestor: e->window];
+	}       
     }
-    buf[offset] = 0;
-    XDeleteProperty (x_dpy, e->requestor, e->property);
-
-    /* Convert to an NSString and write to the pasteboard. */
+}
 
-    if (type == XA_STRING)
-	string = [NSString stringWithCString:(char *) buf];
-    else /* if (type == x_atom_utf8_string) */
-	string = [NSString stringWithUTF8String:(char *) buf];
+- (void) handle_targets: (Atom)selection propdata:(struct propdata *)pdata
+{
+    Atom preferred = find_preferred (pdata);
 
-    free (buf);
+    if (None == preferred) 
+    {
+	/* 
+	 * This isn't required by the ICCCM, but some apps apparently 
+	 * don't respond to TARGETS properly.
+	 */
+	preferred = XA_STRING;
+    }
 
-    [_pasteboard setString:string forType:NSStringPboardType];
+    request_atom = preferred;
+    XConvertSelection (x_dpy, selection, preferred, selection,
+		       _selection_window, CurrentTime);    
 }
 
-
-/* NSPasteboard-required methods */
-
-static Bool
-selnotify_pred (Display *dpy, XEvent *e, XPointer arg)
+/* This handles the image/png type of selection (typically in CLIPBOARD). */
+- (void) handle_png: (struct propdata *)pdata
 {
-    return e->type == SelectionNotify;
+    /* TODO Use the NSPasteboard code I wrote that may work... */
 }
 
-- (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
+/* This handles the UTF8_STRING type of selection. */
+- (void) handle_utf8_string: (struct propdata *)pdata
 {
-    XEvent e;
-    Atom request;
+    size_t i;
+    unsigned char *p = pdata->data;
 
-    TRACE ();
-
-    /* Don't ask for the data yet, first find out which formats
-       the selection owner supports. */
-
-    request = x_atom_targets;
+    puts("HANDLE UTF8_STRING");
+    for (i = 0; i < pdata->length; ++i) {
+	printf("%c", p[i]);
+    }
+    puts("");
 
-again:
-    XConvertSelection (x_dpy, _proxied_selection, request,
-		       _proxied_selection, _selection_window, CurrentTime);
+    NSString *string = [[NSString alloc] initWithBytes:pdata->data length:pdata->length encoding:NSUTF8StringEncoding];
+    [_pasteboard setString:string forType:NSStringPboardType];
+    [string release];
+}
 
-    _pending_notify = YES;
+/* This handles the XA_STRING type, which should be in Latin-1. */
+- (void) handle_string: (struct propdata *)pdata
+{
+    puts("STRING");
 
-    /* Seems like we need to be synchronous here.. Actually, this really
-       sucks, since it means we could get deadlocked if people don't
-       respond to our request. So we need to implement our own timeout
-       code.. */
+    NSString *string = [[NSString alloc] initWithBytes:pdata->data length:pdata->length encoding:NSISOLatin1StringEncoding];
+    [_pasteboard setString:string forType:NSStringPboardType];
+    [string release];
+}
 
-    while (_pending_notify
-	   && IfEventWithTimeout (x_dpy, &e, 1, selnotify_pred, NULL))
+/* Warning: this frees the propdata in most cases. */
+- (void) handle_selection:(Atom)selection type:(Atom)type propdata:(struct propdata *)pdata
+{
+    if (request_atom == atoms->targets && type == atoms->atom)
+    {
+	[self handle_targets:selection propdata:pdata];
+    } 
+    else if (type == atoms->image_png)
     {
-	_pending_notify = NO;
-	[self notify_event:&e.xselection];
+	[self handle_png:pdata];
+    } 
+    else if (type == atoms->utf8_string) 
+    {
+	[self handle_utf8_string:pdata];
+    } 
+    else if (type == atoms->string)
+    {
+	[self handle_string:pdata];
+    } 
+    else
+    {
+	free_propdata(pdata);
     }
-
-    if (_pending_notify && request == x_atom_targets)
+    
+    if (selection == atoms->clipboard)
     {
-	/* App didn't respond to request for TARGETS selection. Let's
-	   try the STRING selection as a last resort.. Helps broken
-	   applications (e.g. nedit, see #3199867) */
-
-	request = XA_STRING;
-	goto again;
+	free_propdata(&request_data.propdata);
+	request_data.propdata = *pdata;
+	request_data.type = type;
+	
+	/* We greedily take the CLIPBOARD selection whenever it changes. */
+	XSetSelectionOwner (x_dpy, atoms->clipboard, _selection_window,
+			    CurrentTime);
+    }
+    else
+    {
+	free_propdata(pdata);
     }
-
-    _pending_notify = NO;
 }
 
+
+/* NSPasteboard-required methods */
+
 - (void) pasteboardChangedOwner:(NSPasteboard *)sender
 {
     TRACE ();
 
+    puts("PB changed owner");
+
     /* Right now we don't care with this. */
 }
 
@@ -467,6 +714,17 @@ again:
     _selection_window = XCreateSimpleWindow (x_dpy, DefaultRootWindow (x_dpy),
 					     0, 0, 1, 1, 0, pixel, pixel);
 
+    XSelectInput (x_dpy, _selection_window, PropertyChangeMask);
+
+    request_atom = None;
+
+    init_propdata (&pending.propdata);
+    pending.requestor = None;
+    pending.selection = None;
+
+    init_propdata (&request_data.propdata);
+    request_data.type = None;
+    
     return self;
 }
 
@@ -479,12 +737,15 @@ again:
     [_known_types release];
     _known_types = nil;
 
-    if (_selection_window != 0)
+    if (None != _selection_window)
     {
 	XDestroyWindow (x_dpy, _selection_window);
-	_selection_window = 0;
+	_selection_window = None;
     }
 
+    free_propdata (&pending.propdata);
+    free_propdata (&request_data.propdata);
+
     [super dealloc];
 }
 


More information about the xorg-commit mailing list