diff src/event-msw.c @ 249:83b3d10dcba9 r20-5b23

Import from CVS: tag r20-5b23
author cvs
date Mon, 13 Aug 2007 10:19:09 +0200
parents 51092a27c943
children 677f6a0ee643
line wrap: on
line diff
--- a/src/event-msw.c	Mon Aug 13 10:18:22 2007 +0200
+++ b/src/event-msw.c	Mon Aug 13 10:19:09 2007 +0200
@@ -33,8 +33,17 @@
 #include <config.h>
 #include "lisp.h"
 
+#include "console-msw.h"
+
+#ifdef HAVE_SCROLLBARS
+# include "scrollbar-msw.h"
+#endif
+
+#ifdef HAVE_MENUBARS
+# include "menubar-msw.h"
+#endif
+
 #include "device.h"
-#include "console-msw.h"
 #include "emacsfns.h"
 #include "events.h"
 #include "frame.h"
@@ -44,7 +53,35 @@
 #include "syswait.h"
 #include "systime.h"
 
-#include "event-msw.h"
+#include "events-mod.h"
+
+#ifdef HAVE_MENUBARS
+#define ADJR_MENUFLAG TRUE
+#else
+#define ADJR_MENUFLAG FALSE
+#endif
+
+/* Fake key modifier which is attached to a quit char event.
+   Removed upon dequeueing an event */
+#define FAKE_MOD_QUIT	0x80
+
+/* Timer ID used for button2 emulation */
+#define BUTTON_2_TIMER_ID 1
+
+/* Drag and drop event data types (subset of types in offix-types.h) */
+#define DndFile		2
+#define	DndFiles	3
+#define	DndText		4
+
+
+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);
+static int mswindows_modifier_state (BYTE* keymap, 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);
+
 
 static struct event_stream *mswindows_event_stream;
 
@@ -57,19 +94,14 @@
 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.
- * Apart from the dispatch queue semaphore, all of these handles may be waited
- * on multiple times in emacs_mswindows_next_event before being processed and so
- * must be manual-reset events.
- */
+/* The number of things we can wait on */
+#define MAX_WAITABLE (MAXIMUM_WAIT_OBJECTS - 1)
+
+/* List of mswindows waitable handles. */
 static HANDLE mswindows_waitable[MAX_WAITABLE];
 
-/* 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
+/* Incremented in WM_[SYS]KEYDOWN handler in the mswindows_wnd_proc()
    Decremented in mswindows_dequeue_dispatch_event() */
 int mswindows_quit_chars_count = 0;
 
@@ -96,10 +128,15 @@
 {
   return (sevt->event_type == key_press_event
 	  || sevt->event_type == button_press_event
-	  || sevt->event_type == button_release_event);
+	  || sevt->event_type == button_release_event
+	  || sevt->event_type == dnd_drop_event);
 }
 
-/*
+/************************************************************************/
+/*                     Dispatch queue management                        */
+/************************************************************************/
+
+/* 
  * Add an emacs event to the proper dispatch queue
  */
 void
@@ -117,6 +154,69 @@
   PostMessage (NULL, XM_BUMPQUEUE, 0, 0);
 }
 
+void
+mswindows_enqueue_magic_event (HWND hwnd, UINT message)
+{
+  Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
+  struct Lisp_Event* event = XEVENT (emacs_event);
+
+  event->channel = mswindows_find_frame (hwnd);
+  event->timestamp = GetMessageTime();
+  event->event_type = magic_event;
+  EVENT_MSWINDOWS_MAGIC_TYPE (event) = message;
+
+  mswindows_enqueue_dispatch_event (emacs_event);
+}
+
+static void
+mswindows_enqueue_mouse_button_event (HWND hwnd, UINT message, POINTS where, DWORD when)
+{
+
+  /* We always use last message time, because mouse button
+     events may get delayed, and XEmacs double click
+     recognition will fail */
+
+  Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
+  struct Lisp_Event* event = XEVENT(emacs_event);
+
+  event->channel = mswindows_find_frame(hwnd);
+  event->timestamp = when;
+  event->event.button.button =
+    (message==WM_LBUTTONDOWN || message==WM_LBUTTONUP) ? 1 :
+    ((message==WM_RBUTTONDOWN || message==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);
+      
+  if (message==WM_LBUTTONDOWN || message==WM_MBUTTONDOWN ||
+      message==WM_RBUTTONDOWN)
+    {
+      event->event_type = button_press_event;
+      SetCapture (hwnd);
+    }
+  else
+    {
+      event->event_type = button_release_event;
+      ReleaseCapture ();
+    }
+  
+  mswindows_enqueue_dispatch_event (emacs_event);
+}
+
+static void
+mswindows_enqueue_keypress_event (HWND hwnd, Lisp_Object keysym, int mods)
+{
+  Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
+  struct Lisp_Event* event = XEVENT(emacs_event);
+
+  event->channel = mswindows_find_console(hwnd);
+  event->timestamp = GetMessageTime();
+  event->event_type = key_press_event;
+  event->event.key.keysym = keysym;
+  event->event.key.modifiers = mods;
+  mswindows_enqueue_dispatch_event (emacs_event);
+}
+
 /*
  * Remove and return the first emacs event on the dispatch queue.
  * Give a preference to user events over non-user ones.
@@ -204,6 +304,11 @@
   return Qnil;
 }
 
+
+/************************************************************************/
+/*                             Event pump                               */
+/************************************************************************/
+
 static Lisp_Object
 mswindows_modal_loop_error_handler (Lisp_Object cons_sig_data,
 				    Lisp_Object u_n_u_s_e_d)
@@ -312,98 +417,16 @@
   /* This function can call lisp */
 
   Lisp_Object result = Qt;
-
+  struct gcpro gcpro1;
+  GCPRO1 (result);
+  
   if (NILP(mswindows_error_caught_in_modal_loop))
       result = mswindows_protect_modal_loop (mswindows_unsafe_pump_events, Qnil);
+  UNGCPRO;
   return result;
 }
 
