(last_event_timestamp): Declare, but don't define.
[bpt/emacs.git] / src / w32fns.c
index f34c215..4753caf 100644 (file)
@@ -1,5 +1,5 @@
 /* Functions for the Win32 window system.
-   Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation.
+   Copyright (C) 1989, 92, 93, 94, 95, 1996 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -49,6 +49,10 @@ Lisp_Object Vwin32_color_map;
 /* Non nil if alt key presses are passed on to Windows.  */
 Lisp_Object Vwin32_pass_alt_to_system;
 
+/* Non nil if alt key is translated to meta_modifier, nil if it is translated
+   to alt_modifier.  */
+Lisp_Object Vwin32_alt_is_meta;
+
 /* Non nil if left window, right window, and application key events
    are passed on to Windows.  */
 Lisp_Object Vwin32_pass_optional_keys_to_system;
@@ -64,6 +68,10 @@ Lisp_Object Vwin32_enable_palette;
    be converted to a middle button down event. */
 Lisp_Object Vwin32_mouse_button_tolerance;
 
+/* Minimum interval between mouse movement (and scroll bar drag)
+   events that are passed on to the event loop. */
+Lisp_Object Vwin32_mouse_move_interval;
+
 /* The name we're using in resource queries.  */
 Lisp_Object Vx_resource_name;
 
@@ -126,7 +134,6 @@ Lisp_Object Qborder_width;
 Lisp_Object Qbox;
 Lisp_Object Qcursor_color;
 Lisp_Object Qcursor_type;
-Lisp_Object Qfont;
 Lisp_Object Qforeground_color;
 Lisp_Object Qgeometry;
 Lisp_Object Qicon_left;
@@ -157,8 +164,13 @@ Lisp_Object Qdisplay;
 #define RMOUSE 4
 
 static int button_state = 0;
-static Win32Msg saved_mouse_msg;
-static unsigned timer_id;      /* non-zero when timer is active */
+static Win32Msg saved_mouse_button_msg;
+static unsigned mouse_button_timer;    /* non-zero when timer is active */
+static Win32Msg saved_mouse_move_msg;
+static unsigned mouse_move_timer;
+
+#define MOUSE_BUTTON_ID        1
+#define MOUSE_MOVE_ID  2
 
 /* The below are defined in frame.c.  */
 extern Lisp_Object Qheight, Qminibuffer, Qname, Qonly, Qwidth;
@@ -2641,7 +2653,8 @@ win32_get_modifiers ()
 {
   return (((GetKeyState (VK_SHIFT)&0x8000)   ? shift_modifier  : 0) |
          ((GetKeyState (VK_CONTROL)&0x8000) ? ctrl_modifier   : 0) |
-         ((GetKeyState (VK_MENU)&0x8000)    ? meta_modifier   : 0));
+          ((GetKeyState (VK_MENU)&0x8000)    ? 
+          ((NILP (Vwin32_alt_is_meta)) ? alt_modifier : meta_modifier) : 0));
 }
 
 void 
