(coded-charset-entity-reference-alist): Add setting for
[chise/xemacs-chise.git.1] / src / event-Xt.c
index 2eb2fca..ccfde91 100644 (file)
@@ -67,7 +67,9 @@ Boston, MA 02111-1307, USA.  */
 
 #include "events-mod.h"
 
+void enqueue_focus_event (Widget wants_it, Lisp_Object frame, int in_p);
 static void handle_focus_event_1 (struct frame *f, int in_p);
+static void handle_focus_event_2 (Window w, struct frame *f, int in_p);
 
 static struct event_stream *Xt_event_stream;
 
@@ -87,11 +89,12 @@ XtAppContext Xt_app_con;
 int x_allow_sendevents;
 
 #ifdef DEBUG_XEMACS
-int debug_x_events;
+Fixnum debug_x_events;
 #endif
 
 static int process_events_occurred;
 static int tty_events_occurred;
+static Widget widget_with_focus;
 
 /* Mask of bits indicating the descriptors that we wait for input on */
 extern SELECT_TYPE input_wait_mask, process_only_mask, tty_only_mask;
@@ -309,9 +312,11 @@ maybe_define_x_key_as_self_inserting_character (KeySym keysym, Lisp_Object symbo
     {
       extern Lisp_Object Vcurrent_global_map;
       extern Lisp_Object Qascii_character;
-      Fput (symbol, Qascii_character, character);
-      if (NILP (Flookup_key (Vcurrent_global_map, symbol, Qnil)))
-       Fdefine_key (Vcurrent_global_map, symbol, Qself_insert_command);
+      if (NILP (Flookup_key (Vcurrent_global_map, symbol, Qnil))) 
+        {
+         Fput (symbol, Qascii_character, character);
+         Fdefine_key (Vcurrent_global_map, symbol, Qself_insert_command); 
+        }
     }
 }
 
@@ -456,7 +461,14 @@ x_reset_modifier_mapping (struct device *d)
   xd->lock_interpretation = 0;
 
   if (xd->x_modifier_keymap)
-    XFreeModifiermap (xd->x_modifier_keymap);
+    {
+      XFreeModifiermap (xd->x_modifier_keymap);
+      /* Set it to NULL in case we receive two MappingModifier events in a
+         row, and the second is processed during some CHECK_QUITs within
+         x_reset_key_mapping. If that happens, XFreeModifierMap will be
+         called twice on the same map, and we crash.  */
+      xd->x_modifier_keymap = NULL;
+    }
 
   x_reset_key_mapping (d);
 
@@ -863,7 +875,7 @@ emacs_Xt_mapping_action (Widget w, XEvent* event)
     case MappingKeyboard:  x_reset_key_mapping      (d); break;
     case MappingModifier:  x_reset_modifier_mapping (d); break;
     case MappingPointer:   /* Do something here? */      break;
-    default: abort();
+    default: ABORT();
     }
 }
 
@@ -1535,12 +1547,23 @@ x_event_to_emacs_event (XEvent *x_event, Lisp_Event *emacs_event)
 static void
 handle_focus_event_1 (struct frame *f, int in_p)
 {
+  handle_focus_event_2 (XtWindow (FRAME_X_TEXT_WIDGET (f)), f, in_p);
+}
+
+static void
+handle_focus_event_2 (Window win, struct frame *f, int in_p)
+{
+  /* Although this treats focus differently for all widgets (including
+     the frame) it seems to work ok. */
+  Widget needs_it = XtWindowToWidget (FRAME_X_DISPLAY (f), win);
+
 #if XtSpecificationRelease > 5
-  Widget focus_widget = XtGetKeyboardFocusWidget (FRAME_X_TEXT_WIDGET (f));
+  widget_with_focus = XtGetKeyboardFocusWidget (FRAME_X_TEXT_WIDGET (f));
 #endif
 #ifdef HAVE_XIM
   XIM_focus_event (f, in_p);
 #endif /* HAVE_XIM */
+
   /* On focus change, clear all memory of sticky modifiers
      to avoid non-intuitive behavior. */
   clear_sticky_modifiers (XDEVICE (FRAME_DEVICE (f)));
@@ -1564,13 +1587,24 @@ handle_focus_event_1 (struct frame *f, int in_p)
      click in the frame. Why is this?  */
   if (in_p
 #if XtSpecificationRelease > 5
-      && FRAME_X_TEXT_WIDGET (f) != focus_widget
+      && needs_it != widget_with_focus
 #endif
       )
     {
-      lw_set_keyboard_focus (FRAME_X_SHELL_WIDGET (f),
-                            FRAME_X_TEXT_WIDGET (f));
+      lw_set_keyboard_focus (FRAME_X_SHELL_WIDGET (f), needs_it);
     }
+
+  /* If we are focusing on a native widget then record and exit. */
+  if (needs_it != FRAME_X_TEXT_WIDGET (f)) {
+    widget_with_focus = needs_it;
+    return;
+  }
+
+  /* We have the focus now. See comment in
+     emacs_Xt_handle_widget_losing_focus (). */
+  if (in_p)
+    widget_with_focus = NULL;
+
   /* do the generic event-stream stuff. */
   {
     Lisp_Object frm;
@@ -1587,6 +1621,40 @@ handle_focus_event_1 (struct frame *f, int in_p)
   }
 }
 
