changeset 38386:6bf3530c700d

(x_decline_selection_request): Handle errors caused by receivers that have vanished. (TRACE0, TRACE1, TRACE2): New macros, defined depending on TRACE_SELECTION. Replace fprintfs in #if 0 with TRACE macros to facilitate debugging. Add additional trace statements. (toplevel): Add prototypes for file-local functions. (x_atom_to_symbol): Remove DPYINFO parameter.
author Gerd Moellmann <gerd@gnu.org>
date Thu, 12 Jul 2001 14:38:51 +0000
parents 0d5fe69d27c5
children 293a8b219972
files src/xselect.c
diffstat 1 files changed, 169 insertions(+), 80 deletions(-) [+]
line wrap: on
line diff
--- a/src/xselect.c	Thu Jul 12 10:17:47 2001 +0000
+++ b/src/xselect.c	Thu Jul 12 14:38:51 2001 +0000
@@ -1,5 +1,6 @@
 /* X Selection processing for Emacs.
-   Copyright (C) 1993, 1994, 1995, 1996, 1997, 2000 Free Software Foundation.
+   Copyright (C) 1993, 1994, 1995, 1996, 1997, 2000, 2001
+   Free Software Foundation.
 
 This file is part of GNU Emacs.
 
@@ -33,6 +34,61 @@
 #include "process.h"
 #include "composite.h"
 
+struct prop_location;
+
+static Lisp_Object x_atom_to_symbol P_ ((Display *dpy, Atom atom));
+static Atom symbol_to_x_atom P_ ((struct x_display_info *, Display *,
+				  Lisp_Object));
+static void x_own_selection P_ ((Lisp_Object, Lisp_Object));
+static Lisp_Object x_get_local_selection P_ ((Lisp_Object, Lisp_Object));
+static void x_decline_selection_request P_ ((struct input_event *));
+static Lisp_Object x_selection_request_lisp_error P_ ((Lisp_Object));
+static Lisp_Object queue_selection_requests_unwind P_ ((Lisp_Object));
+static Lisp_Object some_frame_on_display P_ ((struct x_display_info *));
+static void x_reply_selection_request P_ ((struct input_event *, int,
+					   unsigned char *, int, Atom));
+static int waiting_for_other_props_on_window P_ ((Display *, Window));
+static struct prop_location *expect_property_change P_ ((Display *, Window,
+							 Atom, int));
+static void unexpect_property_change P_ ((struct prop_location *));
+static Lisp_Object wait_for_property_change_unwind P_ ((Lisp_Object));
+static void wait_for_property_change P_ ((struct prop_location *));
+static Lisp_Object x_get_foreign_selection P_ ((Lisp_Object, Lisp_Object));
+static void x_get_window_property P_ ((Display *, Window, Atom,
+				       unsigned char **, int *,
+				       Atom *, int *, unsigned long *, int));
+static void receive_incremental_selection P_ ((Display *, Window, Atom,
+					       Lisp_Object, unsigned,
+					       unsigned char **, int *,
+					       Atom *, int *, unsigned long *));
+static Lisp_Object x_get_window_property_as_lisp_data P_ ((Display *,
+							   Window, Atom,
+							   Lisp_Object, Atom));
+static Lisp_Object selection_data_to_lisp_data P_ ((Display *, unsigned char *,
+						    int, Atom, int));
+static void lisp_data_to_selection_data P_ ((Display *, Lisp_Object,
+					     unsigned char **, Atom *,
+					     unsigned *, int *, int *));
+static Lisp_Object clean_local_selection_data P_ ((Lisp_Object));
+static void initialize_cut_buffers P_ ((Display *, Window));
+
+
+/* Printing traces to stderr.  */
+
+#ifdef TRACE_SELECTION
+#define TRACE0(fmt) \
+  fprintf (stderr, "%d: " fmt "\n", getpid ())
+#define TRACE1(fmt, a0) \
+  fprintf (stderr, "%d: " fmt "\n", getpid (), a0)
+#define TRACE2(fmt, a0, a1) \
+  fprintf (stderr, "%d: " fmt "\n", getpid (), a0, a1)
+#else
+#define TRACE0(fmt)		(void) 0
+#define TRACE1(fmt, a0)		(void) 0
+#define TRACE2(fmt, a0, a1)	(void) 0
+#endif
+
+
 #define CUT_BUFFER_SUPPORT
 
 Lisp_Object QPRIMARY, QSECONDARY, QSTRING, QINTEGER, QCLIPBOARD, QTIMESTAMP,
