import xemacs-21.2.37
[chise/xemacs-chise.git.1] / src / event-msw.c
index 5b7d03b..a09c81f 100644 (file)
@@ -69,13 +69,13 @@ Boston, MA 02111-1307, USA.  */
 #ifdef HAVE_MSG_SELECT
 #include "sysfile.h"
 #include "console-tty.h"
-#elif defined(__CYGWIN32__)
+#elif defined(CYGWIN)
 typedef unsigned int SOCKET;
 #endif
 #include <io.h>
 #include <errno.h>
 
-#if !(defined(__CYGWIN32__) || defined(__MINGW32__))
+#if !(defined(CYGWIN) || defined(MINGW))
 # include <shlobj.h>   /* For IShellLink */
 #endif
 
@@ -96,7 +96,8 @@ static Lisp_Object mswindows_find_frame (HWND hwnd);
 static Lisp_Object mswindows_find_console (HWND hwnd);
 static Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods,
                                                  int extendedp);
-static int mswindows_modifier_state (BYTE* keymap, int has_AltGr);
+static int mswindows_modifier_state (BYTE* keymap, DWORD fwKeys,
+                                    int has_AltGr);
 static void mswindows_set_chord_timer (HWND hwnd);
 static int mswindows_button2_near_enough (POINTS p1, POINTS p2);
 static int mswindows_current_layout_has_AltGr (void);
@@ -151,7 +152,7 @@ int mswindows_mouse_button_max_skew_y;
 int mswindows_mouse_button_tolerance;
 
 #ifdef DEBUG_XEMACS
-int mswindows_debug_events;
+int debug_mswindows_events;
 #endif
 
 /* This is the event signaled by the event pump.
@@ -161,6 +162,8 @@ static int mswindows_in_modal_loop;
 
 /* Count of wound timers */
 static int mswindows_pending_timers_count;
+
+static DWORD mswindows_last_mouse_button_state;
 \f
 /************************************************************************/
 /*                Pipe instream - reads process output                  */
@@ -471,7 +474,7 @@ init_slurp_stream (void)
 #define NTPIPE_SHOVE_STREAM_DATA(stream) \
   LSTREAM_TYPE_DATA (stream, ntpipe_shove)
 
-#define MAX_SHOVE_BUFFER_SIZE 128
+#define MAX_SHOVE_BUFFER_SIZE 512
 
 struct ntpipe_shove_stream
 {
@@ -505,15 +508,18 @@ shove_thread (LPVOID vparam)
       InterlockedIncrement (&s->idle_p);
       WaitForSingleObject (s->hev_thread, INFINITE);
 
-      if (s->die_p)
-       break;
-
-      /* Write passed buffer */
-      if (!WriteFile (s->hpipe, s->buffer, s->size, &bytes_written, NULL)
-         || bytes_written != s->size)
+      /* Write passed buffer if any */
+      if (s->size > 0)
        {
-         s->error_p = TRUE;
-         InterlockedIncrement (&s->die_p);
+         if (!WriteFile (s->hpipe, s->buffer, s->size, &bytes_written, NULL)
+             || bytes_written != s->size)
+           {
+             s->error_p = TRUE;
+             InterlockedIncrement (&s->die_p);
+           }
+         /* Set size to zero so we won't write it again if the closer sets
+            die_p and kicks us */
+         s->size = 0;
        }
 
       if (s->die_p)
@@ -546,6 +552,15 @@ make_ntpipe_output_stream (HANDLE hpipe, LPARAM param)
       return Qnil;
     }
 
+  /* Set the priority of the thread higher so we don't end up waiting
+     on it to send things. */
+  if (!SetThreadPriority (s->hthread, THREAD_PRIORITY_HIGHEST))
+    {
+      CloseHandle (s->hthread);
+      Lstream_delete (lstr);
+      return Qnil;
+    }
+
   /* hev_thread is an auto-reset event, initially nonsignaled */
   s->hev_thread = CreateEvent (NULL, FALSE, FALSE, NULL);
 
@@ -586,6 +601,10 @@ ntpipe_shove_writer (Lstream *stream, const unsigned char *data, size_t size)
   /* Start output */
   InterlockedDecrement (&s->idle_p);
   SetEvent (s->hev_thread);
+  /* Give it a chance to run -- this dramatically improves performance
+     of things like crypt. */
+  if (xSwitchToThread) /* not in Win9x or NT 3.51 */
+    (void) xSwitchToThread ();
   return size;
 }
 
@@ -604,14 +623,18 @@ ntpipe_shove_closer (Lstream *stream)
   /* Force thread stop */
   InterlockedIncrement (&s->die_p);
 