@@ -2746,14 +2759,52 @@ record_keyup (unsigned int wparam, unsigned int lparam)
 static void
 reset_modifiers ()
 {
+  SHORT ctrl, alt;
+
   if (!modifiers_recorded)
     return;
-  bzero (modifiers, sizeof (modifiers));
+
+  ctrl = GetAsyncKeyState (VK_CONTROL);
+  alt = GetAsyncKeyState (VK_MENU);
+
+  if (ctrl == 0 || alt == 0)
+    /* Emacs doesn't have keyboard focus.  Do nothing.  */
+    return;
+
+  if (!(ctrl & 0x08000))
+    /* Clear any recorded control modifier state.  */
+    modifiers[EMACS_RCONTROL] = modifiers[EMACS_LCONTROL] = 0;
+
+  if (!(alt & 0x08000))
+    /* Clear any recorded alt modifier state.  */
+    modifiers[EMACS_RMENU] = modifiers[EMACS_LMENU] = 0;
+
+  /* Otherwise, leave the modifier state as it was when Emacs lost
+     keyboard focus.  */
+}
+
+/* Synchronize modifier state with what is reported with the current
+   keystroke.  Even if we cannot distinguish between left and right
+   modifier keys, we know that, if no modifiers are set, then neither
+   the left or right modifier should be set.  */
+static void
+sync_modifiers ()
+{
+  if (!modifiers_recorded)
+    return;
+
+  if (!(GetKeyState (VK_CONTROL) & 0x8000)) 
+    modifiers[EMACS_RCONTROL] = modifiers[EMACS_LCONTROL] = 0;
+
+  if (!(GetKeyState (VK_MENU) & 0x8000)) 
+    modifiers[EMACS_RMENU] = modifiers[EMACS_LMENU] = 0;
 }
 
 static int
 modifier_set (int vkey)
 {
+  if (vkey == VK_CAPITAL)
+    return (GetKeyState (vkey) & 0x1);
   if (!modifiers_recorded)
     return (GetKeyState (vkey) & 0x8000);
 
@@ -2767,8 +2818,6 @@ modifier_set (int vkey)
       return modifiers[EMACS_LMENU];
     case VK_RMENU:
       return modifiers[EMACS_RMENU];
-    case VK_CAPITAL:
-      return (GetKeyState (vkey) & 0x1);
     default:
       break;
     }