+/* Create a synthetic X focus event. */
+void
+enqueue_focus_event (Widget wants_it, Lisp_Object frame, int in_p)
+{
+  Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
+  Lisp_Event *ev          = XEVENT (emacs_event);
+  XEvent *x_event = &ev->event.magic.underlying_x_event;
+
+  x_event->type = in_p ? FocusIn : FocusOut;
+  x_event->xfocus.window = XtWindow (wants_it);
+
+  ev->channel              = frame;
+  ev->event_type           = magic_event;
+
+  enqueue_Xt_dispatch_event (emacs_event);
+}
+
+/* The idea here is that when a widget glyph gets unmapped we don't
+   want the focus to stay with it if it has focus - because it may
+   well just get deleted next and then we have lost the focus until the
+   user does something. So handle_focus_event_1 records the widget
+   with keyboard focus when FocusOut is processed, and then, when a
+   widget gets unmapped, it calls this function to restore focus if
+   appropriate. */
+void emacs_Xt_handle_widget_losing_focus (struct frame* f, Widget losing_widget);
+void
+emacs_Xt_handle_widget_losing_focus (struct frame* f, Widget losing_widget)
+{
+  if (losing_widget == widget_with_focus)
+    {
+      handle_focus_event_1 (f, 1);
+    }
+}
+
 /* This is called from the external-widget code */
 
 void emacs_Xt_handle_focus_event (XEvent *event);
@@ -1763,6 +1831,41 @@ handle_client_message (struct frame *f, XEvent *event)
     }
 }
 
+/* #### I'm struggling to understand how the X event loop really works. 
+   Here is the problem:
+   
+   When widgets get mapped / changed etc the actual display updates
+   are done asynchronously via X events being processed - this
+   normally happens when XtAppProcessEvent() gets called. However, if
+   we are executing lisp code or even doing redisplay we won't
+   necessarily process X events for a very long time. This has the
+   effect of widgets only getting updated when XEmacs only goes into
+   idle, or some other event causes processing of the X event queue.
+
+   XtAppProcessEvent can get called from the following places:
+
+     emacs_Xt_next_event () - this is normal event processing, almost
+     any non-X event will take precedence and this means that we
+     cannot rely on it to do the right thing at the right time for
+     widget display.
+
+     drain_X_queue () - this happens when SIGIO gets tripped,
+     processing the event queue allows C-g to be checked for. It gets
+     called from emacs_Xt_event_pending_p ().
+
+   In order to solve this I have tried introducing a list primitive -
+   dispatch-non-command-events - which forces processing of X events
+   related to display. Unfortunately this has a number of problems,
+   one is that it is possible for event_stream_event_pending_p to
+   block for ever if there isn't actually an event. I guess this can
+   happen if we drop the synthetic event for reason. It also relies on
+   SIGIO processing which makes things rather fragile.
+
+   People have seen behaviour whereby XEmacs blocks until you move the
+   mouse. This seems to indicate that dispatch-non-command-events is
+   blocking. It may be that in a SIGIO world forcing SIGIO processing
+   does the wrong thing.
+*/
 static void
 emacs_Xt_force_event_pending (struct frame* f)
 {
@@ -1778,8 +1881,8 @@ emacs_Xt_force_event_pending (struct frame* f)
   /* Send the drop message */
   XSendEvent(dpy, XtWindow (FRAME_X_SHELL_WIDGET (f)),
             True, NoEventMask, &event);
-  /* Force event pending to check the X queue. */
-  quit_check_signal_tick_count++;
+  /* We rely on SIGIO and friends to realise we have generated an
+     event. */
 }
 
 static void