-  /* Close pipe handle, possibly breaking it */
-  CloseHandle (s->hpipe);
-
-  /* Thread will end upon unblocking */
+  /* Thread will end upon unblocking.  If it's already unblocked this will
+     do nothing, but the thread won't look at die_p until it's written any
+     pending output. */
   SetEvent (s->hev_thread);
 
   /* Wait while thread terminates */
   WaitForSingleObject (s->hthread, INFINITE);
+
+  /* Close pipe handle, possibly breaking it */
+  CloseHandle (s->hpipe);
+
+  /* Close the thread handle */
   CloseHandle (s->hthread);
 
   /* Destroy the event */
@@ -940,7 +963,7 @@ mswindows_enqueue_process_event (Lisp_Process* p)
 
 static void
 mswindows_enqueue_mouse_button_event (HWND hwnd, UINT msg, POINTS where,
-                                     DWORD when)
+                                     int mods, DWORD when)
 {
   int downp = (msg == WM_LBUTTONDOWN || msg == WM_MBUTTONDOWN ||
               msg == WM_RBUTTONDOWN);
@@ -960,7 +983,7 @@ mswindows_enqueue_mouse_button_event (HWND hwnd, UINT msg, POINTS where,
     ((msg==WM_RBUTTONDOWN || msg==WM_RBUTTONUP) ? 3 : 2);
   event->event.button.x = where.x;
   event->event.button.y = where.y;
-  event->event.button.modifiers = mswindows_modifier_state (NULL, 0);
+  event->event.button.modifiers = mswindows_modifier_state (NULL, mods, 0);
 
   if (downp)
     {
@@ -1251,27 +1274,43 @@ mswindows_drain_windows_queue (void)
 
   while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
     {
-      /* We have to translate messages that are not sent to the main
-         window. This is so that key presses work ok in things like
-         edit fields. However, we *musn't* translate message for the
-         main window as this is handled in the wnd proc.
+      char class_name_buf [sizeof (XEMACS_CLASS) + 2] = "";
+
+      /* Don't translate messages destined for a dialog box, this
+        makes keyboard traversal work. I think?? */
+      if (mswindows_is_dialog_msg (&msg))
+       {
+         mswindows_unmodalize_signal_maybe ();
+         continue;
+       }
+
+      /* We have to translate messages that are not sent to an XEmacs
+         frame. This is so that key presses work ok in things like
+         edit fields. However, we *musn't* translate message for XEmacs
+         frames as this is handled in the wnd proc.
          We also have to avoid generating paint magic events for windows
         that aren't XEmacs frames */
-      if (GetWindowLong (msg.hwnd, GWL_STYLE) & (WS_CHILD|WS_POPUP))
+      /* GetClassName will truncate a longer class name. By adding one
+        extra character, we are forcing textual comparison to fail
+        if the name is longer than XEMACS_CLASS */
+
+      GetClassName (msg.hwnd, class_name_buf, sizeof (class_name_buf) - 1);
+      if (stricmp (class_name_buf, XEMACS_CLASS) != 0)
        {
+         /* Not an XEmacs frame */
          TranslateMessage (&msg);
        }
       else if (msg.message == WM_PAINT)
        {
          struct mswindows_frame* msframe;
-         
+
          /* hdc will be NULL unless this is a subwindow - in which case we
             shouldn't have received a paint message for it here. */
          assert (msg.wParam == 0);
 
          /* Queue a magic event for handling when safe */
-         msframe = FRAME_MSWINDOWS_DATA (
-                                         XFRAME (mswindows_find_frame (msg.hwnd)));
+         msframe =
+           FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (msg.hwnd)));
          if (!msframe->paint_pending)
            {
              msframe->paint_pending = 1;
@@ -1375,7 +1414,7 @@ mswindows_need_event (int badly_p)
            {
              mswindows_drain_windows_queue ();
            }
-         else 
+         else
            {
 #ifdef HAVE_TTY
              /* Look for a TTY event */
@@ -1389,7 +1428,7 @@ mswindows_need_event (int badly_p)
                      struct console *c = tty_find_console_from_fd (i);
                      Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
                      Lisp_Event* event = XEVENT (emacs_event);
-                     
+
                      assert (c);
                      if (read_event_from_tty_or_stream_desc (event, c, i))
                        {
@@ -1408,7 +1447,7 @@ mswindows_need_event (int badly_p)
                        {
                          Lisp_Process *p =
                            get_process_from_usid (FD_TO_USID(i));
-                         
+
                          mswindows_enqueue_process_event (p);
                        }
                      else
@@ -1600,7 +1639,7 @@ mswindows_dde_callback (UINT uType, UINT uFmt, HCONV hconv,
          if (*end)
            return DDE_FNOTPROCESSED;
 
-#ifdef __CYGWIN32__
+#ifdef CYGWIN
          filename = alloca (cygwin32_win32_to_posix_path_list_buf_size (cmd) + 5);
          strcpy (filename, "file:");
          cygwin32_win32_to_posix_path_list (cmd, filename+5);
@@ -1684,7 +1723,7 @@ mswindows_handle_paint (struct frame *frame)
 }
 
 /*
- * Returns 1 if a key is a real modifier or special key, which 
+ * Returns 1 if a key is a real modifier or special key, which
  * is better handled by DefWindowProc
  */
 static int
@@ -1755,7 +1794,7 @@ mswindows_handle_sticky_modifiers (WPARAM wParam, LPARAM lParam,
         This means that we need to distinguish between an
         auto-repeated key and a key pressed and released a bunch
         of times. */
-      else if (downp && !keyp ||
+      else if ((downp && !keyp) ||
               (downp && keyp && last_downkey &&
                (wParam != last_downkey ||
                 /* the "previous key state" bit indicates autorepeat */
@@ -1810,24 +1849,24 @@ do {                                            \
     }                                          \
 } while (0)
 
-      if (wParam == VK_CONTROL && (lParam & 0x1000000)
+      if ((wParam == VK_CONTROL && (lParam & 0x1000000))
          || wParam == VK_RCONTROL)
        FROB (XEMSW_RCONTROL);
-      if (wParam == VK_CONTROL && !(lParam & 0x1000000)
+      if ((wParam == VK_CONTROL && !(lParam & 0x1000000))
          || wParam == VK_LCONTROL)
        FROB (XEMSW_LCONTROL);
 
-      if (wParam == VK_SHIFT && (lParam & 0x1000000)
+      if ((wParam == VK_SHIFT && (lParam & 0x1000000))
          || wParam == VK_RSHIFT)
        FROB (XEMSW_RSHIFT);
-      if (wParam == VK_SHIFT && !(lParam & 0x1000000)
+      if ((wParam == VK_SHIFT && !(lParam & 0x1000000))
          || wParam == VK_LSHIFT)
        FROB (XEMSW_LSHIFT);
 
-      if (wParam == VK_MENU && (lParam & 0x1000000)
+      if ((wParam == VK_MENU && (lParam & 0x1000000))
          || wParam == VK_RMENU)
        FROB (XEMSW_RMENU);
-      if (wParam == VK_MENU && !(lParam & 0x1000000)
+      if ((wParam == VK_MENU && !(lParam & 0x1000000))
          || wParam == VK_LMENU)
        FROB (XEMSW_LMENU);
     }
@@ -1889,6 +1928,8 @@ clear_sticky_modifiers (void)
 
 #ifdef DEBUG_XEMACS
 
+#if 0
+
 static void
 output_modifier_keyboard_state (void)
 {
@@ -1919,6 +1960,8 @@ output_modifier_keyboard_state (void)
              keymap[VK_RSHIFT] & 0x1 ? 1 : 0);
 }
 
+#endif
+
 /* try to debug the stuck-alt-key problem.
 
  #### this happens only inconsistently, and may only happen when using
@@ -1964,14 +2007,14 @@ output_alt_keyboard_state (void)
 /*           asyncstate[2] & 0x1 ? 1 : 0); */
 }
 
-#endif /* DEBUG_XEMACS */  
+#endif /* DEBUG_XEMACS */
 
 
 /*
  * The windows procedure for the window class XEMACS_CLASS
  */
 LRESULT WINAPI
-mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+mswindows_wnd_proc (HWND hwnd, UINT message_, WPARAM wParam, LPARAM lParam)
 {
   /* Note: Remember to initialize emacs_event and event before use.
      This code calls code that can GC. You must GCPRO before calling such code. */
@@ -1982,14 +2025,22 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
   struct frame *frame;
   struct mswindows_frame* msframe;
 
+  /* Not perfect but avoids crashes. There is potential for wierd
+     behavior here. */
+  if (gc_in_progress)
+    goto defproc;
+
   assert (!GetWindowLong (hwnd, GWL_USERDATA));
-  switch (message)
+  switch (message_)
     {
     case WM_DESTROYCLIPBOARD:
       /* We own the clipboard and someone else wants it.  Delete our
         cached copy of the clipboard contents so we'll ask for it from
-        Windows again when someone does a paste. */
-      handle_selection_clear(QCLIPBOARD);
+        Windows again when someone does a paste, and destroy any memory
+         objects we hold on the clipboard that are not in the list of types
+         that Windows will delete itself. */
+      mswindows_destroy_selection (QCLIPBOARD);
+      handle_selection_clear (QCLIPBOARD);
       break;
 
     case WM_ERASEBKGND:
@@ -2013,14 +2064,14 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
        int should_set_keymap = 0;
 
 #ifdef DEBUG_XEMACS
-       if (mswindows_debug_events)
+       if (debug_mswindows_events)
          {
            stderr_out ("%s wparam=%d lparam=%d\n",
-                       message == WM_KEYUP ? "WM_KEYUP" : "WM_SYSKEYUP",
+                       message_ == WM_KEYUP ? "WM_KEYUP" : "WM_SYSKEYUP",
                        wParam, (int)lParam);
            output_alt_keyboard_state ();
-         }         
-#endif /* DEBUG_XEMACS */  
+         }
+#endif /* DEBUG_XEMACS */
 
        mswindows_handle_sticky_modifiers (wParam, lParam, 0, 1);
        if (wParam == VK_CONTROL)
@@ -2037,12 +2088,12 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          }
 
        if (should_set_keymap)
-         //        && (message != WM_SYSKEYUP
+         //        && (message_ != WM_SYSKEYUP
          //    || NILP (Vmenu_accelerator_enabled)))
          SetKeyboardState (keymap);
 
       }
-      
+
       if (key_needs_default_processing_p (wParam))
        goto defproc;
       else
@@ -2052,7 +2103,7 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
     case WM_SYSKEYDOWN:
 
       /* In some locales the right-hand Alt key is labelled AltGr. This key
-       * should produce alternative charcaters when combined with another key.
+       * should produce alternative characters when combined with another key.
        * eg on a German keyboard pressing AltGr+q should produce '@'.
        * AltGr generates exactly the same keystrokes as LCtrl+RAlt. But if
        * TranslateMessage() is called with *any* combination of Ctrl+Alt down,
@@ -2070,14 +2121,14 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
        int sticky_changed;
 
 #ifdef DEBUG_XEMACS
-       if (mswindows_debug_events)
+       if (debug_mswindows_events)
          {
            stderr_out ("%s wparam=%d lparam=%d\n",
-                       message == WM_KEYDOWN ? "WM_KEYDOWN" : "WM_SYSKEYDOWN",
+                       message_ == WM_KEYDOWN ? "WM_KEYDOWN" : "WM_SYSKEYDOWN",
                        wParam, (int)lParam);
            output_alt_keyboard_state ();
-         }         
-#endif /* DEBUG_XEMACS */  
+         }
+#endif /* DEBUG_XEMACS */
 
        GetKeyboardState (keymap_orig);
        frame = XFRAME (mswindows_find_frame (hwnd));
@@ -2087,7 +2138,7 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
            GetKeyboardState (keymap_sticky);
            if (keymap_sticky[VK_MENU] & 0x80)
              {
-               message = WM_SYSKEYDOWN;
+               message_ = WM_SYSKEYDOWN;
                /* We have to set the "context bit" so that the
                   TranslateMessage() call below that generates the
                   SYSCHAR message does its thing; see the documentation
@@ -2098,7 +2149,7 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
        else
          memcpy (keymap_sticky, keymap_orig, 256);
 
-       mods = mswindows_modifier_state (keymap_sticky, has_AltGr);
+       mods = mswindows_modifier_state (keymap_sticky, (DWORD) -1, has_AltGr);
 
        /* Handle non-printables */
        if (!NILP (keysym = mswindows_key_to_emacs_keysym (wParam, mods,
@@ -2116,9 +2167,9 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
            MSG msg, tranmsg;
            int potential_accelerator = 0;
            int got_accelerator = 0;
-         
+
            msg.hwnd = hwnd;
-           msg.message = message;
+           msg.message = message_;
            msg.wParam = wParam;
            msg.lParam = lParam;
            msg.time = GetMessageTime();
@@ -2139,7 +2190,7 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
              }
 
            if (!NILP (Vmenu_accelerator_enabled) &&
-               !(mods & XEMACS_MOD_SHIFT) && message == WM_SYSKEYDOWN)
+               !(mods & XEMACS_MOD_SHIFT) && message_ == WM_SYSKEYDOWN)
              potential_accelerator = 1;
 
            /* Remove shift modifier from an ascii character */
@@ -2186,7 +2237,7 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
                    ++mswindows_quit_chars_count;
                  }
                else if (potential_accelerator && !got_accelerator &&
-                        msw_char_is_accelerator (frame, ch))
+                        mswindows_char_is_accelerator (frame, ch))
                  {
                    got_accelerator = 1;
                    break;
@@ -2197,7 +2248,7 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
            /* This generates WM_SYSCHAR messages, which are interpreted
               by DefWindowProc as the menu selections. */
            if (got_accelerator)
-             { 
+             {
                SetKeyboardState (keymap_sticky);
                TranslateMessage (&msg);
                SetKeyboardState (keymap_orig);
@@ -2218,8 +2269,10 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       /* Real middle mouse button has nothing to do with emulated one:
         if one wants to exercise fingers playing chords on the mouse,
         he is allowed to do that! */
-      mswindows_enqueue_mouse_button_event (hwnd, message,
-                                           MAKEPOINTS (lParam), GetMessageTime());
+      mswindows_enqueue_mouse_button_event (hwnd, message_,
+                                           MAKEPOINTS (lParam),
+                                           wParam &~ MK_MBUTTON,
+                                           GetMessageTime());
       break;
 
     case WM_LBUTTONUP:
@@ -2237,7 +2290,11 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          msframe->button2_is_down = 0;
          msframe->ignore_next_rbutton_up = 1;
          mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP,
-                                               MAKEPOINTS (lParam), GetMessageTime());
+                                               MAKEPOINTS (lParam),
+                                               wParam
+                                               &~ (MK_LBUTTON | MK_MBUTTON
+                                                   | MK_RBUTTON),
+                                               GetMessageTime());
        }
       else
        {
@@ -2245,10 +2302,14 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
            {
              msframe->button2_need_rbutton = 0;
              mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
-                                                   MAKEPOINTS (lParam), GetMessageTime());
+                                                   MAKEPOINTS (lParam),
+                                                   wParam &~ MK_LBUTTON,
+                                                   GetMessageTime());
            }
          mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONUP,
-                                               MAKEPOINTS (lParam), GetMessageTime());
+                                               MAKEPOINTS (lParam),
+                                               wParam &~ MK_LBUTTON,
+                                               GetMessageTime());
        }
       break;
 
@@ -2267,7 +2328,11 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          msframe->button2_is_down = 0;
          msframe->ignore_next_lbutton_up = 1;
          mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP,
-                                               MAKEPOINTS (lParam), GetMessageTime());
+                                               MAKEPOINTS (lParam),
+                                               wParam
+                                               &~ (MK_LBUTTON | MK_MBUTTON
+                                                   | MK_RBUTTON),
+                                               GetMessageTime());
        }
       else
        {
@@ -2275,10 +2340,14 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
            {
              msframe->button2_need_lbutton = 0;
              mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
-                                                   MAKEPOINTS (lParam), GetMessageTime());
+                                                   MAKEPOINTS (lParam),
+                                                   wParam &~ MK_RBUTTON,
+                                                   GetMessageTime());
            }
          mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONUP,
-                                               MAKEPOINTS (lParam), GetMessageTime());
+                                               MAKEPOINTS (lParam),
+                                               wParam &~ MK_RBUTTON,
+                                               GetMessageTime());
        }
       break;
 
