* src/keyboard.c (prev_kbd_event): Delete function.
(readable_events): Return 1 if
x_detect_pending_selection_requests returns true.
(kbd_buffer_unget_event): Also delete function, since nested
selection requests are really handled correctly.
(kbd_buffer_get_event): Handle events from the special X
deferred selection queue as well.
* src/keyboard.h: Update prototypes.
* src/xselect.c (struct selection_event_queue)
(selection_input_event_equal, x_queue_event)
(x_start_queuing_selection_requests)
(x_stop_queuing_selection_requests): Delete structs, since they
are no longer required.
(x_handle_selection_request, x_handle_selection_event): Allow
nested selection events.
* src/xterm.c (struct x_selection_request_event): New struct.
(x_handle_pending_selection_requests_1)
(x_handle_pending_selection_requests): Handle all events in the
new selection event queue.
(x_push_selection_request, x_detect_pending_selection_requests):
New functions.
(x_dnd_begin_drag_and_drop): Drain the selection queue here as
well.
(handle_one_xevent): When inside a nested event loop, just push
selections to that queue.
(XTread_socket): Allow reading X events if x_dnd_unwind_flag is
true, even though DND is in progress.
(x_delete_display): Delete pending selection events for the
display that is going away.
* src/xterm.h: Update prototypes.
return ptr == kbd_buffer + KBD_BUFFER_SIZE - 1 ? kbd_buffer : ptr + 1;
}
-#ifdef HAVE_X11
-static union buffered_input_event *
-prev_kbd_event (union buffered_input_event *ptr)
-{
- return ptr == kbd_buffer ? kbd_buffer + KBD_BUFFER_SIZE - 1 : ptr - 1;
-}
-#endif
-
/* Like EVENT_START, but assume EVENT is an event.
This pacifies gcc -Wnull-dereference, which might otherwise
complain about earlier checks that EVENT is indeed an event. */
return 1;
}
+#ifdef HAVE_X_WINDOWS
+ if (x_detect_pending_selection_requests ())
+ return 1;
+#endif
+
if (!(flags & READABLE_EVENTS_IGNORE_SQUEEZABLES) && some_mouse_moved ())
return 1;
if (single_kboard)
Vquit_flag = Vthrow_on_input;
}
-
-#ifdef HAVE_X11
-
-/* Put a selection input event back in the head of the event queue. */
-
-void
-kbd_buffer_unget_event (struct selection_input_event *event)
-{
- /* Don't let the very last slot in the buffer become full, */
- union buffered_input_event *kp = prev_kbd_event (kbd_fetch_ptr);
- if (kp != kbd_store_ptr)
- {
- kp->sie = *event;
- kbd_fetch_ptr = kp;
- }
-}
-
-#endif
-
/* Limit help event positions to this range, to avoid overflow problems. */
#define INPUT_EVENT_POS_MAX \
((ptrdiff_t) min (PTRDIFF_MAX, min (TYPE_MAXIMUM (Time) / 2, \
struct timespec *end_time)
{
Lisp_Object obj, str;
+#ifdef HAVE_X_WINDOWS
+ bool had_pending_selection_requests;
+
+ had_pending_selection_requests = false;
+#endif
#ifdef subprocesses
if (kbd_on_hold_p () && kbd_buffer_nr_stored () < KBD_BUFFER_SIZE / 4)
#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
gobble_input ();
#endif
+
if (kbd_fetch_ptr != kbd_store_ptr)
break;
if (some_mouse_moved ())
break;
+#ifdef HAVE_X_WINDOWS
+ if (x_detect_pending_selection_requests ())
+ {
+ had_pending_selection_requests = true;
+ break;
+ }
+#endif
if (end_time)
{
struct timespec now = current_timespec ();
gobble_input ();
}
+#ifdef HAVE_X_WINDOWS
+ /* Handle pending selection requests. This can happen if Emacs
+ enters a recursive edit inside a nested event loop (probably
+ because the debugger opened) or someone called
+ `read-char'. */
+
+ if (had_pending_selection_requests)
+ x_handle_pending_selection_requests ();
+#endif
+
if (CONSP (Vunread_command_events))
{
Lisp_Object first;
? movement_frame->last_mouse_device
: virtual_core_pointer_name);
}
+#ifdef HAVE_X_WINDOWS
+ else if (had_pending_selection_requests)
+ obj = Qnil;
+#endif
else
/* We were promised by the above while loop that there was
something for us to read! */
If READABLE_EVENTS_FILTER_EVENTS is set in FLAGS, ignore internal
events (FOCUS_IN_EVENT).
If READABLE_EVENTS_IGNORE_SQUEEZABLES is set in FLAGS, ignore mouse
- movements and toolkit scroll bar thumb drags. */
+ movements and toolkit scroll bar thumb drags.
+
+ On X, this also returns if the selection event chain is full, since
+ that's also "keyboard input". */
static bool
get_input_pending (int flags)
kbd_buffer_store_buffered_event ((union buffered_input_event *) event,
hold_quit);
}
-#ifdef HAVE_X11
-extern void kbd_buffer_unget_event (struct selection_input_event *);
-#endif
extern void poll_for_input_1 (void);
extern void show_help_echo (Lisp_Object, Lisp_Object, Lisp_Object,
Lisp_Object);
assq_no_quit (selection_symbol, dpyinfo->terminal->Vselection_alist)
\f
-/* Define a queue to save up SELECTION_REQUEST_EVENT events for later
- handling. */
-
-struct selection_event_queue
- {
- struct selection_input_event event;
- struct selection_event_queue *next;
- };
-
-static struct selection_event_queue *selection_queue;
-
-/* Nonzero means queue up SELECTION_REQUEST_EVENT events. */
-
-static int x_queue_selection_requests;
-
-/* True if the input events are duplicates. */
-
-static bool
-selection_input_event_equal (struct selection_input_event *a,
- struct selection_input_event *b)
-{
- return (a->kind == b->kind && a->dpyinfo == b->dpyinfo
- && a->requestor == b->requestor && a->selection == b->selection
- && a->target == b->target && a->property == b->property
- && a->time == b->time);
-}
-
-/* Queue up an SELECTION_REQUEST_EVENT *EVENT, to be processed later. */
-
-static void
-x_queue_event (struct selection_input_event *event)
-{
- struct selection_event_queue *queue_tmp;
-
- /* Don't queue repeated requests.
- This only happens for large requests which uses the incremental protocol. */
- for (queue_tmp = selection_queue; queue_tmp; queue_tmp = queue_tmp->next)
- {
- if (selection_input_event_equal (event, &queue_tmp->event))
- {
- TRACE1 ("DECLINE DUP SELECTION EVENT %p", queue_tmp);
- x_decline_selection_request (event);
- return;
- }
- }
-
- queue_tmp = xmalloc (sizeof *queue_tmp);
- TRACE1 ("QUEUE SELECTION EVENT %p", queue_tmp);
- queue_tmp->event = *event;
- queue_tmp->next = selection_queue;
- selection_queue = queue_tmp;
-}
-
-/* Start queuing SELECTION_REQUEST_EVENT events. */
-
-static void
-x_start_queuing_selection_requests (void)
-{
- if (x_queue_selection_requests)
- emacs_abort ();
-
- x_queue_selection_requests++;
- TRACE1 ("x_start_queuing_selection_requests %d", x_queue_selection_requests);
-}
-
-/* Stop queuing SELECTION_REQUEST_EVENT events. */
-
-static void
-x_stop_queuing_selection_requests (void)
-{
- TRACE1 ("x_stop_queuing_selection_requests %d", x_queue_selection_requests);
- --x_queue_selection_requests;
-
- /* Take all the queued events and put them back
- so that they get processed afresh. */
-
- while (selection_queue != NULL)
- {
- struct selection_event_queue *queue_tmp = selection_queue;
- TRACE1 ("RESTORE SELECTION EVENT %p", queue_tmp);
- kbd_buffer_unget_event (&queue_tmp->event);
- selection_queue = queue_tmp->next;
- xfree (queue_tmp);
- }
-}
-\f
/* This converts a Lisp symbol to a server Atom, avoiding a server
roundtrip whenever possible. */
selection_request_dpyinfo = dpyinfo;
record_unwind_protect_void (x_selection_request_lisp_error);
- /* We might be able to handle nested x_handle_selection_requests,
- but this is difficult to test, and seems unimportant. */
- x_start_queuing_selection_requests ();
- record_unwind_protect_void (x_stop_queuing_selection_requests);
-
TRACE2 ("x_handle_selection_request: selection=%s, target=%s",
SDATA (SYMBOL_NAME (selection_symbol)),
SDATA (SYMBOL_NAME (target_symbol)));
TRACE0 ("x_handle_selection_event");
if (event->kind != SELECTION_REQUEST_EVENT)
x_handle_selection_clear (event);
- else if (x_queue_selection_requests)
- x_queue_event (event);
else
x_handle_selection_request (event);
}
static struct input_event *current_hold_quit;
#endif
+struct x_selection_request_event
+{
+ /* The selection request event. */
+ struct selection_input_event se;
+
+ /* The next unprocessed selection request event. */
+ struct x_selection_request_event *next;
+};
+
+/* Chain of unprocessed selection request events. Used to handle
+ selection requests inside long-lasting modal event loops, such as
+ the drag-and-drop loop. */
+
+struct x_selection_request_event *pending_selection_requests;
+
/* Compare two request serials A and B with OP, handling
wraparound. */
#define X_COMPARE_SERIALS(a, op ,b) \
terminating DND as part of the display disconnect handler. */
static sigjmp_buf x_dnd_disconnect_handler;
+/* Whether or not the current invocation of handle_one_xevent
+ happened inside the drag_and_drop event loop. */
+static bool x_dnd_inside_handle_one_xevent;
+
/* Structure describing a single window that can be the target of
drag-and-drop operations. */
struct x_client_list_window
#endif /* USE_X_TOOLKIT || USE_GTK */
+static void
+x_handle_pending_selection_requests_1 (struct x_selection_request_event *tem)
+{
+ specpdl_ref count;
+ struct selection_input_event se;
+
+ count = SPECPDL_INDEX ();
+ se = tem->se;
+
+ record_unwind_protect_ptr (xfree, tem);
+ x_handle_selection_event (&se);
+ unbind_to (count, Qnil);
+}
+
+/* Handle all pending selection request events from modal event
+ loops. */
+void
+x_handle_pending_selection_requests (void)
+{
+ struct x_selection_request_event *tem;
+
+ while (pending_selection_requests)
+ {
+ tem = pending_selection_requests;
+ pending_selection_requests = tem->next;
+
+ x_handle_pending_selection_requests_1 (tem);
+ }
+}
+
+static void
+x_push_selection_request (struct selection_input_event *se)
+{
+ struct x_selection_request_event *tem;
+
+ tem = xmalloc (sizeof *tem);
+ tem->next = pending_selection_requests;
+ tem->se = *se;
+ pending_selection_requests = tem;
+}
+
+bool
+x_detect_pending_selection_requests (void)
+{
+ return pending_selection_requests;
+}
+
/* This function is defined far away from the rest of the XDND code so
it can utilize `x_any_window_to_frame'. */
while (x_dnd_in_progress || x_dnd_waiting_for_finish)
{
EVENT_INIT (hold_quit);
+
#ifdef USE_GTK
current_finish = X_EVENT_NORMAL;
current_hold_quit = &hold_quit;
#endif
block_input ();
+ x_dnd_inside_handle_one_xevent = true;
#ifdef USE_GTK
gtk_main_iteration ();
#elif defined USE_X_TOOLKIT
current_count = -1;
current_hold_quit = NULL;
#endif
+ x_dnd_inside_handle_one_xevent = false;
/* The unblock_input below might try to read input, but
XTread_socket does nothing inside a drag-and-drop event
if (hold_quit.kind != NO_EVENT)
{
- if (hold_quit.kind == SELECTION_REQUEST_EVENT)
- {
- /* It's not safe to run Lisp inside this function if
- x_dnd_in_progress and x_dnd_waiting_for_finish
- are unset, so push it back into the event queue. */
-
- if (!x_dnd_in_progress && !x_dnd_waiting_for_finish)
- kbd_buffer_store_event (&hold_quit);
- else
- {
- x_dnd_old_window_attrs = root_window_attrs;
- x_dnd_unwind_flag = true;
-
- ref = SPECPDL_INDEX ();
- record_unwind_protect_ptr (x_dnd_cleanup_drag_and_drop, f);
- x_handle_selection_event ((struct selection_input_event *) &hold_quit);
- x_dnd_unwind_flag = false;
- unbind_to (ref, Qnil);
- }
-
- continue;
- }
-
if (x_dnd_in_progress)
{
if (x_dnd_last_seen_window != None
quit ();
}
+ if (pending_selection_requests
+ && (x_dnd_in_progress || x_dnd_waiting_for_finish))
+ {
+ x_dnd_old_window_attrs = root_window_attrs;
+ x_dnd_unwind_flag = true;
+
+ ref = SPECPDL_INDEX ();
+ record_unwind_protect_ptr (x_dnd_cleanup_drag_and_drop, f);
+ x_handle_pending_selection_requests ();
+ x_dnd_unwind_flag = false;
+ unbind_to (ref, Qnil);
+ }
+
#ifdef USE_GTK
if (xg_pending_quit_event.kind != NO_EVENT)
{
SELECTION_EVENT_DPYINFO (&inev.sie) = dpyinfo;
SELECTION_EVENT_SELECTION (&inev.sie) = eventp->selection;
SELECTION_EVENT_TIME (&inev.sie) = eventp->time;
+
+ if ((x_dnd_in_progress
+ && dpyinfo == FRAME_DISPLAY_INFO (x_dnd_frame))
+ || (x_dnd_waiting_for_finish
+ && dpyinfo->display == x_dnd_finish_display))
+ {
+ x_push_selection_request (&inev.sie);
+ EVENT_INIT (inev.ie);
+ }
}
break;
|| (x_dnd_waiting_for_finish
&& dpyinfo->display == x_dnd_finish_display))
{
-#ifndef USE_GTK
- eassume (hold_quit);
-#else
- /* If the debugger runs inside a selection converter, then
- xg_select can call handle_one_xevent with no
- hold_quit. */
- if (!hold_quit)
- goto done;
-#endif
-
- *hold_quit = inev.ie;
+ x_push_selection_request (&inev.sie);
EVENT_INIT (inev.ie);
}
read X events while the drag-and-drop event loop is in progress,
things can go wrong very quick.
+ When x_dnd_unwind_flag is true, the above doesn't apply, since
+ the surrounding code takes special precautions to keep it safe.
+
That doesn't matter for events from displays other than the
display of the drag-and-drop operation, though. */
- if ((x_dnd_in_progress
- && dpyinfo->display == FRAME_X_DISPLAY (x_dnd_frame))
- || (x_dnd_waiting_for_finish
- && dpyinfo->display == x_dnd_finish_display))
+ if (!x_dnd_unwind_flag
+ && ((x_dnd_in_progress
+ && dpyinfo->display == FRAME_X_DISPLAY (x_dnd_frame))
+ || (x_dnd_waiting_for_finish
+ && dpyinfo->display == x_dnd_finish_display)))
return 0;
block_input ();
struct terminal *t;
struct color_name_cache_entry *color_entry, *next_color_entry;
int i;
+ struct x_selection_request_event *ie, *last, *temp;
/* Close all frames and delete the generic struct terminal for this
X display. */
break;
}
+ /* Find any pending selection requests for this display and unchain
+ them. */
+
+ last = NULL;
+
+ for (ie = pending_selection_requests; ie; ie = ie->next)
+ {
+ again:
+
+ if (SELECTION_EVENT_DPYINFO (&ie->se) == dpyinfo)
+ {
+ if (last)
+ last->next = ie->next;
+
+ temp = ie;
+ ie = ie->next;
+ xfree (temp);
+
+ goto again;
+ }
+
+ last = ie;
+ }
+
if (next_noop_dpyinfo == dpyinfo)
next_noop_dpyinfo = dpyinfo->next;
extern void x_scroll_bar_configure (GdkEvent *);
#endif
+extern void x_handle_pending_selection_requests (void);
+extern bool x_detect_pending_selection_requests (void);
extern Lisp_Object x_dnd_begin_drag_and_drop (struct frame *, Time, Atom,
Lisp_Object, Atom *, const char **,
size_t, bool, Atom *, int);