emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master db69249b76: Handle selection transfer errors earlier


From: Po Lu
Subject: master db69249b76: Handle selection transfer errors earlier
Date: Thu, 15 Dec 2022 07:07:10 -0500 (EST)

branch: master
commit db69249b761a80158c1469b2a169d6f5c8509ae1
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>

    Handle selection transfer errors earlier
    
    * src/xselect.c (x_decline_selection_request, struct transfer)
    (struct x_selection_request, x_cancel_selection_transfer)
    (x_start_selection_transfer, x_continue_selection_transfer):
    Give the right serial to x_ignore_errors_for_next_request.
    (x_handle_selection_error): New function.
    (x_reply_selection_request): Give the right serial to
    x_ignore_errors_for_next_request.
    * src/xterm.c (x_ignore_errors_for_next_request): New arg
    `selection_serial'.  All callers changed.
    (x_error_handler): Call selection error handler.
    * src/xterm.h (struct x_failable_request): New field
    `selection_serial'.
---
 src/xfns.c    |  2 +-
 src/xselect.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++--------
 src/xterm.c   | 70 +++++++++++++++++++++++++++++++++----------------------
 src/xterm.h   |  7 +++++-
 4 files changed, 114 insertions(+), 39 deletions(-)

diff --git a/src/xfns.c b/src/xfns.c
index 2bf282fd24..668f711bdb 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -1367,7 +1367,7 @@ x_set_mouse_color (struct frame *f, Lisp_Object arg, 
Lisp_Object oldval)
         XCreateFontCursor is not a request that waits for a reply,
         and as such can return IDs that will not actually be used by
         the server.  */
-      x_ignore_errors_for_next_request (FRAME_DISPLAY_INFO (f));
+      x_ignore_errors_for_next_request (FRAME_DISPLAY_INFO (f), 0);
 
       /* Free any successfully created cursors.  */
       for (i = 0; i < mouse_cursor_max; i++)
diff --git a/src/xselect.c b/src/xselect.c
index 191f077033..05548be311 100644
--- a/src/xselect.c
+++ b/src/xselect.c
@@ -460,7 +460,7 @@ x_decline_selection_request (struct selection_input_event 
*event)
   /* The reason for the error may be that the receiver has
      died in the meantime.  Handle that case.  */
   block_input ();
-  x_ignore_errors_for_next_request (dpyinfo);
+  x_ignore_errors_for_next_request (dpyinfo, 0);
   XSendEvent (dpyinfo->display, reply->requestor,
              False, 0, &reply_base);
   x_stop_ignoring_errors (dpyinfo);
@@ -523,6 +523,9 @@ struct transfer
   /* The atimer for the timeout.  */
   struct atimer *timeout;
 
+  /* The selection serial.  */
+  unsigned int serial;
+
   /* Flags.  */
   int flags;
 };
@@ -543,6 +546,9 @@ struct x_selection_request
   /* Linked list of the above (in support of MULTIPLE targets).  */
   struct selection_data *converted_selections;
 
+  /* The serial used to handle X errors.  */
+  unsigned int serial;
+
   /* Whether or not conversion was successful.  */
   bool converted;
 };
@@ -557,6 +563,10 @@ struct x_selection_request *selection_request_stack;
 
 struct transfer outstanding_transfers;
 
+/* A counter for selection serials.  */
+
+static unsigned int selection_serial;
+
 
 
 struct prop_location
@@ -793,7 +803,7 @@ x_cancel_selection_transfer (struct transfer *transfer)
       /* Ignore errors generated by the change window request in case
         the window has gone away.  */
       block_input ();
-      x_ignore_errors_for_next_request (transfer->dpyinfo);
+      x_ignore_errors_for_next_request (transfer->dpyinfo, 0);
       XSelectInput (transfer->dpyinfo->display,
                    transfer->requestor, NoEventMask);
       x_stop_ignoring_errors (transfer->dpyinfo);