@@ -2290,18 +2359,28 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          KillTimer (hwnd, BUTTON_2_TIMER_ID);
          msframe->button2_need_lbutton = 0;
          msframe->button2_need_rbutton = 0;
-         if (mswindows_button2_near_enough (msframe->last_click_point, MAKEPOINTS (lParam)))
+         if (mswindows_button2_near_enough (msframe->last_click_point,
+                                            MAKEPOINTS (lParam)))
            {
              mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN,
-                                                   MAKEPOINTS (lParam), GetMessageTime());
+                                                   MAKEPOINTS (lParam),
+                                                   wParam
+                                                   &~ (MK_LBUTTON | MK_MBUTTON
+                                                       | MK_RBUTTON),
+                                                   GetMessageTime());
              msframe->button2_is_down = 1;
            }
          else
            {
              mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
-                                                   msframe->last_click_point, msframe->last_click_time);
+                                                   msframe->last_click_point,
+                                                   msframe->last_click_mods
+                                                   &~ MK_RBUTTON,
+                                                   msframe->last_click_time);
              mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
-                                                   MAKEPOINTS (lParam), GetMessageTime());
+                                                   MAKEPOINTS (lParam),
+                                                   wParam &~ MK_LBUTTON,
+                                                   GetMessageTime());
            }
        }
       else