-/*
- * Find a free waitable slot
- */
-#if 0 /* NOTUSED */
-static int
-mswindows_find_free_waitable(void)
-{
-  int i;
-  for (i=0; i<mswindows_waitable_count; i++)
-    if (mswindows_waitable_info[i].type == mswindows_waitable_type_none)
-      return i;
-  assert (mswindows_waitable_count < MAX_WAITABLE);
-  return mswindows_waitable_count++;
-}
-#endif
 
-/*
- * Create a new waitable using the type and data passed in by the info structure
- * Returns a pointer to the info associated with the assigned waitable object
- */
-mswindows_waitable_info_type *
-mswindows_add_waitable(mswindows_waitable_info_type *info)
-{
-  int waitable;
-
-  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;
-#if 0
-    InitializeCriticalSection(&mswindows_dispatch_crit);
-#endif
-    assert (mswindows_waitable[0] = CreateSemaphore (NULL, 0, 0x7fffffff, NULL));
-    return mswindows_waitable_info+0;
-
-  default:
-    assert(0);
-  }
-  mswindows_waitable_info[waitable].type = info->type;
-  return mswindows_waitable_info+waitable;
-}
-
-/*
- * Remove a waitable using the type and data passed in by the info structure.
- */
-void
-mswindows_remove_waitable(mswindows_waitable_info_type *info)
-{
-  int waitable;
-
-  switch (info->type)
-  {
-
-  default:
-    assert(0);
-  }
-
-  CloseHandle(mswindows_waitable[waitable]);
-  mswindows_waitable[waitable] = 0;
-  mswindows_waitable_info[waitable].type = mswindows_waitable_type_none;
-  if (waitable == mswindows_waitable_count-1)
-    --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);
-
-  if (KillTimer (NULL, id_timer))
-    --mswindows_pending_timers_count;
-
-  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 ()
@@ -523,20 +546,841 @@
       {
 	/* 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);
-	  }
+	assert(0);	/* #### */
       }
   } /* while */
 
   return;
 }
 