@@ -2841,14 +2890,6 @@ win_msg_worker (dw)
        {
          switch (msg.message)
            {
-           case WM_TIMER:
-             if (saved_mouse_msg.msg.hwnd)
-               {
-                 post_msg (&saved_mouse_msg);
-                 saved_mouse_msg.msg.hwnd = 0;
-               }
-             timer_id = 0;
-             break;
            case WM_EMACS_CREATEWINDOW:
              win32_createwindow ((struct frame *) msg.wParam);
              PostThreadMessage (dwMainThreadId, WM_EMACS_DONE, 0, 0);
@@ -2888,21 +2929,43 @@ win32_wnd_proc (hwnd, msg, wParam, lParam)
   LRESULT ret = 1;
   struct win32_display_info *dpyinfo = &one_win32_display_info;
   Win32Msg wmsg;
-  
+  int windows_translate;
+
+  /* Note that it is okay to call x_window_to_frame, even though we are
+     not running in the main lisp thread, because frame deletion
+     requires the lisp thread to synchronize with this thread.  Thus, if
+     a frame struct is returned, it can be used without concern that the
+     lisp thread might make it disappear while we are using it.
+
+     NB. Walking the frame list in this thread is safe (as long as
+     writes of Lisp_Object slots are atomic, which they are on Windows).
+     Although delete-frame can destructively modify the frame list while
+     we are walking it, a garbage collection cannot occur until after
+     delete-frame has synchronized with this thread.
+
+     It is also safe to use functions that make GDI calls, such as
+     win32_clear_rect, because these functions must obtain a DC handle
+     from the frame struct using get_frame_dc which is thread-aware.  */
+
   switch (msg) 
     {
     case WM_ERASEBKGND:
-      enter_crit ();
-      GetUpdateRect (hwnd, &wmsg.rect, FALSE);
-      leave_crit ();
-      my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
+      f = x_window_to_frame (dpyinfo, hwnd);
+      if (f)
+       {
+         GetUpdateRect (hwnd, &wmsg.rect, FALSE);
+         win32_clear_rect (f, NULL, &wmsg.rect);
+       }
       return 1;
     case WM_PALETTECHANGED:
       /* ignore our own changes */
       if ((HWND)wParam != hwnd)
         {
-         /* simply notify main thread it may need to update frames */
-         my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
+         f = x_window_to_frame (dpyinfo, hwnd);
+         if (f)
+           /* get_frame_dc will realize our palette and force all
+              frames to be redrawn if needed. */
+           release_frame_dc (f, get_frame_dc (f));
        }
       return 0;
     case WM_PAINT:
@@ -2927,28 +2990,34 @@ win32_wnd_proc (hwnd, msg, wParam, lParam)
 
     case WM_KEYDOWN:
     case WM_SYSKEYDOWN:
+      /* Synchronize modifiers with current keystroke.  */
+      sync_modifiers ();
+
       record_keydown (wParam, lParam);
 
       wParam = map_keypad_keys (wParam, lParam);
-      
+
+      windows_translate = 0;
       switch (wParam) {
       case VK_LWIN:
       case VK_RWIN:
       case VK_APPS:
        /* More support for these keys will likely be necessary.  */
        if (!NILP (Vwin32_pass_optional_keys_to_system))
-         goto dflt;
+         windows_translate = 1;
        break;
       case VK_MENU:
        if (NILP (Vwin32_pass_alt_to_system)) 
          return 0;
-       else 
-         goto dflt;
+       windows_translate = 1;
+       break;
       case VK_CONTROL: 
       case VK_CAPITAL: 
       case VK_SHIFT:
-       /* Pass on to Windows.  */
-       goto dflt;
+      case VK_NUMLOCK:
+      case VK_SCROLL: 
+       windows_translate = 1;
+       break;
       default:
        /* If not defined as a function key, change it to a WM_CHAR message. */
        if (lispy_function_keys[wParam] == 0)
@@ -2956,6 +3025,15 @@ win32_wnd_proc (hwnd, msg, wParam, lParam)
        break;
       }
 
+      if (windows_translate)
+       {
+         MSG winmsg = { hwnd, msg, wParam, lParam, 0, {0,0} };
+
+         winmsg.time = GetMessageTime ();
+         TranslateMessage (&winmsg);
+         goto dflt;
+       }
+
       /* Fall through */
       
     case WM_SYSCHAR:
@@ -2965,6 +3043,7 @@ win32_wnd_proc (hwnd, msg, wParam, lParam)
       enter_crit ();
       my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
 
+#if 1
       /* Detect quit_char and set quit-flag directly.  Note that we dow
          this *after* posting the message to ensure the main thread will
          be woken up if blocked in sys_select(). */
@@ -2976,6 +3055,8 @@ win32_wnd_proc (hwnd, msg, wParam, lParam)
        if (c == quit_char)
          Vquit_flag = Qt;
       }
+#endif
+
       leave_crit ();
       break;
 
@@ -3000,10 +3081,10 @@ win32_wnd_proc (hwnd, msg, wParam, lParam)
 
        if (button_state & other)
          {
-           if (timer_id)
+           if (mouse_button_timer)
              {
-               KillTimer (NULL, timer_id);
-               timer_id = 0;
+               KillTimer (hwnd, mouse_button_timer);
+               mouse_button_timer = 0;
 
                /* Generate middle mouse event instead. */
                msg = WM_MBUTTONDOWN;
@@ -3020,25 +3101,25 @@ win32_wnd_proc (hwnd, msg, wParam, lParam)
            else
              {
                /* Flush out saved message. */
-               post_msg (&saved_mouse_msg);
+               post_msg (&saved_mouse_button_msg);
              }
            wmsg.dwModifiers = win32_get_modifiers ();
            my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
 
            /* Clear message buffer. */
-           saved_mouse_msg.msg.hwnd = 0;
+           saved_mouse_button_msg.msg.hwnd = 0;
          }
        else
          {
            /* Hold onto message for now. */
-           timer_id =
-             SetTimer (NULL, 0, XINT (Vwin32_mouse_button_tolerance), NULL);
-           saved_mouse_msg.msg.hwnd = hwnd;
-           saved_mouse_msg.msg.message = msg;
-           saved_mouse_msg.msg.wParam = wParam;
-           saved_mouse_msg.msg.lParam = lParam;
-           saved_mouse_msg.msg.time = GetMessageTime ();
-           saved_mouse_msg.dwModifiers = win32_get_modifiers ();
+           mouse_button_timer =
+             SetTimer (hwnd, MOUSE_BUTTON_ID, XINT (Vwin32_mouse_button_tolerance), NULL);
+           saved_mouse_button_msg.msg.hwnd = hwnd;
+           saved_mouse_button_msg.msg.message = msg;
+           saved_mouse_button_msg.msg.wParam = wParam;
+           saved_mouse_button_msg.msg.lParam = lParam;
+           saved_mouse_button_msg.msg.time = GetMessageTime ();
+           saved_mouse_button_msg.dwModifiers = win32_get_modifiers ();
          }
       }
       return 0;
@@ -3073,18 +3154,18 @@ win32_wnd_proc (hwnd, msg, wParam, lParam)
        else
          {
            /* Flush out saved message if necessary. */
-           if (saved_mouse_msg.msg.hwnd)
+           if (saved_mouse_button_msg.msg.hwnd)
              {
-               post_msg (&saved_mouse_msg);
+               post_msg (&saved_mouse_button_msg);
              }
          }
        wmsg.dwModifiers = win32_get_modifiers ();
        my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
 
        /* Always clear message buffer and cancel timer. */
-       saved_mouse_msg.msg.hwnd = 0;
-       KillTimer (NULL, timer_id);
-       timer_id = 0;
+       saved_mouse_button_msg.msg.hwnd = 0;
+       KillTimer (hwnd, mouse_button_timer);
+       mouse_button_timer = 0;
 
        if (button_state == 0)
          ReleaseCapture ();
@@ -3108,33 +3189,73 @@ win32_wnd_proc (hwnd, msg, wParam, lParam)
       my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
       return 0;
 
-#if 0
+    case WM_VSCROLL:
     case WM_MOUSEMOVE:
-      /* Flush out saved message if necessary. */
-      if (saved_mouse_msg.msg.hwnd)
+      if (XINT (Vwin32_mouse_move_interval) <= 0
+         || (msg == WM_MOUSEMOVE && button_state == 0))
+       {
+         wmsg.dwModifiers = win32_get_modifiers ();
+         my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
+         return 0;
+       }
+  
+      /* Hang onto mouse move and scroll messages for a bit, to avoid
+        sending such events to Emacs faster than it can process them.
+        If we get more events before the timer from the first message
+        expires, we just replace the first message. */
+
+      if (saved_mouse_move_msg.msg.hwnd == 0)
+       mouse_move_timer =
+         SetTimer (hwnd, MOUSE_MOVE_ID, XINT (Vwin32_mouse_move_interval), NULL);
+
+      /* Hold onto message for now. */
+      saved_mouse_move_msg.msg.hwnd = hwnd;
+      saved_mouse_move_msg.msg.message = msg;
+      saved_mouse_move_msg.msg.wParam = wParam;
+      saved_mouse_move_msg.msg.lParam = lParam;
+      saved_mouse_move_msg.msg.time = GetMessageTime ();
+      saved_mouse_move_msg.dwModifiers = win32_get_modifiers ();
+  
+      return 0;
+
+    case WM_TIMER:
+      /* Flush out saved messages if necessary. */
+      if (wParam == mouse_button_timer)
        {
-         wmsg = saved_mouse_msg;
-         my_post_msg (&wmsg, wmsg.msg.hwnd, wmsg.msg.message,
-                      wmsg.msg.wParam, wmsg.msg.lParam);
+         if (saved_mouse_button_msg.msg.hwnd)
+           {
+             post_msg (&saved_mouse_button_msg);
+             saved_mouse_button_msg.msg.hwnd = 0;
+           }
+         KillTimer (hwnd, mouse_button_timer);
+         mouse_button_timer = 0;
+       }
+      else if (wParam == mouse_move_timer)
+       {
+         if (saved_mouse_move_msg.msg.hwnd)
+           {
+             post_msg (&saved_mouse_move_msg);
+             saved_mouse_move_msg.msg.hwnd = 0;
+           }
+         KillTimer (hwnd, mouse_move_timer);
+         mouse_move_timer = 0;
        }
-      wmsg.dwModifiers = win32_get_modifiers ();
-      my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
-
-      /* Always clear message buffer and cancel timer. */
-      saved_mouse_msg.msg.hwnd = 0;
-      KillTimer (NULL, timer_id);
-      timer_id = 0;
-
       return 0;
-#endif
+  
+    case WM_NCACTIVATE:
+      /* Windows doesn't send us focus messages when putting up and
+        taking down a system popup dialog as for Ctrl-Alt-Del on Win95.
+        The only indication we get that something happened is receiving
+        this message afterwards.  So this is a good time to reset our
+        keyboard modifiers' state. */
+      reset_modifiers ();
+      goto dflt;
 
     case WM_SETFOCUS:
       reset_modifiers ();
-    case WM_MOUSEMOVE:
+    case WM_KILLFOCUS:
     case WM_MOVE:
     case WM_SIZE:
-    case WM_KILLFOCUS:
-    case WM_VSCROLL:
     case WM_SYSCOMMAND:
     case WM_COMMAND:
       wmsg.dwModifiers = win32_get_modifiers ();
@@ -3892,7 +4013,7 @@ x_to_win32_font (lpxstr, lplogfont)
   
   memset (lplogfont, 0, sizeof (*lplogfont));
 
-#if 0
+#if 1
   lplogfont->lfOutPrecision = OUT_DEFAULT_PRECIS;
   lplogfont->lfClipPrecision = CLIP_DEFAULT_PRECIS;
   lplogfont->lfQuality = DEFAULT_QUALITY;
@@ -4836,8 +4957,6 @@ syms_of_win32fns ()
   staticpro (&Qcursor_color);
   Qcursor_type = intern ("cursor-type");
   staticpro (&Qcursor_type);
-  Qfont = intern ("font");
-  staticpro (&Qfont);
   Qforeground_color = intern ("foreground-color");
   staticpro (&Qforeground_color);
   Qgeometry = intern ("geometry");
@@ -4901,6 +5020,11 @@ When non-nil, for example, alt pressed and released and then space will\n\
 open the System menu.  When nil, Emacs silently swallows alt key events.");
   Vwin32_pass_alt_to_system = Qnil;
 
+  DEFVAR_LISP ("win32-alt-is-meta", &Vwin32_alt_is_meta,
+              "Non-nil if the alt key is to be considered the same as the meta key.\n\
+When nil, Emacs will translate the alt key to the Alt modifier, and not Meta.");
+  Vwin32_alt_is_meta = Qt;
+
   DEFVAR_LISP ("win32-pass-optional-keys-to-system", 
               &Vwin32_pass_optional_keys_to_system,
               "Non-nil if the 'optional' keys (left window, right window,\n\
@@ -4924,6 +5048,14 @@ If both mouse buttons are depressed within this interval, a middle mouse\n\
 button down event is generated instead.");
   XSETINT (Vwin32_mouse_button_tolerance, GetDoubleClickTime () / 2);
 
+  DEFVAR_INT ("win32-mouse-move-interval",
+             &Vwin32_mouse_move_interval,
+             "Minimum interval between mouse move events.\n\
+The value is the minimum time in milliseconds that must elapse between\n\
+successive mouse move (or scroll bar drag) events before they are\n\
+reported as lisp events.");
+  XSETINT (Vwin32_mouse_move_interval, 50);
+
   init_x_parm_symbols ();
 
   DEFVAR_LISP ("x-bitmap-file-path", &Vx_bitmap_file_path,