diff src/event-msw.c @ 223:2c611d1463a6 r20-4b10

Import from CVS: tag r20-4b10
author cvs
date Mon, 13 Aug 2007 10:10:54 +0200
parents 262b8bb4a523
children 12579d965149
line wrap: on
line diff
--- a/src/event-msw.c	Mon Aug 13 10:10:03 2007 +0200
+++ b/src/event-msw.c	Mon Aug 13 10:10:54 2007 +0200
@@ -46,8 +46,15 @@
 #include "event-msw.h"
 
 static struct event_stream *mswindows_event_stream;
-static Lisp_Object mswindows_dispatch_event_queue, mswindows_dispatch_event_queue_tail;
-CRITICAL_SECTION mswindows_dispatch_crit;
+
+/*
+ * Two separate queues, for efficiency, one (_u_) for user events, and
+ * another (_s_) for non-user ones. We always return events out of the
+ * first one until it is empty and only then proceed with the second
+ * one.
+ */
+static Lisp_Object mswindows_u_dispatch_event_queue, mswindows_u_dispatch_event_queue_tail;
+static Lisp_Object mswindows_s_dispatch_event_queue, mswindows_s_dispatch_event_queue_tail;
 
 /*
  * List of mswindows waitable handles.
@@ -60,74 +67,132 @@
 /* random emacs info associated with each of the wait handles */
 static mswindows_waitable_info_type mswindows_waitable_info[MAX_WAITABLE];
 
+/* Count of quit chars currently in the queue */
+/* Incremented in WM_CHAR handler in msw-proc.c
+   Decremented in mswindows_dequeue_dispatch_event() */
+int mswindows_quit_chars_count = 0;
+
+/* These are Lisp integers; see DEFVARS in this file for description. */
+int mswindows_dynamic_frame_resize;
+int mswindows_num_mouse_buttons;
+int mswindows_button2_max_skew_x;
+int mswindows_button2_max_skew_y;
+int mswindows_button2_chord_time;
+
 /* Number of wait handles */
 static mswindows_waitable_count=0;
 
+static int
+mswindows_user_event_p (struct Lisp_Event* sevt)
+{
+  return (sevt->event_type == key_press_event
+	  || sevt->event_type == button_press_event
+	  || sevt->event_type == button_release_event
+	  || sevt->event_type == pointer_motion_event);
+}
+
 /*
- * Add an emacs event to the dispatch queue and increment the semaphore
+ * Add an emacs event to the proper dispatch queue
  */
 void
 mswindows_enqueue_dispatch_event (Lisp_Object event)
 {
-  enqueue_event (event, &mswindows_dispatch_event_queue,
-		 &mswindows_dispatch_event_queue_tail);
-  ReleaseSemaphore(mswindows_waitable[0], 1, NULL);
+  int user_p = mswindows_user_event_p (XEVENT(event));
+  enqueue_event (event,
+		 user_p ? &mswindows_u_dispatch_event_queue : 
+		 	&mswindows_s_dispatch_event_queue,
+		 user_p ? &mswindows_u_dispatch_event_queue_tail :
+		 	&mswindows_s_dispatch_event_queue_tail);
+
+  /* This one does not go to window procedure, hence does not
+     generate XM_BUMPQUEUE magic event! */
+  PostMessage (NULL, XM_BUMPQUEUE, 0, 0);
 }
 
 /*
- * Remove and return the first emacs event on the dispatch queue. Don't
- * decrement the queue's semaphore because it will be decremented by being
- * waited on.
+ * Remove and return the first emacs event on the dispatch queue.
+ * Give a preference to user events over non-user ones.
  */
 static Lisp_Object