@@ -1864,7 +1967,7 @@ emacs_Xt_handle_magic_event (Lisp_Event *emacs_event)
       if (FRAME_X_EXTERNAL_WINDOW_P (f))
        break;
 #endif
-      handle_focus_event_1 (f, event->type == FocusIn);
+      handle_focus_event_2 (event->xfocus.window, f, event->type == FocusIn);
       break;
 
     case ClientMessage:
@@ -1913,12 +2016,14 @@ static int timeout_id_tick;
 /* Xt interval id's might not fit into an int (they're pointers, as it
    happens), so we need to provide a conversion list. */
 
+/* pending_timeouts is a set (unordered), implemented as a stack.
+   completed_timeouts* is a queue. */
 static struct Xt_timeout
 {
   int id;
   XtIntervalId interval_id;
   struct Xt_timeout *next;
-} *pending_timeouts, *completed_timeouts;
+} *pending_timeouts, *completed_timeouts_head, *completed_timeouts_tail;
 
 static struct Xt_timeout_blocktype
 {
@@ -1931,7 +2036,7 @@ Xt_timeout_callback (XtPointer closure, XtIntervalId *id)
 {
   struct Xt_timeout *timeout = (struct Xt_timeout *) closure;
   struct Xt_timeout *t2 = pending_timeouts;
-  /* Remove this one from the list of pending timeouts */
+  /* Remove this one from the set of pending timeouts */
   if (t2 == timeout)
     pending_timeouts = pending_timeouts->next;
   else
@@ -1940,9 +2045,13 @@ Xt_timeout_callback (XtPointer closure, XtIntervalId *id)
       assert (t2->next);
       t2->next = t2->next->next;
     }
-  /* Add this one to the list of completed timeouts */
-  timeout->next = completed_timeouts;
-  completed_timeouts = timeout;
+  /* Add this one to the queue of completed timeouts */
+  timeout->next = NULL;
+  if (completed_timeouts_head)
+    completed_timeouts_tail->next = timeout;
+  else
+    completed_timeouts_head = timeout;
+  completed_timeouts_tail = timeout;
 }
 
 static int
@@ -1997,24 +2106,27 @@ emacs_Xt_remove_timeout (int id)
        XtRemoveTimeOut (timeout->interval_id);
     }
 
-  /* It could be that the Xt call back was already called but we didn't convert
-     into an Emacs event yet */
-  if (!timeout && completed_timeouts)
+  /* It could be that Xt_timeout_callback was already called but we didn't
+     convert into an Emacs event yet */
+  if (!timeout && completed_timeouts_head)
     {
-      /* Code duplication! */
-      if (id == completed_timeouts->id)
+      /* Thank God for code duplication! */
+      if (id == completed_timeouts_head->id)
        {
-         timeout = completed_timeouts;
-         completed_timeouts = completed_timeouts->next;
+         timeout = completed_timeouts_head;
+         completed_timeouts_head = completed_timeouts_head->next;
+         /* this may not be necessary? */
+         if (!completed_timeouts_head) completed_timeouts_tail = NULL;
        }
       else
        {
-         t2 = completed_timeouts;
+         t2 = completed_timeouts_head;
          while (t2->next && t2->next->id != id) t2 = t2->next;
-         if ( t2->next)   /*found it */
+         if (t2->next)   /* found it */
            {
              timeout = t2->next;
              t2->next = t2->next->next;
+             if (!t2->next) completed_timeouts_tail = t2;
            }
        }
     }
@@ -2029,9 +2141,11 @@ emacs_Xt_remove_timeout (int id)
 static void
 Xt_timeout_to_emacs_event (Lisp_Event *emacs_event)
 {
-  struct Xt_timeout *timeout = completed_timeouts;
+  struct Xt_timeout *timeout = completed_timeouts_head;
   assert (timeout);
-  completed_timeouts = completed_timeouts->next;
+  completed_timeouts_head = completed_timeouts_head->next;
+  /* probably unnecessary */
+  if (!completed_timeouts_head) completed_timeouts_tail = NULL;
   emacs_event->event_type = timeout_event;
   /* timeout events have nil as channel */
   emacs_event->timestamp  = 0; /* #### wrong!! */
@@ -2273,7 +2387,7 @@ Xt_process_to_emacs_event (Lisp_Event *emacs_event)
          return;
        }
     }
-  abort ();
+  ABORT ();
 }
 
 static void