@@ -2309,6 +2388,7 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          mswindows_set_chord_timer (hwnd);
          msframe->button2_need_rbutton = 1;
          msframe->last_click_point = MAKEPOINTS (lParam);
+         msframe->last_click_mods = wParam;
        }
       msframe->last_click_time =  GetMessageTime();
       break;
@@ -2321,18 +2401,28 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          KillTimer (hwnd, BUTTON_2_TIMER_ID);
          msframe->button2_need_lbutton = 0;
          msframe->button2_need_rbutton = 0;
-         if (mswindows_button2_near_enough (msframe->last_click_point, MAKEPOINTS (lParam)))
+         if (mswindows_button2_near_enough (msframe->last_click_point,
+                                            MAKEPOINTS (lParam)))
            {
              mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN,
-                                                   MAKEPOINTS (lParam), GetMessageTime());
+                                                   MAKEPOINTS (lParam),
+                                                   wParam
+                                                   &~ (MK_LBUTTON | MK_MBUTTON
+                                                       | MK_RBUTTON),
+                                                   GetMessageTime());
              msframe->button2_is_down = 1;
            }
          else
            {
              mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
-                                                   msframe->last_click_point, msframe->last_click_time);
+                                                   msframe->last_click_point,
+                                                   msframe->last_click_mods
+                                                   &~ MK_LBUTTON,
+                                                   msframe->last_click_time);
              mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