-mswindows_dequeue_dispatch_event (void)
+mswindows_dequeue_dispatch_event ()
 {
   Lisp_Object event;
-  event = dequeue_event (&mswindows_dispatch_event_queue,
-			 &mswindows_dispatch_event_queue_tail);
+  struct Lisp_Event* sevt;
+
+  assert (!NILP(mswindows_u_dispatch_event_queue) ||
+	  !NILP(mswindows_s_dispatch_event_queue));
+
+  event = dequeue_event (
+		 NILP(mswindows_u_dispatch_event_queue) ? 
+			 &mswindows_s_dispatch_event_queue : 
+			 &mswindows_u_dispatch_event_queue,
+		 NILP(mswindows_u_dispatch_event_queue) ? 
+			 &mswindows_s_dispatch_event_queue_tail :
+			 &mswindows_u_dispatch_event_queue_tail);
+
+  sevt = XEVENT(event);
+  if (sevt->event_type == key_press_event
+      && (sevt->event.key.modifiers & FAKE_MOD_QUIT))
+    {
+      sevt->event.key.modifiers &= ~FAKE_MOD_QUIT;
+      --mswindows_quit_chars_count;
+    }
+
   return event;
 }
 
 /*
  * Remove and return the first emacs event on the dispatch queue that matches
- * the supplied event and decrement the queue's semaphore.
- * Only supports timeout events.
+ * the supplied event
+ * Timeout event matches if interval_id equals to that of the given event.
+ * Keypress event matches if logical AND between modifiers bitmask of the
+ * event in the queue and that of the given event is non-zero
+ * For all other event types, this function asserts.
  */
+
 Lisp_Object
-mswindows_cancel_dispatch_event (Lisp_Object match_event)
+mswindows_cancel_dispatch_event (struct Lisp_Event* match)
 {
   Lisp_Object event;
   Lisp_Object previous_event=Qnil;
-  struct Lisp_Event *match = XEVENT(match_event);
+  int user_p = mswindows_user_event_p (match);
+  Lisp_Object* head = user_p ? &mswindows_u_dispatch_event_queue : 
+    			       &mswindows_s_dispatch_event_queue;
+  Lisp_Object* tail = user_p ? &mswindows_u_dispatch_event_queue_tail : 
+    			       &mswindows_s_dispatch_event_queue_tail;
 
-  assert (match->event_type == timeout_event);
+  assert (match->event_type == timeout_event
+	  || match->event_type == key_press_event);
 
-  EVENT_CHAIN_LOOP (event, mswindows_dispatch_event_queue)
-    if (XEVENT_TYPE (event) == match->event_type)
-      {
-	/* We only handle timeouts */
-	if (XEVENT(event)->event.timeout.interval_id ==
-	    match->event.timeout.interval_id)
-	  {
-	    if (NILP (previous_event))
-	      dequeue_event (&mswindows_dispatch_event_queue,
-			     &mswindows_dispatch_event_queue_tail);
-	    else
-	      {
-		XSET_EVENT_NEXT (previous_event, XEVENT_NEXT (event));
-		if (EQ (mswindows_dispatch_event_queue_tail, event))
-		  mswindows_dispatch_event_queue_tail = previous_event;
-	      }
- 
-	    /* Decrement the dispatch queue counter */
-	    WaitForSingleObject(mswindows_waitable[0], INFINITE);
-	    return event;
-	  }
-      }
-    else
+  EVENT_CHAIN_LOOP (event, *head)
+    {
+      int found = 1;
+      if (XEVENT_TYPE (event) != match->event_type)
+	found = 0;
+      if (found && match->event_type == timeout_event
+	  && (XEVENT(event)->event.timeout.interval_id !=
+	      match->event.timeout.interval_id))
+	found = 0;
+      if (found && match->event_type == key_press_event
+	  && ((XEVENT(event)->event.key.modifiers &
+	      match->event.key.modifiers) == 0))
+	found = 0;
+
+      if (found)
+	{
+	  if (NILP (previous_event))
+	    dequeue_event (head, tail);
+	  else
+	    {
+	      XSET_EVENT_NEXT (previous_event, XEVENT_NEXT (event));
+	      if (EQ (*tail, event))
+		*tail = previous_event;
+	    }
+	  
+	  return event;
+	}
       previous_event = event;
-
-  return Qnil;
+    }
 }
 
 /*
@@ -156,26 +221,14 @@
   switch (info->type)
   {
   case mswindows_waitable_type_dispatch:
+    assert (0); /* kkm - should not get here */
     /* Can only have one waitable for the dispatch queue, and it's the first one */
     assert (mswindows_waitable_count++ == 0);
     waitable=0;