@@ -142,9 +198,7 @@
 #endif
   if (!SYMBOLP (sym)) abort ();
 
-#if 0
-  fprintf (stderr, " XInternAtom %s\n", (char *) XSYMBOL (sym)->name->data);
-#endif
+  TRACE1 (" XInternAtom %s", (char *) XSYMBOL (sym)->name->data);
   BLOCK_INPUT;
   val = XInternAtom (display, (char *) XSYMBOL (sym)->name->data, False);
   UNBLOCK_INPUT;
@@ -156,14 +210,17 @@
    and calls to intern whenever possible.  */
 
 static Lisp_Object
-x_atom_to_symbol (dpyinfo, display, atom)
-     struct x_display_info *dpyinfo;
-     Display *display;
+x_atom_to_symbol (dpy, atom)
+     Display *dpy;
      Atom atom;
 {
+  struct x_display_info *dpyinfo;
   char *str;
   Lisp_Object val;
-  if (! atom) return Qnil;
+  
+  if (! atom)
+    return Qnil;
+  
   switch (atom)
     {
     case XA_PRIMARY:
@@ -196,6 +253,7 @@
 #endif
     }
 
+  dpyinfo = x_display_info_for_display (dpy);
   if (atom == dpyinfo->Xatom_CLIPBOARD)
     return QCLIPBOARD;
   if (atom == dpyinfo->Xatom_TIMESTAMP)
@@ -218,11 +276,9 @@
     return QNULL;
 
   BLOCK_INPUT;
-  str = XGetAtomName (display, atom);
+  str = XGetAtomName (dpy, atom);
   UNBLOCK_INPUT;
-#if 0
-  fprintf (stderr, " XGetAtomName --> %s\n", str);
-#endif
+  TRACE1 ("XGetAtomName --> %s", str);
   if (! str) return Qnil;
   val = intern (str);
   BLOCK_INPUT;
@@ -411,6 +467,8 @@
      struct input_event *event;
 {
   XSelectionEvent reply;
+  int count;
+  
   reply.type = SelectionNotify;
   reply.display = SELECTION_EVENT_DISPLAY (event);
   reply.requestor = SELECTION_EVENT_REQUESTOR (event);
@@ -419,10 +477,13 @@
   reply.target = SELECTION_EVENT_TARGET (event);
   reply.property = None;
 
+  /* The reason for the error may be that the receiver has
+     died in the meantime.  Handle that case.  */
   BLOCK_INPUT;
-  XSendEvent (reply.display, reply.requestor, False, 0L,
-	      (XEvent *) &reply);
+  count = x_catch_errors (reply.display);
+  XSendEvent (reply.display, reply.requestor, False, 0L, (XEvent *) &reply);
   XFlush (reply.display);
+  x_uncatch_errors (reply.display, count);
   UNBLOCK_INPUT;
 }
 
@@ -553,9 +614,7 @@
   if (bytes_remaining <= max_bytes)
     {
       /* Send all the data at once, with minimal handshaking.  */
-#if 0
-      fprintf (stderr,"\nStoring all %d\n", bytes_remaining);
-#endif
+      TRACE1 ("Sending all %d bytes", bytes_remaining);
       XChangeProperty (display, window, reply.property, type, format,
 		       PropModeReplace, data, size);
       /* At this point, the selection was successfully stored; ack it.  */
@@ -583,17 +642,21 @@
 
       if (x_window_to_frame (dpyinfo, window)) /* #### debug */
 	error ("Attempt to transfer an INCR to ourself!");
-#if 0
-      fprintf (stderr, "\nINCR %d\n", bytes_remaining);
-#endif
+      
+      TRACE2 ("Start sending %d bytes incrementally (%s)",
+	      bytes_remaining,  XGetAtomName (display, reply.property));
       wait_object = expect_property_change (display, window, reply.property,
 					    PropertyDelete);
 
+      TRACE1 ("Set %s to number of bytes to send",
+	      XGetAtomName (display, reply.property));
       XChangeProperty (display, window, reply.property, dpyinfo->Xatom_INCR,
 		       32, PropModeReplace,
 		       (unsigned char *) &bytes_remaining, 1);
       XSelectInput (display, window, PropertyChangeMask);
+      
       /* Tell 'em the INCR data is there...  */
+      TRACE0 ("Send SelectionNotify event");
       XSendEvent (display, window, False, 0L, (XEvent *) &reply);
       XFlush (display);
 
@@ -603,8 +666,13 @@
       /* First, wait for the requester to ack by deleting the property.
 	 This can run random lisp code (process handlers) or signal.  */
       if (! had_errors)
-	wait_for_property_change (wait_object);
+	{
+	  TRACE1 ("Waiting for ACK (deletion of %s)",
+		  XGetAtomName (display, reply.property));
+	  wait_for_property_change (wait_object);
+	}
 
+      TRACE0 ("Got ACK");
       while (bytes_remaining)
 	{
 	  int i = ((bytes_remaining < max_bytes)
@@ -616,9 +684,11 @@
 	  wait_object
 	    = expect_property_change (display, window, reply.property,
 				      PropertyDelete);
-#if 0
-	  fprintf (stderr,"  INCR adding %d\n", i);
-#endif
+
+	  TRACE1 ("Sending increment of %d bytes", i);
+	  TRACE1 ("Set %s to increment data",
+		  XGetAtomName (display, reply.property));
+	  
 	  /* Append the next chunk of data to the property.  */
 	  XChangeProperty (display, window, reply.property, type, format,
 			   PropModeAppend, data, i / format_bytes);
@@ -632,21 +702,23 @@
 	    break;
 
 	  /* Now wait for the requester to ack this chunk by deleting the
-	     property.	 This can run random lisp code or signal.
-	   */
+	     property.	 This can run random lisp code or signal.  */
+	  TRACE1 ("Waiting for increment ACK (deletion of %s)",
+		  XGetAtomName (display, reply.property));
 	  wait_for_property_change (wait_object);
 	}
-      /* Now write a zero-length chunk to the property to tell the requester
-	 that we're done.  */
-#if 0
-      fprintf (stderr,"  INCR done\n");
-#endif
+      
+      /* Now write a zero-length chunk to the property to tell the
+	 requester that we're done.  */
       BLOCK_INPUT;
       if (! waiting_for_other_props_on_window (display, window))
 	XSelectInput (display, window, 0L);
 
+      TRACE1 ("Set %s to a 0-length chunk to indicate EOF",
+	      XGetAtomName (display, reply.property));
       XChangeProperty (display, window, reply.property, type, format,
 		       PropModeReplace, data, 0);
+      TRACE0 ("Done sending incrementally");
     }
 
   /* The window we're communicating with may have been deleted
@@ -685,8 +757,7 @@
 
   GCPRO3 (local_selection_data, converted_selection, target_symbol);
 
-  selection_symbol = x_atom_to_symbol (dpyinfo,
-				       SELECTION_EVENT_DISPLAY (event),
+  selection_symbol = x_atom_to_symbol (SELECTION_EVENT_DISPLAY (event),
 				       SELECTION_EVENT_SELECTION (event));
 
   local_selection_data = assq_no_quit (selection_symbol, Vselection_alist);
@@ -717,7 +788,7 @@
   selection_request_dpyinfo = dpyinfo;
   record_unwind_protect (x_selection_request_lisp_error, Qnil);
 
-  target_symbol = x_atom_to_symbol (dpyinfo, SELECTION_EVENT_DISPLAY (event),
+  target_symbol = x_atom_to_symbol (SELECTION_EVENT_DISPLAY (event),
 				    SELECTION_EVENT_TARGET (event));
 
 #if 0 /* #### MULTIPLE doesn't work yet */
@@ -805,7 +876,7 @@
       }
   UNBLOCK_INPUT;
 
-  selection_symbol = x_atom_to_symbol (dpyinfo, display, selection);
+  selection_symbol = x_atom_to_symbol (display, selection);
 
   local_selection_data = assq_no_quit (selection_symbol, Vselection_alist);
 
@@ -945,8 +1016,7 @@
      Atom property;
      int state;
 {
-  struct prop_location *pl
-    = (struct prop_location *) xmalloc (sizeof (struct prop_location));
+  struct prop_location *pl = (struct prop_location *) xmalloc (sizeof *pl);
   pl->identifier = ++prop_location_identifier;
   pl->display = display;
   pl->window = window;
@@ -1021,10 +1091,14 @@
     {
       secs = x_selection_timeout / 1000;
       usecs = (x_selection_timeout % 1000) * 1000;
+      TRACE2 ("  Waiting %d secs, %d usecs", secs, usecs);
       wait_reading_process_input (secs, usecs, property_change_reply, 0);
 
       if (NILP (XCAR (property_change_reply)))
-	error ("Timed out waiting for property-notify event");
+	{
+	  TRACE0 ("  Timed out");
+	  error ("Timed out waiting for property-notify event");
+	}
     }
 
   unbind_to (count, Qnil);
@@ -1037,6 +1111,7 @@
      XPropertyEvent *event;
 {
   struct prop_location *prev = 0, *rest = property_change_wait_list;
+
   while (rest)
     {
       if (rest->property == event->atom
@@ -1044,13 +1119,9 @@
 	  && rest->display == event->display
 	  && rest->desired_state == event->state)
 	{
-#if 0
-	  fprintf (stderr, "Saw expected prop-%s on %s\n",
-		   (event->state == PropertyDelete ? "delete" : "change"),
-		   (char *) XSYMBOL (x_atom_to_symbol (dpyinfo, event->display,
-						       event->atom))
-		   ->name->data);
-#endif
+	  TRACE2 ("Expected %s of property %s",
+		  (event->state == PropertyDelete ? "deletion" : "change"),
+		  XGetAtomName (event->display, event->atom));
 
 	  rest->arrived = 1;
 
@@ -1066,16 +1137,10 @@
 	  xfree (rest);
 	  return;
 	}
+      
       prev = rest;
       rest = rest->next;
     }
-#if 0
-  fprintf (stderr, "Saw UNexpected prop-%s on %s\n",
-	   (event->state == PropertyDelete ? "delete" : "change"),
-	   (char *) XSYMBOL (x_atom_to_symbol (dpyinfo,
-					       event->display, event->atom))
-	   ->name->data);
-#endif
 }
 
 
@@ -1160,7 +1225,13 @@
     type_atom = symbol_to_x_atom (dpyinfo, display, target_type);
 
   BLOCK_INPUT;
+  
   count = x_catch_errors (display);
+  
+  TRACE2 ("Get selection %s, type %s",
+	  XGetAtomName (display, type_atom),
+	  XGetAtomName (display, target_property));
+
   XConvertSelection (display, selection_atom, type_atom, target_property,
 		     requestor_window, requestor_time);
   XFlush (display);
@@ -1187,7 +1258,9 @@
   /* This allows quits.  Also, don't wait forever.  */
   secs = x_selection_timeout / 1000;
   usecs = (x_selection_timeout % 1000) * 1000;
+  TRACE1 ("  Start waiting %d secs for SelectionNotify", secs);
   wait_reading_process_input (secs, usecs, reading_selection_reply, 0);
+  TRACE1 ("  Got event = %d", !NILP (XCAR (reading_selection_reply)));
 
   BLOCK_INPUT;
   x_check_errors (display, "Cannot get selection: %s");
@@ -1230,9 +1303,12 @@
   unsigned char *tmp_data = 0;
   int result;
   int buffer_size = SELECTION_QUANTUM (display);
-  if (buffer_size > MAX_SELECTION_QUANTUM) buffer_size = MAX_SELECTION_QUANTUM;
+  
+  if (buffer_size > MAX_SELECTION_QUANTUM)
+    buffer_size = MAX_SELECTION_QUANTUM;
   
   BLOCK_INPUT;
+  
   /* First probe the thing to find out how big it is.  */
   result = XGetWindowProperty (display, window, property,
 			       0L, 0L, False, AnyPropertyType,
@@ -1246,6 +1322,7 @@
       *bytes_ret = 0;
       return;
     }
+  
   /* This was allocated by Xlib, so use XFree.  */
   XFree ((char *) tmp_data);
   
@@ -1261,7 +1338,7 @@
   /* Now read, until we've gotten it all.  */
   while (bytes_remaining)
     {
-#if 0
+#ifdef TRACE_SELECTION
       int last = bytes_remaining;
 #endif
       result
@@ -1271,17 +1348,20 @@
 			      AnyPropertyType,
 			      actual_type_ret, actual_format_ret,
 			      actual_size_ret, &bytes_remaining, &tmp_data);
-#if 0
-      fprintf (stderr, "<< read %d\n", last-bytes_remaining);
-#endif
+
+      TRACE2 ("Read %ld bytes from property %s",
+	      last - bytes_remaining,
+	      XGetAtomName (display, property));
+
       /* If this doesn't return Success at this point, it means that
 	 some clod deleted the selection while we were in the midst of
-	 reading it.  Deal with that, I guess....
-       */
-      if (result != Success) break;
+	 reading it.  Deal with that, I guess.... */
+      if (result != Success)
+	break;
       *actual_size_ret *= *actual_format_ret / 8;
       bcopy (tmp_data, (*data_ret) + offset, *actual_size_ret);
       offset += *actual_size_ret;
+      
       /* This was allocated by Xlib, so use XFree.  */
       XFree ((char *) tmp_data);
     }
@@ -1312,9 +1392,8 @@
   struct prop_location *wait_object;
   *size_bytes_ret = min_size_bytes;
   *data_ret = (unsigned char *) xmalloc (*size_bytes_ret);
-#if 0
-  fprintf (stderr, "\nread INCR %d\n", min_size_bytes);
-#endif
+
+  TRACE1 ("Read %d bytes incrementally", min_size_bytes);
 
   /* At this point, we have read an INCR property.
      Delete the property to ack it.
@@ -1326,7 +1405,11 @@
    */
   BLOCK_INPUT;
   XSelectInput (display, window, STANDARD_EVENT_SET | PropertyChangeMask);
+  TRACE1 ("  Delete property %s",
+	  XSYMBOL (x_atom_to_symbol (display, property))->name->data);
   XDeleteProperty (display, window, property);
+  TRACE1 ("  Expect new value of property %s",
+	  XSYMBOL (x_atom_to_symbol (display, property))->name->data);
   wait_object = expect_property_change (display, window, property,
 					PropertyNewValue);
   XFlush (display);
@@ -1336,20 +1419,24 @@
     {
       unsigned char *tmp_data;
       int tmp_size_bytes;
+
+      TRACE0 ("  Wait for property change");
       wait_for_property_change (wait_object);
+      
       /* expect it again immediately, because x_get_window_property may
 	 .. no it won't, I don't get it.
-	 .. Ok, I get it now, the Xt code that implements INCR is broken.
-       */
+	 .. Ok, I get it now, the Xt code that implements INCR is broken. */
+      TRACE0 ("  Get property value");
       x_get_window_property (display, window, property,
 			     &tmp_data, &tmp_size_bytes,
 			     type_ret, format_ret, size_ret, 1);
 
+      TRACE1 ("  Read increment of %d bytes", tmp_size_bytes);
+
       if (tmp_size_bytes == 0) /* we're done */
 	{
-#if 0
-	  fprintf (stderr, "  read INCR done\n");
-#endif
+	  TRACE0 ("Done reading incrementally");
+
 	  if (! waiting_for_other_props_on_window (display, window))
 	    XSelectInput (display, window, STANDARD_EVENT_SET);
 	  unexpect_property_change (wait_object);
@@ -1360,31 +1447,29 @@
 	}
 
       BLOCK_INPUT;
+      TRACE1 ("  ACK by deleting property %s",
+	      XGetAtomName (display, property));
       XDeleteProperty (display, window, property);
       wait_object = expect_property_change (display, window, property,
 					    PropertyNewValue);
       XFlush (display);
       UNBLOCK_INPUT;
 
-#if 0
-      fprintf (stderr, "  read INCR %d\n", tmp_size_bytes);
-#endif
       if (*size_bytes_ret < offset + tmp_size_bytes)
 	{
-#if 0
-	  fprintf (stderr, "  read INCR realloc %d -> %d\n",
-		   *size_bytes_ret, offset + tmp_size_bytes);
-#endif
 	  *size_bytes_ret = offset + tmp_size_bytes;
 	  *data_ret = (unsigned char *) xrealloc (*data_ret, *size_bytes_ret);
 	}
+      
       bcopy (tmp_data, (*data_ret) + offset, tmp_size_bytes);
       offset += tmp_size_bytes;
+      
       /* Use xfree, not XFree, because x_get_window_property
 	 calls xmalloc itself.  */
       xfree (tmp_data);
     }
 }