-                                                   MAKEPOINTS (lParam), GetMessageTime());
+                                                   MAKEPOINTS (lParam),
+                                                   wParam &~ MK_RBUTTON,
+                                                   GetMessageTime());
            }
        }
       else
@@ -2340,6 +2430,7 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          mswindows_set_chord_timer (hwnd);
          msframe->button2_need_lbutton = 1;
          msframe->last_click_point = MAKEPOINTS (lParam);
+         msframe->last_click_mods = wParam;
        }
       msframe->last_click_time =  GetMessageTime();
       break;
@@ -2354,13 +2445,19 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
            {
              msframe->button2_need_lbutton = 0;
              mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
-                                                   msframe->last_click_point, msframe->last_click_time);
+                                                   msframe->last_click_point,
+                                                   msframe->last_click_mods
+                                                   &~ MK_RBUTTON,
+                                                   msframe->last_click_time);
            }
          else if (msframe->button2_need_rbutton)
            {
              msframe->button2_need_rbutton = 0;
              mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
-                                                   msframe->last_click_point, msframe->last_click_time);
+                                                   msframe->last_click_point,
+                                                   msframe->last_click_mods
+                                                   &~ MK_LBUTTON,
+                                                   msframe->last_click_time);
            }
        }
       else
@@ -2391,7 +2488,8 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          event->event_type = pointer_motion_event;
          event->event.motion.x = MAKEPOINTS(lParam).x;
          event->event.motion.y = MAKEPOINTS(lParam).y;