-    InitializeCriticalSection(&mswindows_dispatch_crit);
+//    InitializeCriticalSection(&mswindows_dispatch_crit);
     assert (mswindows_waitable[0] = CreateSemaphore (NULL, 0, 0x7fffffff, NULL));
     return mswindows_waitable_info+0;
 
-#if 0	/* Windows95 doesn't support WaitableTimers */
-  case mswindows_waitable_type_timeout:
-    {
-      LARGE_INTEGER due;
-      due.QuadPart = 10000 * (LONGLONG) info->data.timeout.milliseconds;
-      waitable = mswindows_find_free_waitable();
-      mswindows_waitable[waitable] = CreateWaitableTimer(NULL, TRUE, NULL);
-      SetWaitableTimer(mswindows_waitable[waitable], &due, 0, NULL, NULL, FALSE);
-      mswindows_waitable_info[waitable].data.timeout.id = waitable;
-    }
-    break;
-#endif
-
   default:
     assert(0);
   }
@@ -193,12 +246,6 @@
 
   switch (info->type)
   {
-#if 0
-  case mswindows_waitable_type_timeout:
-    waitable = info->data.timeout.id;
-    CancelWaitableTimeout(mswindows_waitable[waitable]);
-    break;
-#endif
 
   default:
     assert(0);
@@ -211,6 +258,106 @@
     --mswindows_waitable_count;
 }
 
+/* 
+ * Callback procedure for synchronous timer messages
+ */
+static void CALLBACK
+mswindows_wm_timer_callback (HWND hwnd, UINT umsg, UINT id_timer, DWORD dwtime)
+{
+  Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
+  struct Lisp_Event *event = XEVENT (emacs_event);
+
+  KillTimer (NULL, id_timer);
+
+  event->channel = Qnil;
+  event->timestamp = dwtime;
+  event->event_type = timeout_event;
+  event->event.timeout.interval_id = id_timer;
+
+  mswindows_enqueue_dispatch_event (emacs_event);
+}
+
+static void 
+mswindows_drain_windows_queue ()
+{
+  MSG msg;
+  while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
+    DispatchMessage (&msg);
+}
+
+/*
+ * This drains the event queue and fills up two internal queues until
+ * an event of a type specified by USER_P is retrieved.
+ *
+ * If user_p, then the function drains until the first user event, or
+ * the first non-user event if there no user events. Otherwise, If
+ * not user_p, it does not give preference to user events.
+ *
+ * If badly_p, then the function does not return until an event is
+ * available.
+ *
+ * The code does not rely on MsgWaitForMultipleObjects preference for
+ * messages over waitable handles.
+ *
+ * Used by emacs_mswindows_event_pending_p and emacs_mswindows_next_event
+ */
+static void
+mswindows_need_event (int user_p, int badly_p)
+{
+  int active;
+
+  /* Have to drain Windows message queue first, otherwise, we may miss
+     quit char when called from quit_p */
+  mswindows_drain_windows_queue ();
+
+  while (NILP (mswindows_u_dispatch_event_queue) &&
+	 (user_p || NILP (mswindows_s_dispatch_event_queue)))
+  {
+    /* If we already have an event, we've got someting to return - no wait! */
+    if (!NILP (mswindows_u_dispatch_event_queue)
+	|| !NILP (mswindows_s_dispatch_event_queue))
+      badly_p = 0;
+    
+    /* Now try getting a message */
+    active = MsgWaitForMultipleObjects (mswindows_waitable_count,
+					mswindows_waitable,
+					FALSE, badly_p ? INFINITE : 0,
+					QS_ALLINPUT);
+
+    /* This will assert if handle being waited for becomes abandoned.
+       Not the case currently tho */
+    assert ((!badly_p && active == WAIT_TIMEOUT) ||
+	    (active >= WAIT_OBJECT_0 &&
+	     active <= WAIT_OBJECT_0 + mswindows_waitable_count));
+    
+    if (active == WAIT_TIMEOUT)
+      {
+	/* No luck trying - just return what we've already got */
+	return;
+      }
+    else if (active == WAIT_OBJECT_0 + mswindows_waitable_count)
+      {
+	/* Got your message, thanks */
+	mswindows_drain_windows_queue ();
+      }
+    else
+      {
+	/* XXX FIXME: We should do some kind of round-robin scheme to ensure fairness */
+	int waitable = active - WAIT_OBJECT_0;
+	mswindows_waitable_info_type *info  = mswindows_waitable_info + waitable;
+
+	switch (info->type)
+	  {
+	    /* XXX FIXME: Should enque subprocess event here so that it is not lost */
+	  default:
+	    assert(0);
+	  }
+      }
+  } /* while */
+
+  return;
+}
+
 
 /************************************************************************/
 /*                            methods                                   */
