From: Po Lu Date: Thu, 15 Dec 2022 12:06:37 +0000 (+0800) Subject: Handle selection transfer errors earlier X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=db69249b761a80158c1469b2a169d6f5c8509ae1;p=emacs.git 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'. --- diff --git a/src/xfns.c b/src/xfns.c index 2bf282fd243..668f711bdb5 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 191f0770337..05548be311f 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 968dec6fbdd..278a8dda10b 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 a888f57823f..832ffc172b9 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);