-         event->event.motion.modifiers = mswindows_modifier_state (NULL, 0);
+         event->event.motion.modifiers =
+           mswindows_modifier_state (NULL, wParam, 0);
 
          mswindows_enqueue_dispatch_event (emacs_event);
        }
@@ -2427,9 +2525,7 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
              {
                /* I think this is safe since the text will only go away
                   when the toolbar does...*/
-               TO_EXTERNAL_FORMAT (LISP_STRING, btext,
-                                   C_STRING_ALLOCA, tttext->lpszText,
-                                   Qnative);
+               LISP_STRING_TO_EXTERNAL (btext, tttext->lpszText, Qnative);
              }
 #endif
          }
@@ -2461,7 +2557,7 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
         shouldn't have received a paint message for it here. */
       assert (wParam == 0);
 
-      /* Can't queue a magic event because windows goes modal and sends paint 
+      /* Can't queue a magic event because windows goes modal and sends paint
         messages directly to the windows procedure when doing solid drags
         and the message queue doesn't get processed. */
       mswindows_handle_paint (XFRAME (mswindows_find_frame (hwnd)));
@@ -2533,21 +2629,31 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
     case WM_DISPLAYCHANGE:
       {
        struct device *d;
+       DWORD message_tick = GetMessageTime ();
 
        fobj = mswindows_find_frame (hwnd);
        frame = XFRAME (fobj);
        d = XDEVICE (FRAME_DEVICE (frame));
 
-       DEVICE_MSWINDOWS_HORZRES(d) = LOWORD (lParam);
-       DEVICE_MSWINDOWS_VERTRES(d) = HIWORD (lParam);
-       DEVICE_MSWINDOWS_BITSPIXEL(d) = wParam;
-       break;
+       /* Do this only once per message. XEmacs can receive this message
+          through as many frames as it currently has open. Message time
+          will be the same for all these messages. Despite extreme
+          efficiency, the code below has about one in 4 billion
+          probability that the HDC is not recreated, provided that
+          XEmacs is running sufficiently longer than 52 days. */
+       if (DEVICE_MSWINDOWS_UPDATE_TICK(d) != message_tick)
+         {
+           DEVICE_MSWINDOWS_UPDATE_TICK(d) = message_tick;
+           DeleteDC (DEVICE_MSWINDOWS_HCDC(d));
+           DEVICE_MSWINDOWS_HCDC(d) = CreateCompatibleDC (NULL);
+         }
       }
+      break;
 
       /* Misc magic events which only require that the frame be identified */
     case WM_SETFOCUS:
     case WM_KILLFOCUS:
-      mswindows_enqueue_magic_event (hwnd, message);
+      mswindows_enqueue_magic_event (hwnd, message_);
       break;
 
     case WM_WINDOWPOSCHANGING:
@@ -2688,6 +2794,8 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          case CBN_SELCHANGE:
            if (!NILP (mswindows_handle_gui_wm_command (frame, cid, id)))
              return 0;
+         case BN_SETFOCUS:
+           
          }
        /* menubars always must come last since the hashtables do not
           always exist*/
@@ -2696,7 +2804,7 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          break;
 #endif
 