+
 
 /* Once a requested selection is "ready" (we got a SelectionNotify event),
    fetch value from property PROPERTY of X window WINDOW on display DISPLAY.
@@ -1407,6 +1492,8 @@
   Lisp_Object val;
   struct x_display_info *dpyinfo = x_display_info_for_display (display);
 
+  TRACE0 ("Reading selection data");
+
   x_get_window_property (display, window, property, &data, &bytes,
 			 &actual_type, &actual_format, &actual_size, 1);
   if (! data)
@@ -1421,12 +1508,12 @@
 	       ? Fcons (build_string ("selection owner couldn't convert"),
 			actual_type
 			? Fcons (target_type,
-				 Fcons (x_atom_to_symbol (dpyinfo, display,
+				 Fcons (x_atom_to_symbol (display,
 							  actual_type),
 					Qnil))
 			: Fcons (target_type, Qnil))
 	       : Fcons (build_string ("no selection"),
-			Fcons (x_atom_to_symbol (dpyinfo, display,
+			Fcons (x_atom_to_symbol (display,
 						 selection_atom),
 			       Qnil)));
     }
@@ -1448,6 +1535,7 @@
     }
 
   BLOCK_INPUT;
+  TRACE1 ("  Delete property %s", XGetAtomName (display, property));
   XDeleteProperty (display, window, property);
   XFlush (display);
   UNBLOCK_INPUT;
@@ -1574,14 +1662,14 @@
     {
       int i;
       if (size == sizeof (Atom))
-	return x_atom_to_symbol (dpyinfo, display, *((Atom *) data));
+	return x_atom_to_symbol (display, *((Atom *) data));
       else
 	{
 	  Lisp_Object v = Fmake_vector (make_number (size / sizeof (Atom)),
 					make_number (0));
 	  for (i = 0; i < size / sizeof (Atom); i++)
 	    Faset (v, make_number (i),
-		   x_atom_to_symbol (dpyinfo, display, ((Atom *) data) [i]));
+		   x_atom_to_symbol (display, ((Atom *) data) [i]));
 	  return v;
 	}
     }
@@ -1853,6 +1941,7 @@
   if (event->selection != reading_which_selection)
     return;
 
+  TRACE0 ("Received SelectionNotify");
   XCAR (reading_selection_reply)
     = (event->property != 0 ? Qt : Qlambda);
 }
@@ -2121,7 +2210,7 @@
   if (format != 8 || type != XA_STRING)
     Fsignal (Qerror,
 	     Fcons (build_string ("cut buffer doesn't contain 8-bit data"),
-		    Fcons (x_atom_to_symbol (dpyinfo, display, type),
+		    Fcons (x_atom_to_symbol (display, type),
 			   Fcons (make_number (format), Qnil))));
 
   ret = (bytes ? make_string ((char *) data, bytes) : Qnil);