@@ -885,12 +895,23 @@ x_start_selection_transfer (struct x_display_info 
*dpyinfo, Window requestor,
       transfer->next->last = transfer;
       transfer->last->next = transfer;
 
+      /* Find a valid (non-zero) serial for the selection transfer.
+        Any asynchronously trapped errors will then cause the
+        selection transfer to be cancelled.  */
+      transfer->serial = (++selection_serial
+                         ? selection_serial
+                         : ++selection_serial);
+
       /* Now, write the INCR property to begin incremental selection
         transfer.  offset is currently 0.  */
 
       data_size = selection_data_size (&transfer->data);
 
-      x_ignore_errors_for_next_request (dpyinfo);
+      /* Set SELECTED_EVENTS before the actual XSelectInput
+        request.  */
+      transfer->flags |= SELECTED_EVENTS;
+
+      x_ignore_errors_for_next_request (dpyinfo, transfer->serial);
       XChangeProperty (dpyinfo->display, requestor,
                       transfer->data.property,
                       dpyinfo->Xatom_INCR, 32, PropModeReplace,
@@ -903,7 +924,6 @@ x_start_selection_transfer (struct x_display_info *dpyinfo, 
Window requestor,
          manager) asks Emacs for selection data, things will subtly go
          wrong.  */
       XSelectInput (dpyinfo->display, requestor, PropertyChangeMask);
-      transfer->flags |= SELECTED_EVENTS;
       x_stop_ignoring_errors (dpyinfo);
     }
   else
@@ -917,7 +937,7 @@ x_start_selection_transfer (struct x_display_info *dpyinfo, 
Window requestor,
              " %zu elements directly to requestor window",
              remaining);
 
-      x_ignore_errors_for_next_request (dpyinfo);
+      x_ignore_errors_for_next_request (dpyinfo, 0);
       XChangeProperty (dpyinfo->display, requestor,
                       transfer->data.property,
                       transfer->data.type,
@@ -951,7 +971,7 @@ x_continue_selection_transfer (struct transfer *transfer)
         signal EOF and remove the transfer.  */
       TRACE0 (" x_continue_selection_transfer: writing 0 items to"
              " indicate EOF");
-      x_ignore_errors_for_next_request (transfer->dpyinfo);
+      x_ignore_errors_for_next_request (transfer->dpyinfo, 0);
       XChangeProperty (transfer->dpyinfo->display,
                       transfer->requestor,
                       transfer->data.property,
@@ -970,7 +990,10 @@ x_continue_selection_transfer (struct transfer *transfer)
              "; current offset is %zu", remaining, transfer->offset);
       eassert (remaining <= INT_MAX);
 
-      x_ignore_errors_for_next_request (transfer->dpyinfo);
+      transfer->offset += remaining;
+
+      x_ignore_errors_for_next_request (transfer->dpyinfo,
+                                       transfer->serial);
       XChangeProperty (transfer->dpyinfo->display,
                       transfer->requestor,
                       transfer->data.property,
@@ -979,7 +1002,6 @@ x_continue_selection_transfer (struct transfer *transfer)
                       PropModeReplace, xdata,
                       remaining);
       x_stop_ignoring_errors (transfer->dpyinfo);
-      transfer->offset += remaining;
     }
 }
 
@@ -999,6 +1021,40 @@ x_remove_selection_transfers (struct x_display_info 
*dpyinfo)
     }
 }
 
+/* Handle an X error generated trying to write to a window.  SERIAL
+   identifies the outstanding incremental selection transfer, which is
+   immediately removed.  */
+
+void
+x_handle_selection_error (unsigned int serial, XErrorEvent *error)
+{
+  struct transfer *next, *last;
+
+  if (error->error_code != BadWindow)
+    /* The error was not caused by the window going away.  As such,
+       Emacs must deselect for PropertyChangeMask from the requestor
+       window, which isn't safe here.  Return and wait for the timeout
+       to run.  */
+    return;
+
+  next = outstanding_transfers.next;
+  while (next != &outstanding_transfers)
+    {
+      last = next;
+      next = next->next;
+
+      if (last->serial == serial)
+       {
+         /* Clear SELECTED_EVENTS, so x_cancel_selection_transfer
+            will not make X requests.  That is unsafe inside an error
+            handler, and unnecessary because the window has already
+            gone.  */
+         last->flags &= ~SELECTED_EVENTS;
+         x_cancel_selection_transfer (last);
+       }
+    }
+}
+
 /* Send the reply to a selection request event EVENT.  */
 
 static void