+/************************************************************************/
+/*                           Event generators                           */
+/************************************************************************/
+
+/* 
+ * 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);
+
+  if (KillTimer (NULL, id_timer))
+    --mswindows_pending_timers_count;
+
+  event->channel = Qnil;
+  event->timestamp = dwtime;
+  event->event_type = timeout_event;
+  event->event.timeout.interval_id = id_timer;
+
+  mswindows_enqueue_dispatch_event (emacs_event);
+}
+
+/* 
+ * Callback procedure for dde messages
+ */
+HDDEDATA CALLBACK
+mswindows_dde_callback (UINT uType, UINT uFmt, HCONV hconv,
+			HSZ hszTopic, HSZ hszItem, HDDEDATA hdata,
+			DWORD dwData1, DWORD dwData2)
+{ 
+  switch (uType)
+    { 
+    case XTYP_CONNECT:
+      if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system))
+	return (HDDEDATA)TRUE;
+      return (HDDEDATA)FALSE;
+
+    case XTYP_WILDCONNECT:
+      {
+	/* We only support one {service,topic} pair */
+	HSZPAIR pairs[2] = {
+	  { mswindows_dde_service, mswindows_dde_topic_system }, { 0, 0 } };
+
+	if (!(hszItem  || DdeCmpStringHandles (hszItem, mswindows_dde_service)) &&
+	    !(hszTopic || DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system)));
+	  return (DdeCreateDataHandle (mswindows_dde_mlid, (LPBYTE)pairs,
+				       sizeof (pairs), 0L, 0, uFmt, 0));
+      }
+      return (HDDEDATA)NULL; 
+
+    case XTYP_EXECUTE:
+      if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system))
+	{
+	  DWORD len = DdeGetData (hdata, NULL, 0, 0);
+	  char *cmd = alloca (len+1);
+	  char *end;
+          Lisp_Object l_dndlist;
+	  Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
+	  struct Lisp_Event *event = XEVENT (emacs_event);
+
+	  DdeGetData (hdata, cmd, len, 0);
+	  cmd[len] = '\0';
+	  DdeFreeDataHandle (hdata);
+
+	  /* Check syntax & that it's an [Open("foo")] command */
+	  /* #### Ought to be generalised and accept some other commands */
+	  if (*cmd == '[')
+	    cmd++;
+	  if (strnicmp (cmd, MSWINDOWS_DDE_ITEM_OPEN,
+			strlen (MSWINDOWS_DDE_ITEM_OPEN)))
+	    return DDE_FNOTPROCESSED;
+	  cmd += strlen (MSWINDOWS_DDE_ITEM_OPEN);
+	  while (*cmd==' ')
+	    cmd++;
+	  if (*cmd!='(' || *(cmd+1)!='\"')
+	    return DDE_FNOTPROCESSED;
+	  end = (cmd+=2);
+	  while (*end && *end!='\"')
+	    end++;
+	  if (!*end)
+	    return DDE_FNOTPROCESSED;
+	  *end = '\0';
+	  if (*(++end)!=')')
+	    return DDE_FNOTPROCESSED;
+	  if (*(++end)==']')
+	    end++;
+	  if (*end)
+	    return DDE_FNOTPROCESSED;
+
+	  l_dndlist = make_ext_string (cmd, strlen(cmd), FORMAT_FILENAME);
+
+	  event->channel = Qnil;
+	  event->timestamp = GetTickCount();
+	  event->event_type = dnd_drop_event;
+	  event->event.dnd_drop.button = 0;
+	  event->event.dnd_drop.modifiers = 0;
+	  event->event.dnd_drop.x = -1;
+	  event->event.dnd_drop.y = -1;
+	  event->event.dnd_drop.data = Fcons (make_int (DndFile),
+					      Fcons (l_dndlist, Qnil));
+	  mswindows_enqueue_dispatch_event (emacs_event);
+
+	  return (HDDEDATA) DDE_FACK;
+	}
+      DdeFreeDataHandle (hdata); 
+      return (HDDEDATA) DDE_FNOTPROCESSED;
+
+    default: 
+      return (HDDEDATA) NULL; 
+    } 
+
+}
+
+/*
+ * The windows procedure for the window class XEMACS_CLASS
+ */
+LRESULT WINAPI
+mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+  /* Note: Remember to initialise emacs_event and event before use.
+     This code calls code that can GC. You must GCPRO before calling such code. */
+  Lisp_Object emacs_event = Qnil;
+  Lisp_Object fobj = Qnil;
+
+  struct Lisp_Event *event;
+  struct frame *frame;
+  struct mswindows_frame* msframe;
+
+  switch (message)
+  {
+  case WM_ERASEBKGND:
+    /* Erase background only during non-dynamic sizing */
+    msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
+    if (msframe->sizing && !mswindows_dynamic_frame_resize)
+      goto defproc;
+    return 1;
+
+  case WM_CLOSE:
+    fobj = mswindows_find_frame (hwnd);
+    enqueue_misc_user_event (fobj, Qeval, list3 (Qdelete_frame, fobj, Qt));
+    mswindows_enqueue_magic_event (hwnd, XM_BUMPQUEUE);
+    break;
+
+  case WM_KEYDOWN:
+  case WM_SYSKEYDOWN:
+    {
+      BYTE keymap[256];
+      int has_AltGr = mswindows_current_layout_has_AltGr ();
+      int mods;
+      Lisp_Object keysym;
+
+      GetKeyboardState (keymap);
+      mods = mswindows_modifier_state (keymap, has_AltGr);
+
+      /* Handle those keys that TranslateMessage won't generate a WM_CHAR for */
+      if (!NILP (keysym = mswindows_key_to_emacs_keysym(wParam, mods)))
+	mswindows_enqueue_keypress_event (hwnd, keysym, mods);
+      else
+	{
+	  int quit_ch = CONSOLE_QUIT_CHAR (XCONSOLE (mswindows_find_console (hwnd)));
+	  BYTE keymap_orig[256];
+	  MSG msg = { hwnd, message, wParam, lParam, GetMessageTime(), (GetMessagePos()) };
+	  memcpy (keymap_orig, keymap, 256);
+
+	  /* Clear control and alt modifiers out of the keymap */
+	  keymap [VK_RCONTROL] = 0;
+	  keymap [VK_LMENU] = 0;
+	  if (!has_AltGr || !(keymap [VK_LCONTROL] & 0x80) || !(keymap [VK_RMENU] & 0x80))
+	    {
+	      keymap [VK_LCONTROL] = 0;
+	      keymap [VK_CONTROL] = 0;
+	      keymap [VK_RMENU] = 0;
+	      keymap [VK_MENU] = 0;
+	    }
+	  SetKeyboardState (keymap);
+
+	  /* Have some WM_[SYS]CHARS in the queue */
+	  TranslateMessage (&msg);
+
+	  while (PeekMessage (&msg, hwnd, WM_CHAR, WM_CHAR, PM_REMOVE)
+		 ||PeekMessage (&msg, hwnd, WM_SYSCHAR, WM_SYSCHAR, PM_REMOVE))
+	    {
+	      int ch = msg.wParam;
+	      /* CH is a character code for the key: 
+		 'C' for Shift+C and Ctrl+Shift+C
+		 'c' for c and Ctrl+c */
+
+	      /* #### If locale is not C, US or other latin-1,
+		 isalpha() maybe not what do we mean */
+	      
+	      /* XEmacs doesn't seem to like Shift on non-alpha keys */
+	      if (!isalpha(ch))
+		mods &= ~MOD_SHIFT;
+
+	      /* Un-capitalise alpha control keys */
+	      if ((mods & MOD_CONTROL) && isalpha(ch))
+		ch |= ('A' ^ 'a');
+
+	      /* If a quit char with no modifiers other than control and
+		 shift, then mark it with a fake modifier, which is removed
+		 upon dequeueing the event */
+	      /* #### This might also not withstand localization, if
+		 quit character is not a latin-1 symbol */
+	      if (((quit_ch < ' ' && (mods & MOD_CONTROL) && quit_ch + 'a' - 1 == ch)
+		   || (quit_ch >= ' ' && !(mods & MOD_CONTROL) && quit_ch == ch))
+		  && ((mods  & ~(MOD_CONTROL | MOD_SHIFT)) == 0))
+		{
+		  mods |= FAKE_MOD_QUIT;
+		  ++mswindows_quit_chars_count;
+		}
+
+	      mswindows_enqueue_keypress_event (hwnd, make_char(ch), mods);
+	    } /* while */
+	  SetKeyboardState (keymap_orig);
+	} /* else */
+    }
+    goto defproc;
+
+  case WM_MBUTTONDOWN:
+  case WM_MBUTTONUP:
+    /* 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());
+    break;
+    
+  case WM_LBUTTONUP:
+    msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
+    msframe->last_click_time =  GetMessageTime();
+
+    KillTimer (hwnd, BUTTON_2_TIMER_ID);
+    msframe->button2_need_lbutton = 0;
+    if (msframe->ignore_next_lbutton_up)
+      {
+	msframe->ignore_next_lbutton_up = 0;
+      }
+    else if (msframe->button2_is_down)
+      {
+	msframe->button2_is_down = 0;
+	msframe->ignore_next_rbutton_up = 1;
+	mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP,
+					      MAKEPOINTS (lParam), GetMessageTime());
+      }
+    else
+      {
+	if (msframe->button2_need_rbutton)
+	  {
+	    msframe->button2_need_rbutton = 0;
+	    mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
+						  MAKEPOINTS (lParam), GetMessageTime());
+	  }
+	mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONUP,
+					      MAKEPOINTS (lParam), GetMessageTime());
+      }
+    break;
+
+  case WM_RBUTTONUP:
+    msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
+    msframe->last_click_time =  GetMessageTime();
+
+    KillTimer (hwnd, BUTTON_2_TIMER_ID);
+    msframe->button2_need_rbutton = 0;
+    if (msframe->ignore_next_rbutton_up)
+      {
+	msframe->ignore_next_rbutton_up = 0;
+      }
+    else if (msframe->button2_is_down)
+      {
+	msframe->button2_is_down = 0;
+	msframe->ignore_next_lbutton_up = 1;
+	mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP,
+					      MAKEPOINTS (lParam), GetMessageTime());
+      }
+    else
+      {
+	if (msframe->button2_need_lbutton)
+	  {
+	    msframe->button2_need_lbutton = 0;
+	    mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
+						  MAKEPOINTS (lParam), GetMessageTime());
+	  }
+	mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONUP,
+					      MAKEPOINTS (lParam), GetMessageTime());
+      }
+    break;
+
+  case WM_LBUTTONDOWN:
+    msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
+
+    if (msframe->button2_need_lbutton)
+      {
+	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)))
+	  {
+	    mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN,
+						  MAKEPOINTS (lParam), GetMessageTime());
+	    msframe->button2_is_down = 1;
+	  }
+	else
+	  {
+	    mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
+			msframe->last_click_point, msframe->last_click_time);
+	    mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
+						  MAKEPOINTS (lParam), GetMessageTime());
+	  }
+      }
+    else
+      {
+	mswindows_set_chord_timer (hwnd);
+	msframe->button2_need_rbutton = 1;
+	msframe->last_click_point = MAKEPOINTS (lParam);
+      }
+    msframe->last_click_time =  GetMessageTime();
+    break;
+
+  case WM_RBUTTONDOWN:
+    msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
+
+    if (msframe->button2_need_rbutton)
+      {
+	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)))
+	  {
+	    mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN,
+						  MAKEPOINTS (lParam), GetMessageTime());
+	    msframe->button2_is_down = 1;
+	  }
+	else
+	  {
+	    mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
+				msframe->last_click_point, msframe->last_click_time);
+	    mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
+						  MAKEPOINTS (lParam), GetMessageTime());
+	  }
+      }
+    else
+      {
+	mswindows_set_chord_timer (hwnd);
+	msframe->button2_need_lbutton = 1;
+	msframe->last_click_point = MAKEPOINTS (lParam);
+      }
+    msframe->last_click_time =  GetMessageTime();
+    break;
+	
+  case WM_TIMER:
+    if (wParam == BUTTON_2_TIMER_ID)
+      {
+	msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
+	KillTimer (hwnd, BUTTON_2_TIMER_ID);
+
+	if (msframe->button2_need_lbutton)
+	  {
+	    msframe->button2_need_lbutton = 0;
+	    mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
+				msframe->last_click_point, 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);
+	  }
+      }
+    else
+      assert ("Spurious timer fired" == 0);
+    break;
+
+  case WM_MOUSEMOVE:
+    /* Optimization: don't report mouse movement while size is changind */
+    msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
+    if (!msframe->sizing)
+    {
+      /* When waiting for the second mouse button to finish
+	 button2 emulation, and have moved too far, just pretend
+	 as if timer has expired. This impoves drag-select feedback */
+      if ((msframe->button2_need_lbutton || msframe->button2_need_rbutton)
+	  && !mswindows_button2_near_enough (msframe->last_click_point,
+					     MAKEPOINTS (lParam)))
+	{
+	  KillTimer (hwnd, BUTTON_2_TIMER_ID);
+	  SendMessage (hwnd, WM_TIMER, BUTTON_2_TIMER_ID, 0);
+	}
+
+      emacs_event = Fmake_event (Qnil, Qnil);
+      event = XEVENT(emacs_event);
+
+      event->channel = mswindows_find_frame(hwnd);
+      event->timestamp = GetMessageTime();
+      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);
+      
+      mswindows_enqueue_dispatch_event (emacs_event);
+    }
+    break;
+
+  case WM_PAINT:
+    {
+      PAINTSTRUCT paintStruct;
+      
+      frame = XFRAME (mswindows_find_frame (hwnd));
+
+      BeginPaint (hwnd, &paintStruct);
+      mswindows_redraw_exposed_area (frame,
+			paintStruct.rcPaint.left, paintStruct.rcPaint.top,
+			paintStruct.rcPaint.right, paintStruct.rcPaint.bottom);
+      EndPaint (hwnd, &paintStruct);
+    }
+    break;
+
+  case WM_SIZE:
+    /* We only care about this message if our size has really changed */
+    if (wParam==SIZE_RESTORED || wParam==SIZE_MAXIMIZED || wParam==SIZE_MINIMIZED)
+    {
+      RECT rect;
+      int columns, rows;
+
+      fobj = mswindows_find_frame (hwnd);
+      frame = XFRAME (fobj);
+      msframe  = FRAME_MSWINDOWS_DATA (frame);
+
+      /* We cannot handle frame map and unmap hooks right in
+	 this routine, because these may throw. We queue
+	 magic events to run these hooks instead - kkm */
+
+      if (wParam==SIZE_MINIMIZED)
+	{
+	  /* Iconified */
+	  FRAME_VISIBLE_P (frame) = 0;
+	  mswindows_enqueue_magic_event (hwnd, XM_UNMAPFRAME);
+	  Fframe_iconified_p (fobj);
+	}
+      else
+	{
+	  int was_visible = FRAME_VISIBLE_P (frame);
+	  if (!msframe->sizing && !was_visible)
+	    mswindows_enqueue_magic_event (hwnd, XM_MAPFRAME);
+	  
+	  GetClientRect(hwnd, &rect);
+      	  FRAME_VISIBLE_P(frame) = 1;
+	  FRAME_PIXWIDTH(frame) = rect.right;
+	  FRAME_PIXHEIGHT(frame) = rect.bottom;
+	  pixel_to_char_size (frame, rect.right, rect.bottom, &columns, &rows);
+	  change_frame_size (frame, rows, columns, 1);
+
+	  if (msframe->sizing && mswindows_dynamic_frame_resize)
+	    redisplay ();
+	}
+    }
+    break;
+
+  /* Misc magic events which only require that the frame be identified */
+  case WM_SETFOCUS:
+  case WM_KILLFOCUS:
+    mswindows_enqueue_magic_event (hwnd, message);
+    break;
+
+  case WM_WINDOWPOSCHANGING:
+    {
+      WINDOWPOS *wp = (LPWINDOWPOS) lParam;
+      WINDOWPLACEMENT wpl = { sizeof(WINDOWPLACEMENT) };
+      GetWindowPlacement(hwnd, &wpl);
+
+      /* Only interested if size is changing and we're not being iconified */
+      if ((wpl.showCmd != SW_SHOWMINIMIZED) && !(wp->flags & SWP_NOSIZE))
+      {
+	RECT ncsize = { 0, 0, 0, 0 };
+	int pixwidth, pixheight;
+ 	AdjustWindowRectEx (&ncsize, GetWindowLong (hwnd, GWL_STYLE),
+ 			    GetMenu(hwnd) != NULL,
+			    GetWindowLong (hwnd, GWL_EXSTYLE));
+
+	round_size_to_char (XFRAME (mswindows_find_frame (hwnd)),
+			    wp->cx - (ncsize.right - ncsize.left),
+			    wp->cy - (ncsize.bottom - ncsize.top),
+			    &pixwidth, &pixheight);
+
+	/* Convert client sizes to window sizes */
+	pixwidth += (ncsize.right - ncsize.left);
+	pixheight += (ncsize.bottom - ncsize.top);
+
+	if (wpl.showCmd != SW_SHOWMAXIMIZED)
+	  {
+	    /* Adjust so that the bottom or right doesn't move if it's
+	     * the top or left that's being changed */
+	    RECT rect;
+	    GetWindowRect (hwnd, &rect);
+
+	    if (rect.left != wp->x)
+	      wp->x += wp->cx - pixwidth;
+	    if (rect.top != wp->y)
+	      wp->y += wp->cy - pixheight;
+	  }
+
+	wp->cx = pixwidth;
+	wp->cy = pixheight;
+      }
+    }
+    break;
+
+  case WM_ENTERSIZEMOVE:
+    msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
+    msframe->sizing = 1;
+    return 0;
+
+  case WM_EXITSIZEMOVE:
+    msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
+    msframe->sizing = 0;
+    /* Queue noop event */
+    mswindows_enqueue_magic_event (hwnd, XM_BUMPQUEUE);
+    return 0;
+
+#ifdef HAVE_SCROLLBARS
+  case WM_VSCROLL:
+  case WM_HSCROLL:
+    {
+      /* Direction of scroll is determined by scrollbar instance. */
+      int code = (int) LOWORD(wParam);
+      int pos = (short int) HIWORD(wParam);
+      HWND hwndScrollBar = (HWND) lParam;
+      struct gcpro gcpro1, gcpro2;
+
+      mswindows_handle_scrollbar_event (hwndScrollBar, code,  pos);
+      GCPRO2 (emacs_event, fobj);
+      if (UNBOUNDP(mswindows_pump_outstanding_events()))	/* Can GC */
+	{
+	  /* Error during event pumping - cancel scroll */
+	  SendMessage (hwndScrollBar, WM_CANCELMODE, 0, 0);
+	}
+      UNGCPRO;
+      break;     
+    }
+#endif
+
+#ifdef HAVE_MENUBARS
+  case WM_INITMENU:
+    if (UNBOUNDP (mswindows_handle_wm_initmenu (
+			(HMENU) wParam,
+			XFRAME (mswindows_find_frame (hwnd)))))
+      SendMessage (hwnd, WM_CANCELMODE, 0, 0);
+    break;
+
+  case WM_INITMENUPOPUP:
+    if (!HIWORD(lParam))
+      {
+	if (UNBOUNDP (mswindows_handle_wm_initmenupopup (
+			(HMENU) wParam,
+			 XFRAME (mswindows_find_frame (hwnd)))))
+	  SendMessage (hwnd, WM_CANCELMODE, 0, 0);
+      }
+    break;
+
+  case WM_EXITMENULOOP:
+    if (UNBOUNDP (mswindows_handle_wm_exitmenuloop (
+			XFRAME (mswindows_find_frame (hwnd)))))
+      SendMessage (hwnd, WM_CANCELMODE, 0, 0);
+    break;
+
+#endif /* HAVE_MENUBARS */
+
+  case WM_COMMAND:
+    {
+      WORD id = LOWORD (wParam);
+      frame = XFRAME (mswindows_find_frame (hwnd));
+
+#ifdef HAVE_MENUBARS
+      if (!NILP (mswindows_handle_wm_command (frame, id)))
+	break;
+#endif
+
+#ifdef HAVE_TOOLBARS
+      O Toolbar Implementor, this place may have something for you!;
+#endif
+
+      /* Bite me - a spurious command. No abort(), for safety */
+      /* #### Perhaps, this message should be changed */
+      error ("Cannot decode command. Tell kkm he's a parallelogramm, if you know"
+	     " what does that mean!");
+    }
+  break;
+
+  case WM_DROPFILES:	/* implementation ripped-off from event-Xt.c */
+    {
+      UINT filecount, i, len;
+      POINT point;
+      char filename[MAX_PATH];
+      Lisp_Object l_type, l_dndlist = Qnil, l_item;
+
+      emacs_event = Fmake_event (Qnil, Qnil);
+      event = XEVENT(emacs_event);
+
+      if (!DragQueryPoint ((HANDLE) wParam, &point))
+	point.x = point.y = -1;		/* outside client area */
+
+      filecount = DragQueryFile ((HANDLE) wParam, -1, NULL, 0);
+      if (filecount == 1)
+	{
+      	  l_type = make_int (DndFile);
+	  len = DragQueryFile ((HANDLE) wParam, 0, filename, MAX_PATH);
+	  l_dndlist = make_ext_string (filename, len, FORMAT_FILENAME);
+	}
+      else
+	{
+	  l_type = make_int (DndFiles);	  
+	  for (i=0; i<filecount; i++)
+	    {
+  	      len = DragQueryFile ((HANDLE) wParam, i, filename, MAX_PATH);
+	      l_item = make_ext_string (filename, len, FORMAT_FILENAME);
+	      l_dndlist = Fcons (l_item, l_dndlist);	/* reverse order */
+	    }
+	}
+      DragFinish ((HANDLE) wParam);
+      
+      event->channel = mswindows_find_frame(hwnd);
+      event->timestamp = GetMessageTime();
+      event->event_type = dnd_drop_event;
+      event->event.dnd_drop.button = 1;		/* #### Should try harder */
+      event->event.dnd_drop.modifiers = mswindows_modifier_state (NULL, 0);
+      event->event.dnd_drop.x = point.x;
+      event->event.dnd_drop.y = point.y;
+      event->event.dnd_drop.data = Fcons (l_type, Fcons (l_dndlist, Qnil));
+
+      mswindows_enqueue_dispatch_event (emacs_event);
+    }
+  break;
+
+  defproc:
+  default:
+    return DefWindowProc (hwnd, message, wParam, lParam);
+  }
+  return (0);
+}
+
+
+/************************************************************************/
+/*      keyboard, mouse & other helpers for the windows procedure       */
+/************************************************************************/
+static void
+mswindows_set_chord_timer (HWND hwnd)
+{
+  int interval;
+
+  /* We get half system threshold as it seems to
+     long before drag-selection is shown */
+  if (mswindows_button2_chord_time <= 0)
+    interval = GetDoubleClickTime () / 2;
+  else
+    interval = mswindows_button2_chord_time;
+
+  SetTimer (hwnd, BUTTON_2_TIMER_ID, interval, 0);
+}
+
+static int
+mswindows_button2_near_enough (POINTS p1, POINTS p2)
+{
+  int dx, dy;
+  if (mswindows_button2_max_skew_x <= 0)
+    dx = GetSystemMetrics (SM_CXDOUBLECLK) / 2;
+  else
+    dx = mswindows_button2_max_skew_x;
+
+  if (mswindows_button2_max_skew_y <= 0)
+    dy = GetSystemMetrics (SM_CYDOUBLECLK) / 2;
+  else
+    dy = mswindows_button2_max_skew_y;
+
+  return abs (p1.x - p2.x) < dx && abs (p1.y- p2.y)< dy;
+}
+
+static int
+mswindows_current_layout_has_AltGr (void)
+{
+  /* This simple caching mechanism saves 10% of CPU
+     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 = GetKeyboardLayout (0);
+  if (current_hkl != last_hkl)
+    {
+      TCHAR c;
+      last_hkl_has_AltGr = 0;
+      /* In this loop, we query whether a character requires
+	 AltGr to be down to generate it. If at least such one
+	 found, this means that the layout does regard AltGr */
+      for (c = ' '; c <= 0xFFU && c != 0 && !last_hkl_has_AltGr; ++c)
+	if (HIBYTE (VkKeyScan (c)) == 6)
+	  last_hkl_has_AltGr = 1;
+      last_hkl = current_hkl;
+    }
+  return last_hkl_has_AltGr;
+}
+
+
+/* Returns the state of the modifier keys in the format expected by the
+ * Lisp_Event key_data, button_data and motion_data modifiers member */
+int mswindows_modifier_state (BYTE* keymap, int has_AltGr)
+{
+  int mods = 0;
+
+  if (keymap == NULL)
+    {
+      keymap = (BYTE*) alloca(256);
+      GetKeyboardState (keymap);
+      has_AltGr = mswindows_current_layout_has_AltGr ();
+    }
+
+  if (has_AltGr && (keymap [VK_LCONTROL] & 0x80) && (keymap [VK_RMENU] & 0x80))
+    {
+      mods |= (keymap [VK_LMENU] & 0x80) ? MOD_META : 0;
+      mods |= (keymap [VK_RCONTROL] & 0x80) ? MOD_CONTROL : 0;
+    }
+  else
+    {
+      mods |= (keymap [VK_MENU] & 0x80) ? MOD_META : 0;
+      mods |= (keymap [VK_CONTROL] & 0x80) ? MOD_CONTROL : 0;
+    }
+
+  mods |= (keymap [VK_SHIFT] & 0x80) ? MOD_SHIFT : 0;
+
+  return mods;
+}
+
+/*
+ * 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
+ * XXX I'm not sure that KEYSYM("name") is the best thing to use here.
+ */
+Lisp_Object mswindows_key_to_emacs_keysym(int mswindows_key, int mods)
+{
+  switch (mswindows_key)
+  {
+  /* First the predefined ones */
+  case VK_BACK:		return QKbackspace;
+  case VK_TAB:		return QKtab;
+  case '\n':		return QKlinefeed;  /* No VK_LINEFEED in winresrc.h */
+  case VK_RETURN:	return QKreturn;
+  case VK_ESCAPE:	return QKescape;
+  case VK_SPACE:	return QKspace;
+  case VK_DELETE:	return QKdelete;
+
+  /* The rest */
+  case VK_CLEAR:	return KEYSYM ("clear");  /* Should do ^L ? */
+  case VK_PRIOR:	return KEYSYM ("prior");
+  case VK_NEXT:		return KEYSYM ("next");
+  case VK_END:		return KEYSYM ("end");
+  case VK_HOME:		return KEYSYM ("home");
+  case VK_LEFT:		return KEYSYM ("left");
+  case VK_UP:		return KEYSYM ("up");
+  case VK_RIGHT:	return KEYSYM ("right");
+  case VK_DOWN:		return KEYSYM ("down");
+  case VK_SELECT:	return KEYSYM ("select");
+  case VK_PRINT:	return KEYSYM ("print");
+  case VK_EXECUTE:	return KEYSYM ("execute");
+  case VK_SNAPSHOT:	return KEYSYM ("print");
+  case VK_INSERT:	return KEYSYM ("insert");
+  case VK_HELP:		return KEYSYM ("help");
+#if 0	/* XXX What are these supposed to do? */
+  case VK_LWIN		return KEYSYM ("");
+  case VK_RWIN		return KEYSYM ("");
+#endif
+  case VK_APPS:		return KEYSYM ("menu");
+  case VK_F1:		return KEYSYM ("f1");
+  case VK_F2:		return KEYSYM ("f2");
+  case VK_F3:		return KEYSYM ("f3");
+  case VK_F4:		return KEYSYM ("f4");
+  case VK_F5:		return KEYSYM ("f5");
+  case VK_F6:		return KEYSYM ("f6");
+  case VK_F7:		return KEYSYM ("f7");
+  case VK_F8:		return KEYSYM ("f8");
+  case VK_F9:		return KEYSYM ("f9");
+  case VK_F10:		return KEYSYM ("f10");
+  case VK_F11:		return KEYSYM ("f11");
+  case VK_F12:		return KEYSYM ("f12");
+  case VK_F13:		return KEYSYM ("f13");
+  case VK_F14:		return KEYSYM ("f14");
+  case VK_F15:		return KEYSYM ("f15");
+  case VK_F16:		return KEYSYM ("f16");
+  case VK_F17:		return KEYSYM ("f17");
+  case VK_F18:		return KEYSYM ("f18");
+  case VK_F19:		return KEYSYM ("f19");
+  case VK_F20:		return KEYSYM ("f20");
+  case VK_F21:		return KEYSYM ("f21");
+  case VK_F22:		return KEYSYM ("f22");
+  case VK_F23:		return KEYSYM ("f23");
+  case VK_F24:		return KEYSYM ("f24");
+  }
+  return Qnil;
+}
+
+/*
+ * Find the console that matches the supplied mswindows window handle
+ */
+Lisp_Object
+mswindows_find_console (HWND hwnd)
+{
+  Lisp_Object concons;
+
+  CONSOLE_LOOP (concons)
+    {
+      Lisp_Object console = XCAR (concons);
+      /* We only support one console so this must be it */
+      return console;
+    }
+
+  return Qnil;
+}
+
+/*
+ * Find the frame that matches the supplied mswindows window handle
+ */
+static Lisp_Object
+mswindows_find_frame (HWND hwnd)
+{
+  return (Lisp_Object) GetWindowLong (hwnd, XWL_FRAMEOBJ);
+}
+
+
 
 /************************************************************************/
 /*                            methods                                   */
@@ -554,7 +1398,8 @@
   if (milliseconds < 1)
     milliseconds = 1;
   ++mswindows_pending_timers_count;
-  return SetTimer (NULL, 0, milliseconds, mswindows_wm_timer_callback);
+  return SetTimer (NULL, 0, milliseconds,
+		   (TIMERPROC) mswindows_wm_timer_callback);
 }
 
 static void
@@ -617,11 +1462,6 @@
 static void
 emacs_mswindows_handle_magic_event (struct Lisp_Event *emacs_event)
 {
-#if 0  
-  stderr_out("magic %x, (%d,%d), (%d,%d)\n",
-	     EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event),
-	     rect->left, rect->top, rect->right, rect->bottom);
-#endif
   switch (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event))
   {
   case WM_SETFOCUS:
@@ -662,7 +1502,7 @@
     }
     break;
 			    
-      /* XXX What about Enter & Leave */
+      /* #### What about Enter & Leave */
 #if 0
       va_run_hook_with_args (in_p ? Qmouse_enter_frame_hook :
 			     Qmouse_leave_frame_hook, 1, frame);