Mercurial > hg > xemacs-beta
diff src/event-msw.c @ 227:0e522484dd2a r20-5b12
Import from CVS: tag r20-5b12
author | cvs |
---|---|
date | Mon, 13 Aug 2007 10:12:37 +0200 |
parents | 12579d965149 |
children | 557eaa0339bf |
line wrap: on
line diff
--- a/src/event-msw.c Mon Aug 13 10:11:42 2007 +0200 +++ b/src/event-msw.c Mon Aug 13 10:12:37 2007 +0200 @@ -35,10 +35,11 @@ #include "device.h" #include "console-msw.h" +#include "emacsfns.h" #include "events.h" #include "frame.h" #include "process.h" - +#include "redisplay.h" #include "sysproc.h" #include "syswait.h" #include "systime.h" @@ -82,6 +83,14 @@ /* Number of wait handles */ static mswindows_waitable_count=0; +/* This is the event signaled by the event pump. + See mswindows_pump_outstanding_events for comments */ +static Lisp_Object mswindows_error_caught_by_event_pump; +static int mswindows_in_event_pump; + +/* Count of wound timers */ +static int mswindows_pending_timers_count; + static int mswindows_user_event_p (struct Lisp_Event* sevt) { @@ -195,6 +204,101 @@ } /* + * This is an unsafe part of event pump, guarded by + * condition_case. See mswindows_pump_outstanding_events + */ +static Lisp_Object +mswindows_unsafe_pump_events (Lisp_Object u_n_u_s_e_d) +{ + /* This function can call lisp */ + Lisp_Object event = Fmake_event (Qnil, Qnil); + struct gcpro gcpro1; + int do_redisplay = 0; + GCPRO1 (event); + + while (detect_input_pending ()) + { + Fnext_event (event, Qnil); + Fdispatch_event (event); + do_redisplay = 1; + } + + if (do_redisplay) + redisplay (); + + Fdeallocate_event (event); + UNGCPRO; + + /* Qt becomes return value of mswindows_pump_outstanding_events + once we get here */ + return Qt; +} + +/* See mswindows_pump_outstanding_events */ +static Lisp_Object +mswindows_event_pump_error_handler (Lisp_Object cons_sig_data, + Lisp_Object u_n_u_s_e_d) +{ + mswindows_error_caught_by_event_pump = cons_sig_data; + return Qnil; +} + +/* + * This function pumps emacs events, while available, by using + * next_message/dispatch_message loop. Errors are trapped around + * the loop so the function always returns. + * + * Windows message queue is not looked into during the call, + * neither are waitable handles checked. The function pumps + * thus only dispatch events already queued, as well as those + * resulted in dispatching thereof. This is done by setting + * module local variable mswidows_in_event_pump to nonzero. + * + * Return value is Qt if no errors was trapped, or Qnil if + * there was an error. + * + * In case of error, a cons representing the error, in the + * form (SIGNAL . DATA), is stored in the module local variable + * mswindows_error_caught_by_event_pump. This error is signaled + * again when DispatchMessage returns. Thus, Windows internal + * modal loops are protected against throws, which are proven + * to corrupt internal Windows structures. + * + * In case of success, mswindows_error_caught_by_event_pump is + * assigned Qnil. + * + * If the value of mswindows_error_caught_by_event_pump is not + * nil already upon entry, the function just returns non-nil. + * This situation means that a new event has been queued while + * cancleng mode. The event will be dequeued on the next regular + * call of next-event; the pump is off since error is caught. + * The caller must *unconditionally* cancel modal loop if the + * value returned by this function is nil. Otherwise, everything + * will become frozen until the modal loop exits under normal + * condition (scrollbar drag is released, menu closed etc.) + */ +Lisp_Object +mswindows_pump_outstanding_events (void) +{ + /* This function can call lisp */ + + Lisp_Object result = Qt; + + if (NILP(mswindows_error_caught_by_event_pump)) + { + + mswindows_in_event_pump = 1; + + result = condition_case_1 (Qt, + mswindows_unsafe_pump_events, Qnil, + mswindows_event_pump_error_handler, Qnil); + + mswindows_in_event_pump = 0; + } + return result; +} + +/* * Find a free waitable slot */ static int @@ -267,6 +371,7 @@ struct Lisp_Event *event = XEVENT (emacs_event); KillTimer (NULL, id_timer); + --mswindows_pending_timers_count; event->channel = Qnil; event->timestamp = dwtime; @@ -281,7 +386,60 @@ { MSG msg; while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) - DispatchMessage (&msg); + { + DispatchMessage (&msg); + if (!NILP (mswindows_error_caught_by_event_pump)) + { + /* Got an error while messages were pumped while + in window procedure - have to resignal */ + Lisp_Object sym = XCAR (mswindows_error_caught_by_event_pump); + Lisp_Object data = XCDR (mswindows_error_caught_by_event_pump); + mswindows_error_caught_by_event_pump = Qnil; + Fsignal (sym, data); + } + } +} + +/* + * This is a special flavour of the mswindows_need_event function, + * used while in event pump. Actually, there is only kind of events + * allowed while in event pump: a timer. An attempt to fetch any + * other event leads to a dealock, as there's no source of user input + * ('cause event pump mirrors windows modal loop, which is a sole + * owner of thread message queue). + * + * To detect this, we use a counter of active timers, and allow + * fetching WM_TIMER messages. Instead of trying to fetch a WM_TIMER + * which will never come when there are no pending timers, which leads + * to deadlock, we simply signal an error. + * + * The implementation does not honor user_p by design. + */ +static void +mswindows_need_event_in_event_pump (int user_p, int badly_p) +{ + MSG msg; + + /* Check if already have one */ + if (!NILP (mswindows_u_dispatch_event_queue) + || !NILP (mswindows_s_dispatch_event_queue)) + return; + + /* No event is ok */ + if (!badly_p) + return; + + /* We do not check the _u_ queue, because timers go to _s_ */ + while (NILP (mswindows_s_dispatch_event_queue)) + { + /* We'll deadlock if go waiting */ + if (mswindows_pending_timers_count == 0) + error ("Deadlock due to an attempt to call next-event in a wrong context"); + + /* Fetch and dispatch any pending timers */ + GetMessage (&msg, NULL, WM_TIMER, WM_TIMER); + DispatchMessage (&msg); + } } /* @@ -305,6 +463,12 @@ { int active; + if (mswindows_in_event_pump) + { + mswindows_need_event_in_event_pump (user_p, badly_p); + return; + } + /* Have to drain Windows message queue first, otherwise, we may miss quit char when called from quit_p */ mswindows_drain_windows_queue (); @@ -373,6 +537,7 @@ (EMACS_USECS (thyme) + 500) / 1000; if (milliseconds < 1) milliseconds = 1; + ++mswindows_pending_timers_count; return SetTimer (NULL, 0, milliseconds, mswindows_wm_timer_callback); } @@ -383,6 +548,7 @@ Lisp_Object emacs_event; KillTimer (NULL, id); + --mswindows_pending_timers_count; /* If there is a dispatch event generated by this timeout in the queue, we have to remove it too. */ @@ -573,6 +739,11 @@ staticpro (&mswindows_s_dispatch_event_queue); mswindows_s_dispatch_event_queue_tail = Qnil; + mswindows_error_caught_by_event_pump = Qnil; + staticpro (&mswindows_error_caught_by_event_pump); + mswindows_in_event_pump = 0; + mswindows_pending_timers_count = 0; + mswindows_event_stream = xnew (struct event_stream); mswindows_event_stream->event_pending_p = emacs_mswindows_event_pending_p;