@@ -1033,7 +1089,7 @@ x_reply_selection_request (struct selection_input_event 
*event,
 
   /* Send the SelectionNotify event to the requestor, telling it that
      the property data has arrived.  */
-  x_ignore_errors_for_next_request (dpyinfo);
+  x_ignore_errors_for_next_request (dpyinfo, 0);
   XSendEvent (dpyinfo->display, SELECTION_EVENT_REQUESTOR (event),
              False, NoEventMask, &message);
   x_stop_ignoring_errors (dpyinfo);
diff --git a/src/xterm.c b/src/xterm.c
index 968dec6fbd..278a8dda10 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -2587,7 +2587,7 @@ xm_send_drop_message (struct x_display_info *dpyinfo, 
Window source,
   *((uint32_t *) &msg.xclient.data.b[12]) = dmsg->index_atom;
   *((uint32_t *) &msg.xclient.data.b[16]) = dmsg->source_window;
 
-  x_ignore_errors_for_next_request (dpyinfo);
+  x_ignore_errors_for_next_request (dpyinfo, 0);
   XSendEvent (dpyinfo->display, target, False, NoEventMask, &msg);
   x_stop_ignoring_errors (dpyinfo);
 }
@@ -2614,7 +2614,7 @@ xm_send_top_level_enter_message (struct x_display_info 
*dpyinfo, Window source,
   msg.xclient.data.b[18] = 0;
   msg.xclient.data.b[19] = 0;
 
-  x_ignore_errors_for_next_request (dpyinfo);
+  x_ignore_errors_for_next_request (dpyinfo, 0);
   XSendEvent (dpyinfo->display, target, False, NoEventMask, &msg);
   x_stop_ignoring_errors (dpyinfo);
 }
@@ -2645,7 +2645,7 @@ xm_send_drag_motion_message (struct x_display_info 
*dpyinfo, Window source,
   msg.xclient.data.b[18] = 0;
   msg.xclient.data.b[19] = 0;
 
-  x_ignore_errors_for_next_request (dpyinfo);
+  x_ignore_errors_for_next_request (dpyinfo, 0);
   XSendEvent (dpyinfo->display, target, False, NoEventMask, &msg);
   x_stop_ignoring_errors (dpyinfo);
 }
@@ -2704,7 +2704,7 @@ xm_send_top_level_leave_message (struct x_display_info 
*dpyinfo, Window source,
   msg.xclient.data.b[18] = 0;
   msg.xclient.data.b[19] = 0;
 
-  x_ignore_errors_for_next_request (dpyinfo);
+  x_ignore_errors_for_next_request (dpyinfo, 0);
   XSendEvent (dpyinfo->display, target, False, NoEventMask, &msg);
   x_stop_ignoring_errors (dpyinfo);
 }
@@ -2999,7 +2999,7 @@ x_dnd_free_toplevels (bool display_alive)
       if (n_windows)
        {
          eassume (dpyinfo);
-         x_ignore_errors_for_next_request (dpyinfo);
+         x_ignore_errors_for_next_request (dpyinfo, 0);
 
          for (i = 0; i < n_windows; ++i)
            {
@@ -3369,7 +3369,7 @@ x_dnd_compute_toplevels (struct x_display_info *dpyinfo)
 
          if (dpyinfo->xshape_supported_p)
            {
-             x_ignore_errors_for_next_request (dpyinfo);
+             x_ignore_errors_for_next_request (dpyinfo, 0);
              XShapeSelectInput (dpyinfo->display,
                                 toplevels[i],
                                 ShapeNotifyMask);
@@ -3534,7 +3534,7 @@ x_dnd_compute_toplevels (struct x_display_info *dpyinfo)
            }
 #endif
 
-         x_ignore_errors_for_next_request (dpyinfo);
+         x_ignore_errors_for_next_request (dpyinfo, 0);
          XSelectInput (dpyinfo->display, toplevels[i],
                        (attrs.your_event_mask
                         | StructureNotifyMask
@@ -4059,7 +4059,7 @@ x_dnd_do_unsupported_drop (struct x_display_info *dpyinfo,
   event.xbutton.type = ButtonPress;
   event.xbutton.time = before + 1;
 
-  x_ignore_errors_for_next_request (dpyinfo);
+  x_ignore_errors_for_next_request (dpyinfo, 0);
   XSendEvent (dpyinfo->display, child,
              True, ButtonPressMask, &event);
 
@@ -4571,7 +4571,7 @@ x_dnd_send_enter (struct frame *f, Window target, Window 
toplevel,
      so we don't have to set it again.  */
   x_dnd_init_type_lists = true;
 
-  x_ignore_errors_for_next_request (dpyinfo);
+  x_ignore_errors_for_next_request (dpyinfo, 0);
   XSendEvent (FRAME_X_DISPLAY (f), target, False, NoEventMask, &msg);
   x_stop_ignoring_errors (dpyinfo);
 }
@@ -4643,7 +4643,7 @@ x_dnd_send_position (struct frame *f, Window target, 
Window toplevel,
            return;
        }
 
-      x_ignore_errors_for_next_request (dpyinfo);
+      x_ignore_errors_for_next_request (dpyinfo, 0);
       XSendEvent (FRAME_X_DISPLAY (f), target, False, NoEventMask, &msg);
       x_stop_ignoring_errors (dpyinfo);
 
@@ -4670,7 +4670,7 @@ x_dnd_send_leave (struct frame *f, Window target, Window 
toplevel)
   x_dnd_waiting_for_status_window = None;
   x_dnd_pending_send_position.type = 0;
 
-  x_ignore_errors_for_next_request (dpyinfo);
+  x_ignore_errors_for_next_request (dpyinfo, 0);
   XSendEvent (FRAME_X_DISPLAY (f), target, False, NoEventMask, &msg);
   x_stop_ignoring_errors (dpyinfo);
 }
@@ -4703,7 +4703,7 @@ x_dnd_send_drop (struct frame *f, Window target, Window 
toplevel,
   if (supported >= 1)
     msg.xclient.data.l[2] = timestamp;
 
-  x_ignore_errors_for_next_request (dpyinfo);
+  x_ignore_errors_for_next_request (dpyinfo, 0);
   XSendEvent (FRAME_X_DISPLAY (f), target, False, NoEventMask, &msg);
   x_stop_ignoring_errors (dpyinfo);
   return true;
@@ -6815,7 +6815,7 @@ x_set_frame_alpha (struct frame *f)
      Do this unconditionally as this function is called on reparent when
      alpha has not changed on the frame.  */
 
-  x_ignore_errors_for_next_request (dpyinfo);
+  x_ignore_errors_for_next_request (dpyinfo, 0);
 
   if (!FRAME_PARENT_FRAME (f))
     {
@@ -11339,7 +11339,7 @@ XTring_bell (struct frame *f)
         implementation.  */
 
       block_input ();
-      x_ignore_errors_for_next_request (dpyinfo);
+      x_ignore_errors_for_next_request (dpyinfo, 0);
 #ifdef HAVE_XKB
       XkbBell (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), 0, None);
 #else
@@ -11593,7 +11593,7 @@ x_frame_highlight (struct frame *f)
      the window-manager in use, tho something more is at play since I've been
      using that same window-manager binary for ever.  Let's not crash just
      because of this (bug#9310).  */
-  x_ignore_errors_for_next_request (dpyinfo);
+  x_ignore_errors_for_next_request (dpyinfo, 0);
   XSetWindowBorder (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
                    f->output_data.x->border_pixel);
   x_stop_ignoring_errors (dpyinfo);
@@ -11616,7 +11616,7 @@ x_frame_unhighlight (struct frame *f)
 
   block_input ();
   /* Same as above for XSetWindowBorder (bug#9310).  */
-  x_ignore_errors_for_next_request (dpyinfo);
+  x_ignore_errors_for_next_request (dpyinfo, 0);
   XSetWindowBorderPixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
                          f->output_data.x->border_tile);
   x_stop_ignoring_errors (dpyinfo);
@@ -18611,7 +18611,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                      x_dnd_waiting_for_status_window = None;
                    else
                      {
-                       x_ignore_errors_for_next_request (dpyinfo);
+                       x_ignore_errors_for_next_request (dpyinfo, 0);
                        XSendEvent (dpyinfo->display, target,
                                    False, NoEventMask,
                                    &x_dnd_pending_send_position);
@@ -21480,7 +21480,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
            if (FRAME_PARENT_FRAME (f) || (hf && frame_ancestor_p (f, hf)))
              {
-               x_ignore_errors_for_next_request (dpyinfo);
+               x_ignore_errors_for_next_request (dpyinfo, 0);
                XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
                                RevertToParent, event->xbutton.time);
                x_stop_ignoring_errors (dpyinfo);
@@ -23178,7 +23178,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                          /* This can generate XI_BadDevice if the
                             device's attachment was destroyed
                             server-side.  */
-                         x_ignore_errors_for_next_request (dpyinfo);
+                         x_ignore_errors_for_next_request (dpyinfo, 0);
                          XISetFocus (dpyinfo->display, device->attachment,
                                      /* Note that the input extension
                                         only supports RevertToParent-type
@@ -23191,7 +23191,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                         events to handle focus.  Errors are still
                         caught here in case the window is not
                         viewable.  */
-                     x_ignore_errors_for_next_request (dpyinfo);
+                     x_ignore_errors_for_next_request (dpyinfo, 0);
                      XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW 
(f),
                                      RevertToParent, xev->time);
                      x_stop_ignoring_errors (dpyinfo);
@@ -24200,7 +24200,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 #ifndef HAVE_GTK3
                  else if (x_input_grab_touch_events)
                    {
-                     x_ignore_errors_for_next_request (dpyinfo);
+                     x_ignore_errors_for_next_request (dpyinfo, 0);
                      XIAllowTouchEvents (dpyinfo->display, xev->deviceid,
                                          xev->detail, xev->event, 
XIRejectTouch);
                      x_stop_ignoring_errors (dpyinfo);
@@ -25637,10 +25637,17 @@ x_clean_failable_requests (struct x_display_info 
*dpyinfo)
    x_uncatch_errors_after_check is that this function does not sync to
    catch errors if requests were made.  It should be used instead of
    those two functions for catching errors around requests that do not
-   require a reply.  */
+   require a reply.
+
+   As a special feature intended to support xselect.c,
+   SELECTION_SERIAL may be an arbitrary number greater than zero: when
+   that is the case, x_select_handle_selection_error is called with
+   the specified number to delete the selection request that
+   encountered the error.  */
 
 void
-x_ignore_errors_for_next_request (struct x_display_info *dpyinfo)
+x_ignore_errors_for_next_request (struct x_display_info *dpyinfo,
+                                 unsigned int selection_serial)
 {
   struct x_failable_request *request, *max;
   unsigned long next_request;
@@ -25694,6 +25701,7 @@ x_ignore_errors_for_next_request (struct x_display_info 
*dpyinfo)
 
   request->start = next_request;
   request->end = 0;
+  request->selection_serial = selection_serial;
 
   dpyinfo->next_failable_request++;
 }
@@ -26208,6 +26216,12 @@ x_error_handler (Display *display, XErrorEvent *event)
                                                + (last - fail));
            }
 
+         /* If a selection transfer is the cause of this error,
+            remove the selection transfer now.  */
+         if (fail->selection_serial)
+           x_handle_selection_error (fail->selection_serial,
+                                     event);
+
          return 0;
        }
     }
@@ -27704,7 +27718,7 @@ frame_set_mouse_pixel_position (struct frame *f, int 
pix_x, int pix_y)
       && deviceid != -1)
     {
       block_input ();
-      x_ignore_errors_for_next_request (FRAME_DISPLAY_INFO (f));
+      x_ignore_errors_for_next_request (FRAME_DISPLAY_INFO (f), 0);
       XIWarpPointer (FRAME_X_DISPLAY (f), deviceid, None,
                     FRAME_X_WINDOW (f), 0, 0, 0, 0, pix_x, pix_y);
       x_stop_ignoring_errors (FRAME_DISPLAY_INFO (f));
@@ -28001,7 +28015,7 @@ x_set_input_focus (struct x_display_info *dpyinfo, 
Window window,
        {
          eassert (device->use == XIMasterPointer);
 
-         x_ignore_errors_for_next_request (dpyinfo);
+         x_ignore_errors_for_next_request (dpyinfo, 0);
          XISetFocus (dpyinfo->display, device->attachment,
                      /* Note that the input extension
                         only supports RevertToParent-type
@@ -28016,7 +28030,7 @@ x_set_input_focus (struct x_display_info *dpyinfo, 
Window window,
 
   /* Otherwise, use the pointer device that the X server says is the
      client pointer.  */
-  x_ignore_errors_for_next_request (dpyinfo);
+  x_ignore_errors_for_next_request (dpyinfo, 0);
   XSetInputFocus (dpyinfo->display, window, RevertToParent, time);
   x_stop_ignoring_errors (dpyinfo);
 }
@@ -28158,7 +28172,7 @@ xembed_send_message (struct frame *f, Time t, enum 
xembed_message msg,
      but I don't understand why: there is no way for clients to
      survive the death of the parent anyway.  */
 
-  x_ignore_errors_for_next_request (FRAME_DISPLAY_INFO (f));
+  x_ignore_errors_for_next_request (FRAME_DISPLAY_INFO (f), 0);
   XSendEvent (FRAME_X_DISPLAY (f), FRAME_X_OUTPUT (f)->parent_desc,
              False, NoEventMask, &event);
   x_stop_ignoring_errors (FRAME_DISPLAY_INFO (f));
@@ -31335,7 +31349,7 @@ x_catch_errors_for_lisp (struct x_display_info *dpyinfo)
   if (!x_fast_protocol_requests)
     x_catch_errors (dpyinfo->display);
   else
-    x_ignore_errors_for_next_request (dpyinfo);
+    x_ignore_errors_for_next_request (dpyinfo, 0);
 }
 
 void
diff --git a/src/xterm.h b/src/xterm.h
index a888f57823..832ffc172b 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -334,6 +334,9 @@ struct x_failable_request
   /* If this is zero, then the request has not yet been made.
      Otherwise, this is the request that ends this sequence.  */
   unsigned long end;
+
+  /* Any selection event serial associated with this error trap.  */
+  unsigned int selection_serial;
 };
 
 #ifdef HAVE_XFIXES
@@ -1670,7 +1673,8 @@ extern bool x_had_errors_p (Display *);
 extern void x_unwind_errors_to (int);
 extern void x_uncatch_errors (void);
 extern void x_uncatch_errors_after_check (void);
-extern void x_ignore_errors_for_next_request (struct x_display_info *);
+extern void x_ignore_errors_for_next_request (struct x_display_info *,
+                                             unsigned int);
 extern void x_stop_ignoring_errors (struct x_display_info *);
 extern void x_clear_errors (Display *);
 extern void x_set_window_size (struct frame *, bool, int, int);
@@ -1830,6 +1834,7 @@ extern void x_handle_selection_notify (const 
XSelectionEvent *);
 extern void x_handle_selection_event (struct selection_input_event *);
 extern void x_clear_frame_selections (struct frame *);
 extern void x_remove_selection_transfers (struct x_display_info *);
+extern void x_handle_selection_error (unsigned int, XErrorEvent *);
 
 extern Lisp_Object x_atom_to_symbol (struct x_display_info *, Atom);
 extern Atom symbol_to_x_atom (struct x_display_info *, Lisp_Object);



reply via email to

[Prev in Thread] Current Thread [Next in Thread]