-       return DefWindowProc (hwnd, message, wParam, lParam);
+       return DefWindowProc (hwnd, message_, wParam, lParam);
        /* Bite me - a spurious command. This used to not be able to
           happen but with the introduction of widgets its now
           possible. */
@@ -2779,7 +2887,8 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
        event->channel = mswindows_find_frame(hwnd);
        event->timestamp = GetMessageTime();
        event->event.misc.button = 1;           /* #### Should try harder */
-       event->event.misc.modifiers = mswindows_modifier_state (NULL, 0);
+       event->event.misc.modifiers = mswindows_modifier_state (NULL,
+                                                               (DWORD) -1, 0);
        event->event.misc.x = point.x;
        event->event.misc.y = point.y;
        event->event.misc.function = Qdragdrop_drop_dispatch;
@@ -2796,7 +2905,7 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
            DragQueryFile ((HANDLE) wParam, i, fname, len+1);
 
            /* May be a shell link aka "shortcut" - replace fname if so */
-#if !(defined(__CYGWIN32__) || defined(__MINGW32__))
+#if !(defined(CYGWIN) || defined(MINGW))
            /* cygwin doesn't define this COM stuff */
            if (!stricmp (fname + strlen (fname) - 4, ".LNK"))
              {
@@ -2804,7 +2913,7 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 
                if (CoCreateInstance (&CLSID_ShellLink, NULL,
                                      CLSCTX_INPROC_SERVER, &IID_IShellLink, &psl) == S_OK)
-                 { 
+                 {
                    IPersistFile* ppf;
 
                    if (psl->lpVtbl->QueryInterface (psl, &IID_IPersistFile,
@@ -2833,7 +2942,7 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
              }
 #endif
 
-#ifdef __CYGWIN32__
+#ifdef CYGWIN
            filename = xmalloc (cygwin32_win32_to_posix_path_list_buf_size (fname) + 5);
            strcpy (filename, "file:");
            cygwin32_win32_to_posix_path_list (fname, filename+5);
@@ -2858,7 +2967,7 @@ mswindows_wnd_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 
     defproc:
     default:
-      return DefWindowProc (hwnd, message, wParam, lParam);
+      return DefWindowProc (hwnd, message_, wParam, lParam);
     }
   return (0);
 }
@@ -2905,8 +3014,10 @@ mswindows_current_layout_has_AltGr (void)
      time when a key typed at autorepeat rate of 30 cps! */
   static HKL last_hkl = 0;
   static int last_hkl_has_AltGr;
+  HKL current_hkl = (HKL) -1;
 
-  HKL current_hkl = GetKeyboardLayout (0);
+  if (xGetKeyboardLayout) /* not in NT 3.5 */
+    current_hkl = xGetKeyboardLayout (0);
   if (current_hkl != last_hkl)
     {
       TCHAR c;
@@ -2926,11 +3037,20 @@ mswindows_current_layout_has_AltGr (void)
 /* Returns the state of the modifier keys in the format expected by the
  * Lisp_Event key_data, button_data and motion_data modifiers member */
 static int
-mswindows_modifier_state (BYTE* keymap, int has_AltGr)
+mswindows_modifier_state (BYTE* keymap, DWORD fwKeys, int has_AltGr)
 {
   int mods = 0;
+  int keys_is_real = 0;
   BYTE keymap2[256];
 
+  if (fwKeys == (DWORD) -1)
+    fwKeys = mswindows_last_mouse_button_state;
+  else
+    {
+      keys_is_real = 1;
+      mswindows_last_mouse_button_state = fwKeys;
+    }
+
   if (keymap == NULL)
     {
       keymap = keymap2;
@@ -2938,6 +3058,8 @@ mswindows_modifier_state (BYTE* keymap, int has_AltGr)
       has_AltGr = mswindows_current_layout_has_AltGr ();
     }
 
+  /* #### should look at fwKeys for MK_CONTROL.  I don't understand how
+     AltGr works. */
   if (has_AltGr && (keymap [VK_LCONTROL] & 0x80) && (keymap [VK_RMENU] & 0x80))
     {
       mods |= (keymap [VK_LMENU] & 0x80) ? XEMACS_MOD_META : 0;
@@ -2949,7 +3071,11 @@ mswindows_modifier_state (BYTE* keymap, int has_AltGr)
       mods |= (keymap [VK_CONTROL] & 0x80) ? XEMACS_MOD_CONTROL : 0;
     }
 
-  mods |= (keymap [VK_SHIFT] & 0x80) ? XEMACS_MOD_SHIFT : 0;
+  mods |= (keys_is_real ? fwKeys & MK_SHIFT : (keymap [VK_SHIFT] & 0x80))
+    ? XEMACS_MOD_SHIFT : 0;
+  mods |= fwKeys & MK_LBUTTON ? XEMACS_MOD_BUTTON1 : 0;
+  mods |= fwKeys & MK_MBUTTON ? XEMACS_MOD_BUTTON2 : 0;
+  mods |= fwKeys & MK_RBUTTON ? XEMACS_MOD_BUTTON3 : 0;
 
   return mods;
 }
@@ -2958,7 +3084,6 @@ mswindows_modifier_state (BYTE* keymap, int has_AltGr)
  * Translate a mswindows virtual key to a keysym.
  * Only returns non-Qnil for keys that don't generate WM_CHAR messages
  * or whose ASCII codes (like space) xemacs doesn't like.
- * Virtual key values are defined in winresrc.h
  */
 Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods,
                                           int extendedp)
@@ -2967,6 +3092,7 @@ Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods,
     {
       switch (mswindows_key)
         {
+       case VK_CANCEL:         return KEYSYM ("pause");
        case VK_RETURN:         return KEYSYM ("kp-enter");
        case VK_PRIOR:          return KEYSYM ("prior");
        case VK_NEXT:           return KEYSYM ("next");
@@ -2978,6 +3104,11 @@ Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods,
        case VK_DOWN:           return KEYSYM ("down");
        case VK_INSERT:         return KEYSYM ("insert");
        case VK_DELETE:         return QKdelete;
+#if 0  /* FSF Emacs allows these to return configurable syms/mods */
+       case VK_LWIN            return KEYSYM ("");
+       case VK_RWIN            return KEYSYM ("");
+#endif
+       case VK_APPS:           return KEYSYM ("menu");
        }
     }
   else
@@ -2989,6 +3120,7 @@ Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods,
        case '\n':              return QKlinefeed;
        case VK_CLEAR:          return KEYSYM ("clear");
        case VK_RETURN:         return QKreturn;
+       case VK_PAUSE:          return KEYSYM ("pause");
        case VK_ESCAPE:         return QKescape;
        case VK_SPACE:          return QKspace;
        case VK_PRIOR:          return KEYSYM ("kp-prior");
@@ -3006,11 +3138,6 @@ Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods,
        case VK_INSERT:         return KEYSYM ("kp-insert");
        case VK_DELETE:         return KEYSYM ("kp-delete");
        case VK_HELP:           return KEYSYM ("help");
-#if 0  /* FSF Emacs allows these to return configurable syms/mods */
-         case VK_LWIN          return KEYSYM ("");
-         case VK_RWIN          return KEYSYM ("");
-#endif
-       case VK_APPS:           return KEYSYM ("menu");
        case VK_NUMPAD0:        return KEYSYM ("kp-0");
        case VK_NUMPAD1:        return KEYSYM ("kp-1");
        case VK_NUMPAD2:        return KEYSYM ("kp-2");
@@ -3323,7 +3450,7 @@ emacs_mswindows_quit_p (void)
        {
          emacs_event = mswindows_cancel_dispatch_event (&match_against);
          assert (!NILP (emacs_event));
-         
+
          if (XEVENT(emacs_event)->event.key.modifiers & XEMACS_MOD_SHIFT)
            critical_p = 1;
 
@@ -3437,6 +3564,12 @@ emacs_mswindows_delete_stream_pair (Lisp_Object instream,
          : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM (instream))));
 }
 