@@ -219,27 +366,32 @@
 static int
 emacs_mswindows_add_timeout (EMACS_TIME thyme)
 {
+  int milliseconds;
   EMACS_TIME current_time;
-  int milliseconds;
-  int id;
-  mswindows_request_type request;
-
   EMACS_GET_TIME (current_time);
   EMACS_SUB_TIME (thyme, thyme, current_time);
-  milliseconds = EMACS_SECS (thyme) * 1000 + EMACS_USECS (thyme) / 1000;
+  milliseconds = EMACS_SECS (thyme) * 1000 +
+    (EMACS_USECS (thyme) + 500) / 1000;
   if (milliseconds < 1)
     milliseconds = 1;
-  request.thing1 = (void *) milliseconds;
-  id = mswindows_make_request(WM_XEMACS_SETTIMER, 0, &request);
-  assert(id);	/* XXX */
-  return id;
+  return SetTimer (NULL, 0, milliseconds, mswindows_wm_timer_callback);
 }
 
 static void
 emacs_mswindows_remove_timeout (int id)
 {
-  mswindows_request_type request = { (void *) id };
-  mswindows_make_request(WM_XEMACS_KILLTIMER, 0, &request);
+  struct Lisp_Event match_against;
+  Lisp_Object emacs_event;
+
+  KillTimer (NULL, id);
+
+  /* If there is a dispatch event generated by this
+     timeout in the queue, we have to remove it too. */
+  match_against.event_type = timeout_event;
+  match_against.event.timeout.interval_id = id;
+  emacs_event = mswindows_cancel_dispatch_event (&match_against);
+  if (!NILP (emacs_event))
+    Fdeallocate_event(emacs_event);
 }
 
 /* If `user_p' is false, then return whether there are any win32, timeout,
@@ -254,97 +406,36 @@
 static int
 emacs_mswindows_event_pending_p (int user_p)
 {
-  if (user_p)
-    {
-      /* Iterate over the dispatch queue looking for user-events */
-      int found = 0;
-      Lisp_Object event;
+  mswindows_need_event (user_p, 0);
 
