#ifdef HAVE_X_I18N
static int x_filter_event (struct x_display_info *, XEvent *);
#endif
+static void x_ignore_errors_for_next_request (struct x_display_info *);
+static void x_clean_failable_requests (struct x_display_info *);
static struct frame *x_tooltip_window_to_frame (struct x_display_info *,
Window, bool *);
*((uint32_t *) &msg.xclient.data.b[12]) = dmsg->index_atom;
*((uint32_t *) &msg.xclient.data.b[16]) = dmsg->source_window;
- x_catch_errors (dpyinfo->display);
+ x_ignore_errors_for_next_request (dpyinfo);
XSendEvent (dpyinfo->display, target, False, NoEventMask, &msg);
- x_uncatch_errors ();
}
static void
msg.xclient.data.b[18] = 0;
msg.xclient.data.b[19] = 0;
- x_catch_errors (dpyinfo->display);
+ x_ignore_errors_for_next_request (dpyinfo);
XSendEvent (dpyinfo->display, target, False, NoEventMask, &msg);
- x_uncatch_errors ();
}
static void
msg.xclient.data.b[18] = 0;
msg.xclient.data.b[19] = 0;
- x_catch_errors (dpyinfo->display);
+ x_ignore_errors_for_next_request (dpyinfo);
XSendEvent (dpyinfo->display, target, False, NoEventMask, &msg);
- x_uncatch_errors ();
}
static void
msg.xclient.data.b[18] = 0;
msg.xclient.data.b[19] = 0;
- x_catch_errors (dpyinfo->display);
+ x_ignore_errors_for_next_request (dpyinfo);
XSendEvent (dpyinfo->display, target, False, NoEventMask, &msg);
- x_uncatch_errors ();
}
static int
so we don't have to set it again. */
x_dnd_init_type_lists = true;
- x_catch_errors (dpyinfo->display);
+ x_ignore_errors_for_next_request (dpyinfo);
XSendEvent (FRAME_X_DISPLAY (f), target, False, NoEventMask, &msg);
- x_uncatch_errors ();
}
static void
x_dnd_pending_send_position = msg;
else
{
- x_catch_errors (dpyinfo->display);
+ x_ignore_errors_for_next_request (dpyinfo);
XSendEvent (FRAME_X_DISPLAY (f), target, False, NoEventMask, &msg);
- x_uncatch_errors ();
x_dnd_waiting_for_status_window = target;
}
x_dnd_waiting_for_status_window = None;
- x_catch_errors (dpyinfo->display);
+ x_ignore_errors_for_next_request (dpyinfo);
XSendEvent (FRAME_X_DISPLAY (f), target, False, NoEventMask, &msg);
- x_uncatch_errors ();
}
static bool
if (supported >= 1)
msg.xclient.data.l[2] = timestamp;
- x_catch_errors (dpyinfo->display);
+ x_ignore_errors_for_next_request (dpyinfo);
XSendEvent (FRAME_X_DISPLAY (f), target, False, NoEventMask, &msg);
- x_uncatch_errors ();
return true;
}
#endif
x_dnd_inside_handle_one_xevent = false;
+ /* Clean up any event handlers that are now out of date. */
+ x_clean_failable_requests (FRAME_DISPLAY_INFO (f));
+
/* The unblock_input below might try to read input, but
XTread_socket does nothing inside a drag-and-drop event
loop, so don't let it clear the pending_signals flag. */
{
if (x_dnd_pending_send_position.type != 0)
{
- x_catch_errors (dpyinfo->display);
+ x_ignore_errors_for_next_request (dpyinfo);
XSendEvent (dpyinfo->display, target,
False, NoEventMask,
&x_dnd_pending_send_position);
- x_uncatch_errors ();
}
x_dnd_pending_send_position.type = 0;
&& dpyinfo->display == x_dnd_finish_display)))
return 0;
+ x_clean_failable_requests (dpyinfo);
+
block_input ();
/* For debugging, this gives a way to fake an I/O error. */
always skips an XSync to the server, and should be used only
immediately after x_had_errors_p or x_check_errors, or when it is
known that no requests have been made since the last x_catch_errors
- call for DPY. */
+ call for DPY.
+
+ There is no need to use this mechanism for ignoring errors from
+ single asynchronous requests, such as sending a ClientMessage to a
+ window that might no longer exist. Use
+ x_ignore_errors_for_next_request instead. */
void
x_catch_errors_with_handler (Display *dpy, x_special_error_handler handler,
x_catch_errors_with_handler (dpy, NULL, NULL);
}
+/* Return if errors for REQUEST should be ignored even if there is no
+ error handler applied. */
+static unsigned long *
+x_request_can_fail (struct x_display_info *dpyinfo,
+ unsigned long request)
+{
+ unsigned long *failable_requests;
+
+ for (failable_requests = dpyinfo->failable_requests;
+ failable_requests < dpyinfo->next_failable_request;
+ failable_requests++)
+ {
+ if (*failable_requests == request)
+ return failable_requests;
+ }
+
+ return NULL;
+}
+
+/* Remove outdated request serials from
+ dpyinfo->failable_requests. */
+static void
+x_clean_failable_requests (struct x_display_info *dpyinfo)
+{
+ unsigned long *first, *last;
+
+ last = dpyinfo->next_failable_request;
+
+ for (first = dpyinfo->failable_requests; first < last; first++)
+ {
+ if (*first > LastKnownRequestProcessed (dpyinfo->display))
+ break;
+ }
+
+ memmove (&dpyinfo->failable_requests, first,
+ sizeof *first * (last - first));
+
+ dpyinfo->next_failable_request = (dpyinfo->failable_requests
+ + (last - first));
+}
+
+static void
+x_ignore_errors_for_next_request (struct x_display_info *dpyinfo)
+{
+ unsigned long *request, *max;
+
+ request = dpyinfo->next_failable_request;
+ max = dpyinfo->failable_requests + N_FAILABLE_REQUESTS;
+
+ if (request > max)
+ {
+ /* There is no point in making this extra sync if all requests
+ are known to have been fully processed. */
+ if ((LastKnownRequestProcessed (x_error_message->dpy)
+ != NextRequest (x_error_message->dpy) - 1))
+ XSync (dpyinfo->display, False);
+
+ x_clean_failable_requests (dpyinfo);
+ request = dpyinfo->next_failable_request;
+ }
+
+ if (request >= max)
+ /* A request should always be made immediately after calling this
+ function. */
+ emacs_abort ();
+
+ *request = NextRequest (dpyinfo->display);
+ dpyinfo->next_failable_request++;
+}
+
/* Undo the last x_catch_errors call.
DPY should be the display that was passed to x_catch_errors.
x_uncatch_errors (void)
{
struct x_error_message_stack *tmp;
+ struct x_display_info *dpyinfo;
/* In rare situations when running Emacs run in daemon mode,
shutting down an emacsclient via delete-frame can cause
block_input ();
+ dpyinfo = x_display_info_for_display (x_error_message->dpy);
+
/* The display may have been closed before this function is called.
Check if it is still open before calling XSync. */
- if (x_display_info_for_display (x_error_message->dpy) != 0
+ if (dpyinfo != 0
/* There is no point in making this extra sync if all requests
are known to have been fully processed. */
&& (LastKnownRequestProcessed (x_error_message->dpy)
installed. */
&& (NextRequest (x_error_message->dpy)
> x_error_message->first_request))
- XSync (x_error_message->dpy, False);
+ {
+ XSync (x_error_message->dpy, False);
+ x_clean_failable_requests (dpyinfo);
+ }
tmp = x_error_message;
x_error_message = x_error_message->prev;
x_error_handler (Display *display, XErrorEvent *event)
{
struct x_error_message_stack *stack;
-#ifdef HAVE_XINPUT2
struct x_display_info *dpyinfo;
-#endif
+ unsigned long *fail, *last;
#if defined USE_GTK && defined HAVE_GTK3
if ((event->error_code == BadMatch
return 0;
#endif
+ dpyinfo = x_display_info_for_display (display);
+
+ if (dpyinfo)
+ {
+ fail = x_request_can_fail (dpyinfo, event->serial);
+
+ if (fail)
+ {
+ /* Now that this request has been handled, remove it from
+ the list of requests that can fail. */
+ last = dpyinfo->next_failable_request;
+ memmove (&dpyinfo->failable_requests, fail,
+ sizeof *fail * (last - fail));
+ dpyinfo->next_failable_request = (dpyinfo->failable_requests
+ + (last - fail));
+
+ return 0;
+ }
+ }
+
/* If we try to ungrab or grab a device that doesn't exist anymore
(that happens a lot in xmenu.c), just ignore the error. */
#ifdef HAVE_XINPUT2
- dpyinfo = x_display_info_for_display (display);
-
/* 51 is X_XIGrabDevice and 52 is X_XIUngrabDevice.
53 is X_XIAllowEvents. We handle errors from that here to avoid
dpyinfo = xzalloc (sizeof *dpyinfo);
terminal = x_create_terminal (dpyinfo);
+ dpyinfo->next_failable_request = dpyinfo->failable_requests;
+
{
struct x_display_info *share;