/* 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);
/* The atimer for the timeout. */
struct atimer *timeout;
+ /* The selection serial. */
+ unsigned int serial;
+
/* Flags. */
int flags;
};
/* 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;
};
struct transfer outstanding_transfers;
+/* A counter for selection serials. */
+
+static unsigned int selection_serial;
+
\f
struct prop_location
/* 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);
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,
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
" %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,
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,
"; 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,
PropModeReplace, xdata,
remaining);
x_stop_ignoring_errors (transfer->dpyinfo);
- transfer->offset += remaining;
}
}
}
}
+/* 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
/* 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);
*((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);
}
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);
}
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);
}
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);
}
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)
{
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);
}
#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
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);
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);
}
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);
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);
}
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;
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))
{
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
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);
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);
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);
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);
/* 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
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);
#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);
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;
request->start = next_request;
request->end = 0;
+ request->selection_serial = selection_serial;
dpyinfo->next_failable_request++;
}
+ (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;
}
}
&& 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));
{
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
/* 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);
}
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));
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