-      EnterCriticalSection (&mswindows_dispatch_crit);
-      EVENT_CHAIN_LOOP (event, mswindows_dispatch_event_queue)
-	if (command_event_p (event))
-	  found = 1;
-      LeaveCriticalSection (&mswindows_dispatch_crit);
-      return found;
-    }
-  else
-    {
-      /* Check for any kind of input, including the dispatch queue */
-#if 0
-      /* Want do do the following, but it's not clear whether this would
-       * cause the waitables to become unsignalled */
-      return (WaitForMultipleObjects (mswindows_waitable_count,
-				      mswindows_waitable, FALSE, 0)
-	      != WAIT_TIMEOUT);
-#else
-      return !NILP (mswindows_dispatch_event_queue);
-#endif
-    }
-}
-
-static struct console *
-find_console_from_fd (int fd)
-{
-  return 0;
+  return (!NILP (mswindows_u_dispatch_event_queue)
+	  || (!user_p && !NILP (mswindows_s_dispatch_event_queue)));
 }
 
 /*
  * Return the next event
- * We return windows events off the dispatch event queue in preference to other events
  */
 static void
 emacs_mswindows_next_event (struct Lisp_Event *emacs_event)
 {
-  DWORD active;
-  active = WaitForMultipleObjects (mswindows_waitable_count, mswindows_waitable,
-				   FALSE, INFINITE);
-  assert(active >= WAIT_OBJECT_0 && active <= WAIT_OBJECT_0 + mswindows_waitable_count - 1);
-  
-  /* Windows events on the dispatch event queue */
-  if (active == WAIT_OBJECT_0)
-  {
-    /* XXX Copied from event-Xt.c */
-    Lisp_Object event, event2;
+  Lisp_Object event, event2;
+
+  /* Give strong preference to user events */
+  mswindows_need_event (1, 1);
 
-    EnterCriticalSection (&mswindows_dispatch_crit);
-    XSETEVENT (event2, emacs_event);
-    event = mswindows_dequeue_dispatch_event ();
-    Fcopy_event (event, event2);
-    Fdeallocate_event (event);
-    LeaveCriticalSection (&mswindows_dispatch_crit);
-  }
-  else
-  {
-    /* XXX FIXME: We should do some kind of round-robin scheme to ensure fairness */
-    int waitable = active - WAIT_OBJECT_0;
-    mswindows_waitable_info_type *info  = mswindows_waitable_info + waitable;
-
-    switch (info->type)
-    {
-    case mswindows_waitable_type_timeout:
-      emacs_event->channel = Qnil;
-      emacs_event->event_type = timeout_event;
-      emacs_event->event.timeout.interval_id = info->data.timeout.id;
-      mswindows_remove_waitable(info);
-      break;
-
-    default:
-      assert(0);
-    }
-  }
+  /* XXX Copied from event-Xt.c */
+  event = mswindows_dequeue_dispatch_event (!NILP(mswindows_u_dispatch_event_queue));
+  XSETEVENT (event2, emacs_event);
+  Fcopy_event (event, event2);
+  Fdeallocate_event (event);
 }
 
 /*
  * Handle a magic event off the dispatch queue.
- * XXX split into seperate functions for clarity.
  */
 static void
 emacs_mswindows_handle_magic_event (struct Lisp_Event *emacs_event)
 {
-  RECT *rect = &EVENT_MSWINDOWS_MAGIC_DATA(emacs_event);
-  struct frame *f = XFRAME (EVENT_CHANNEL (emacs_event));
-  Lisp_Object frame = Qnil;
-  XSETFRAME (frame, f);
 #if 0  
   stderr_out("magic %x, (%d,%d), (%d,%d)\n",
 	     EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event),
@@ -355,8 +446,11 @@
   case WM_SETFOCUS:
   case WM_KILLFOCUS:
     {
+      Lisp_Object frame = EVENT_CHANNEL (emacs_event);
+      struct frame *f = XFRAME (frame);
       int in_p = (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event) == WM_SETFOCUS);
       Lisp_Object conser;
+
       /* struct gcpro gcpro1; */
 
       /* Clear sticky modifiers here (if we had any) */
@@ -368,53 +462,31 @@
 	 I Don't know why */
       emacs_handle_focus_change_final (conser);
       /* UNGCPRO; */
+
     }
     break;
 