@@ -2603,7 +2717,7 @@ emacs_Xt_next_event (Lisp_Event *emacs_event)
  we_didnt_get_an_event:
 
   while (NILP (dispatch_event_queue) &&
-        !completed_timeouts         &&
+        !completed_timeouts_head    &&
         !fake_event_occurred        &&
         !process_events_occurred    &&
         !tty_events_occurred)
@@ -2657,7 +2771,7 @@ emacs_Xt_next_event (Lisp_Event *emacs_event)
       if (!Xt_tty_to_emacs_event (emacs_event))
        goto we_didnt_get_an_event;
     }
-  else if (completed_timeouts)
+  else if (completed_timeouts_head)
     Xt_timeout_to_emacs_event (emacs_event);
   else if (fake_event_occurred)
     {
@@ -2843,8 +2957,47 @@ emacs_Xt_quit_p (void)
 static void
 drain_X_queue (void)
 {
+  Lisp_Object devcons, concons;
+  CONSOLE_LOOP (concons)
+  {
+    struct console *con = XCONSOLE (XCAR (concons));
+    if (!con->input_enabled)
+      continue;
+
+    /* sjt sez: Have you tried the loop over devices with XtAppPending(),
+       not XEventsQueued()?
+       Ben Sigelman sez: No.
+       sjt sez: I'm guessing that the reason that your patch "works" is this:
+
+       +      struct device* d;
+       +      Display* display;
+       +      d = XDEVICE (XCAR (devcons));
+       +      if (DEVICE_X_P (d) && DEVICE_X_DISPLAY (d)) {
+
+       Ie, if the device goes down, XEmacs detects that and deletes it.
+       Then the if() fails (DEVICE_X_DISPLAY(d) is NULL), and we don't go
+       into the Xlib-of-no-return.  If you know different, I'd like to hear
+       about it. ;-)
+
+       These ideas haven't been tested; the code below works for Ben.
+    */
+    CONSOLE_DEVICE_LOOP (devcons, con)
+    {
+      struct device* d;
+      Display* display;
+      d = XDEVICE (XCAR (devcons));
+      if (DEVICE_X_P (d) && DEVICE_X_DISPLAY (d)) {
+        display = DEVICE_X_DISPLAY (d);
+        while (XEventsQueued (display, QueuedAfterReading))
+          XtAppProcessEvent (Xt_app_con, XtIMXEvent);
+      }
+    }
+  }
+  /* This is the old code, before Ben Sigelman's patch. */
+  /*
   while (XtAppPending (Xt_app_con) & XtIMXEvent)
     XtAppProcessEvent (Xt_app_con, XtIMXEvent);
+  */
 }
 
 static int
@@ -2949,7 +3102,11 @@ emacs_Xt_event_pending_p (int user_p)
   /* quit_check_signal_tick_count is volatile so try to avoid race conditions
      by using a temporary variable */
   tick_count_val = quit_check_signal_tick_count;
-  if (last_quit_check_signal_tick_count != tick_count_val)
+  if (last_quit_check_signal_tick_count != tick_count_val
+#if !defined (SIGIO) || defined (CYGWIN)
+      || (XtIMXEvent & XtAppPending (Xt_app_con))
+#endif 
+      )
     {
       last_quit_check_signal_tick_count = tick_count_val;
 
@@ -3023,7 +3180,7 @@ XtConvertArgRec Const colorConvertArgs[] = {
 
 /* JH: We use this because I think there's a possibility this
    is called before the device is properly set up, in which case
-   I don't want to abort. */
+   I don't want to ABORT. */
 extern struct device *get_device_from_display_1 (Display *dpy);
 
 static
@@ -3226,7 +3383,7 @@ vars_of_event_Xt (void)
   dispatch_event_queue = Qnil;
   staticpro (&dispatch_event_queue);
   dispatch_event_queue_tail = Qnil;
-  pdump_wire (&dispatch_event_queue_tail);
+  dump_add_root_object (&dispatch_event_queue_tail);
 
   DEFVAR_BOOL ("x-allow-sendevents", &x_allow_sendevents /*
 *Non-nil means to allow synthetic events.  Nil means they are ignored.
@@ -3272,8 +3429,9 @@ void
 init_event_Xt_late (void) /* called when already initialized */
 {
   timeout_id_tick = 1;
-  pending_timeouts = 0;
-  completed_timeouts = 0;
+  pending_timeouts = NULL;
+  completed_timeouts_head = NULL; /* queue is empty */
+  completed_timeouts_tail = NULL; /* just to be picky */
 
   event_stream = Xt_event_stream;