+static int
+emacs_mswindows_current_event_timestamp (struct console *c)
+{
+  return GetTickCount ();
+}
+
 #ifndef HAVE_X_WINDOWS
 /* This is called from GC when a process object is about to be freed.
    If we've still got pointers to it in this file, we're gonna lose hard.
@@ -3491,6 +3624,8 @@ reinit_vars_of_event_mswindows (void)
   mswindows_event_stream->create_stream_pair_cb = emacs_mswindows_create_stream_pair;
   mswindows_event_stream->delete_stream_pair_cb = emacs_mswindows_delete_stream_pair;
 #endif
+  mswindows_event_stream->current_event_timestamp_cb =
+    emacs_mswindows_current_event_timestamp;
 }
 
 void
@@ -3513,7 +3648,7 @@ vars_of_event_mswindows (void)
 
 
 #ifdef DEBUG_XEMACS
-  DEFVAR_INT ("mswindows-debug-events", &mswindows_debug_events /*
+  DEFVAR_INT ("debug-mswindows-events", &debug_mswindows_events /*
 If non-zero, display debug information about Windows events that XEmacs sees.
 Information is displayed in a console window.  Currently defined values are:
 
@@ -3522,7 +3657,7 @@ Information is displayed in a console window.  Currently defined values are:
 
 #### Unfortunately, not yet implemented.
 */ );
-  mswindows_debug_events = 0;
+  debug_mswindows_events = 0;
 #endif
 
   DEFVAR_BOOL ("mswindows-alt-by-itself-activates-menu",