-    /* XXX What about Enter & Leave */
+  case XM_BUMPQUEUE:
+    /* This is a nice event, when we're in need to queue *something* */
+    break;
+
+  case XM_MAPFRAME:
+  case XM_UNMAPFRAME:
+    {
+      Lisp_Object frame = EVENT_CHANNEL (emacs_event);
+      va_run_hook_with_args (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event) 
+			      == XM_MAPFRAME ?
+			      Qmap_frame_hook : Qunmap_frame_hook, 
+			      1, frame);
+    }
+    break;
+			    
+      /* XXX What about Enter & Leave */
 #if 0
       va_run_hook_with_args (in_p ? Qmouse_enter_frame_hook :
 			     Qmouse_leave_frame_hook, 1, frame);
-    break;
 #endif
 
-  case WM_SIZE:
-    if ((rect->left & rect->top & rect->right & rect->bottom) == -1)
-      {
-	/* Iconified */
-        FRAME_VISIBLE_P (f) = 0;
-        va_run_hook_with_args (Qunmap_frame_hook, 1, frame);
-	Fframe_iconified_p (frame);
-      }
-    else
-      {
-	/* If we're uniconified, our size may or may not have changed */
-        int columns, rows;
-	int was_visible = FRAME_VISIBLE_P (f);
-
-	FRAME_VISIBLE_P (f) = 1;
-        FRAME_PIXWIDTH(f) = rect->right;
-	FRAME_PIXHEIGHT(f) = rect->bottom;
-
-        pixel_to_char_size (f, rect->right, rect->bottom, &columns, &rows);
-	change_frame_size (f, rows, columns, 0);
-/*	      MARK_FRAME_WINDOWS_STRUCTURE_CHANGED (f); /* XXX Too extreme? */
-
-	if (!was_visible)
-          va_run_hook_with_args (Qmap_frame_hook, 1, frame);
-
-      }
-      break;
-
-  case WM_PAINT:
-    mswindows_redraw_exposed_area(f, rect->left, rect->top,
-				  rect->right, rect->bottom);
-    break;
-
-  case WM_CLOSE:
-    enqueue_misc_user_event (frame, Qeval, list3 (Qdelete_frame, frame, Qt));
-    break;
-
   default:
     assert(0);
   }
@@ -443,6 +515,26 @@
 static void
 emacs_mswindows_quit_p (void)
 {
+  mswindows_need_event (1, 0);
+
+  if (mswindows_quit_chars_count > 0)
+    {
+      /* Yes there's a hidden one... Throw it away */
+      struct Lisp_Event match_against;
+      Lisp_Object emacs_event;
+
+      match_against.event_type = key_press_event;
+      match_against.event.key.modifiers = FAKE_MOD_QUIT;
+
+      emacs_event = mswindows_cancel_dispatch_event (&match_against);
+      assert (!NILP (emacs_event));
+
+      Vquit_flag = (XEVENT(emacs_event)->event.key.modifiers & MOD_SHIFT
+		    ? Qcritical : Qt);
+
+      Fdeallocate_event(emacs_event);
+      --mswindows_quit_chars_count;
+    }
 }
 
 /* This is called from GC when a process object is about to be freed.
@@ -474,22 +566,71 @@
 void
 vars_of_event_mswindows (void)
 {
-  mswindows_dispatch_event_queue = Qnil;
-  staticpro (&mswindows_dispatch_event_queue);
-  mswindows_dispatch_event_queue_tail = Qnil;
+  mswindows_u_dispatch_event_queue = Qnil;
+  staticpro (&mswindows_u_dispatch_event_queue);
+  mswindows_u_dispatch_event_queue_tail = Qnil;
+
+  mswindows_s_dispatch_event_queue = Qnil;
+  staticpro (&mswindows_s_dispatch_event_queue);
+  mswindows_s_dispatch_event_queue_tail = Qnil;
 
   mswindows_event_stream = xnew (struct event_stream);
 
   mswindows_event_stream->event_pending_p 	= emacs_mswindows_event_pending_p;
-  mswindows_event_stream->next_event_cb	= emacs_mswindows_next_event;
+  mswindows_event_stream->next_event_cb		= emacs_mswindows_next_event;
   mswindows_event_stream->handle_magic_event_cb = emacs_mswindows_handle_magic_event;
   mswindows_event_stream->add_timeout_cb 	= emacs_mswindows_add_timeout;
   mswindows_event_stream->remove_timeout_cb 	= emacs_mswindows_remove_timeout;
   mswindows_event_stream->select_console_cb 	= emacs_mswindows_select_console;
-  mswindows_event_stream->unselect_console_cb = emacs_mswindows_unselect_console;
+  mswindows_event_stream->unselect_console_cb	= emacs_mswindows_unselect_console;
   mswindows_event_stream->select_process_cb 	= emacs_mswindows_select_process;
-  mswindows_event_stream->unselect_process_cb = emacs_mswindows_unselect_process;
+  mswindows_event_stream->unselect_process_cb	= emacs_mswindows_unselect_process;
   mswindows_event_stream->quit_p_cb		= emacs_mswindows_quit_p;
+
+  DEFVAR_BOOL ("w32-dynamic-frame-resize", &mswindows_dynamic_frame_resize /*
+*Controls redrawing frame contents during mouse-drag or keyboard resize
+operation. When non-nil, the frame is redrawn while being resized. When
+nil, frame is not redrawn, and exposed areas are filled with default
+MDI application background color. Note that this option only has effect
+if "Show window contents while dragging" is on in system Display/Plus!
+settings.
+Default is t on fast machines, nil on slow.
+*/ );
+
+/* The description copied verbatim from nt-emacs. (C) Geoff Voelker */
+  DEFVAR_INT ("w32-mouse-button-tolerance", &mswindows_button2_chord_time /*
+*Analogue of double click interval for faking middle mouse events.
+The value is the minimum time in milliseconds that must elapse between
+left/right button down events before they are considered distinct events.
+If both mouse buttons are depressed within this interval, a middle mouse
+button down event is generated instead.
+If negative or zero, currently set system default is used instead.
+*/ );
+
+/* The description copied verbatim from nt-emacs. (C) Geoff Voelker */
+  DEFVAR_INT ("w32-num-mouse-buttons", &mswindows_num_mouse_buttons /*
+Number of physical mouse buttons.
+*/ );
+
+  DEFVAR_INT ("w32-mouse-button-max-skew-x", &mswindows_button2_max_skew_x /*
+*Maximum horizontal distance in pixels between points in which left and
+right button clicks occured for them to be translated into single
+middle button event. Clicks must occur in time not longer than defined
+by the variable w32-mouse-button-tolerance.
+If negative or zero, currently set system default is used instead.
+*/ );
+
+  DEFVAR_INT ("w32-mouse-button-max-skew-y", &mswindows_button2_max_skew_y /*
+*Maximum vertical distance in pixels between points in which left and
+right button clicks occured for them to be translated into single
+middle button event. Clicks must occur in time not longer than defined
+by the variable w32-mouse-button-tolerance.
+If negative or zero, currently set system default is used instead.
+*/ );
+
+  mswindows_button2_max_skew_x = 0;
+  mswindows_button2_max_skew_y = 0;
+  mswindows_button2_chord_time = 0;
 }
 
 void
@@ -501,4 +642,7 @@
 init_event_mswindows_late (void)
 {
   event_stream = mswindows_event_stream;
+
+  mswindows_dynamic_frame_resize = !GetSystemMetrics (SM_SLOWMACHINE);
+  mswindows_num_mouse_buttons = GetSystemMetrics (SM_CMOUSEBUTTONS);
 }