emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Using windows keys for Emacs META on w32


From: Lennart Borgman (gmail)
Subject: Using windows keys for Emacs META on w32
Date: Sat, 05 Jan 2008 21:48:48 +0100
User-agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.9) Gecko/20071031 Thunderbird/2.0.0.9 Mnenhy/0.7.5.666

I promised Juanma to make a new patch with the changes I have made to allow using the windows left and/or right keys for Emacs meta. Here it is, in a bit rough state.
Index: src/w32fns.c
===================================================================
RCS file: /sources/emacs/emacs/src/w32fns.c,v
retrieving revision 1.310
diff -u -b -r1.310 w32fns.c
--- src/w32fns.c        29 Dec 2007 19:20:04 -0000      1.310
+++ src/w32fns.c        5 Jan 2008 20:05:42 -0000
@@ -2155,9 +2155,20 @@
 #define EMACS_LMENU    2
 #define EMACS_RMENU    3

-static int modifiers[4];
+/* The keys above are also saved in modifiers[]. To allow
+   w32-pass-lwindow-to-system to be nil the state of the left and
+   right windows keys must also be saved in modifiers[]. This is a bit
+   tricky since Windows does not keep track of the state of those keys
+   when StickyKeys are on.  (Because they are registered as hot
+   keys?) */
+
+#define EMACS_LWIN    4
+#define EMACS_RWIN    5
+
+static int modifiers[6];
 static int modifiers_recorded;
 static int modifier_key_support_tested;
+static BOOL b_menu_pending = FALSE;

 static void
 test_modifier_support (unsigned int wparam)
@@ -2191,10 +2202,25 @@
   if (!modifier_key_support_tested)
     test_modifier_support (wparam);

+  if (VK_LWIN != wparam && VK_RWIN != wparam) {
   if ((wparam != VK_CONTROL && wparam != VK_MENU) || !modifiers_recorded)
     return;
+  } else {
+    if (VK_LWIN == wparam && !NILP(Vw32_pass_lwindow_to_system)) {
+      return;
+    }
+    if (VK_RWIN == wparam && !NILP(Vw32_pass_rwindow_to_system)) {
+      return;
+    }
+  }

-  if (wparam == VK_CONTROL)
+  //printf("record_keydow2 wp=%d lp=%x\n", wparam, lparam); fflush(stdout);
+
+  if (VK_LWIN == wparam) {
+    i = EMACS_LWIN;
+  } else if (VK_RWIN == wparam) {
+    i = EMACS_RWIN;
+  } else if (wparam == VK_CONTROL)
     i = (lparam & 0x1000000) ? EMACS_RCONTROL : EMACS_LCONTROL;
   else
     i = (lparam & 0x1000000) ? EMACS_RMENU : EMACS_LMENU;
@@ -2207,17 +2233,47 @@
 {
   int i;

+  if (VK_LWIN != wparam && VK_RWIN != wparam) {
   if ((wparam != VK_CONTROL && wparam != VK_MENU) || !modifiers_recorded)
     return;
+  } else {
+    if (VK_LWIN == wparam && !NILP(Vw32_pass_lwindow_to_system)) {
+      return;
+    }
+    if (VK_RWIN == wparam && !NILP(Vw32_pass_rwindow_to_system)) {
+      return;
+    }
+  }
+  //printf("record_keyup2  wp=%d lp=%x\n", wparam, lparam); fflush(stdout);

-  if (wparam == VK_CONTROL)
+  if (VK_LWIN == wparam) {
+    i = EMACS_LWIN;
+  } else if (VK_RWIN == wparam) {
+    i = EMACS_RWIN;
+  } else if (wparam == VK_CONTROL)
     i = (lparam & 0x1000000) ? EMACS_RCONTROL : EMACS_LCONTROL;
   else
     i = (lparam & 0x1000000) ? EMACS_RMENU : EMACS_LMENU;

+  if (EMACS_LWIN == i || EMACS_RWIN == i) {
+    //printf("record_keyup wp=%d, i=%d\n", wparam, i); fflush(stdout);
+  }
   modifiers[i] = 0;
 }

+static void
+set_key_state(int vkey, int state)
+{
+  BYTE keystate[256];
+
+#define CURRENT_STATE(key) ((GetAsyncKeyState (key) & 0x8000) >> 8)
+
+  GetKeyboardState (keystate);
+  keystate[vkey] = state;
+  SetKeyboardState (keystate);
+}
+
+
 /* Emacs can lose focus while a modifier key has been pressed.  When
    it regains focus, be conservative and clear all modifiers since
    we cannot reconstruct the left and right modifier state.  */
@@ -2230,6 +2286,9 @@
     /* Emacs doesn't have keyboard focus.  Do nothing.  */
     return;

+  //printf("resetting menu_pending 2\n"); fflush(stdout);
+  b_menu_pending = FALSE;
+
   ctrl = GetAsyncKeyState (VK_CONTROL);
   alt = GetAsyncKeyState (VK_MENU);

@@ -2241,6 +2300,15 @@
     /* Clear any recorded alt modifier state.  */
     modifiers[EMACS_RMENU] = modifiers[EMACS_LMENU] = 0;

+  if (!(GetAsyncKeyState(VK_LWIN) & 0x08000)) {
+    //printf("EMACS_LWIN 1=0\n"); fflush(stdout);
+    modifiers[EMACS_LWIN] = 0;
+  }
+  if (!(GetAsyncKeyState(VK_RWIN) & 0x08000)) {
+    //printf("EMACS_RWIN 1=0\n"); fflush(stdout);
+    modifiers[EMACS_RWIN] = 0;
+  }
+
   /* Update the state of all modifier keys, because modifiers used in
      hot-key combinations can get stuck on if Emacs loses focus as a
      result of a hot-key being pressed.  */
@@ -2279,13 +2347,31 @@

   if (!(GetKeyState (VK_MENU) & 0x8000))
     modifiers[EMACS_RMENU] = modifiers[EMACS_LMENU] = 0;
+
+  if (!(GetKeyState (VK_LWIN) & 0x8000)) {
+    //printf("EMACS_LWIN 2=0\n"); fflush(stdout);
+    modifiers[EMACS_LWIN] = 0;
+  }
+  if (!(GetKeyState (VK_RWIN) & 0x8000)) {
+    //printf("EMACS_RWIN 2=0\n"); fflush(stdout);
+    modifiers[EMACS_RWIN] = 0;
+  }
 }

 static int
 modifier_set (int vkey)
 {
+  //if (vkey) { printf("modifier_set %d\n", vkey); fflush(stdout); }
   if (vkey == VK_CAPITAL || vkey == VK_SCROLL)
     return (GetKeyState (vkey) & 0x1);
+  if (VK_LWIN == vkey && NILP(Vw32_pass_lwindow_to_system)) {
+    //printf("modifier_set L %d, %d\n", vkey, modifiers[EMACS_LWIN]); 
fflush(stdout);
+    return modifiers[EMACS_LWIN];
+  }
+  if (VK_RWIN == vkey && NILP(Vw32_pass_rwindow_to_system)) {
+    //printf("modifier_set R %d, %d\n", vkey, modifiers[EMACS_RWIN]); 
fflush(stdout);
+    return modifiers[EMACS_RWIN];
+  }
   if (!modifiers_recorded)
     return (GetKeyState (vkey) & 0x8000);

@@ -2481,6 +2567,13 @@
     }
 }

+int cAccelerators = 0;      // number of accelerators in table
+static HACCEL hAccelDyn = NULL;
+static LPACCEL lpaccelDyn = NULL;
+
+BOOL add_LowLevelKeyboardProc();
+BOOL remove_LowLevelKeyboardProc();
+
 /* Main message dispatch loop. */

 static void
@@ -2489,11 +2582,15 @@
   MSG msg;
   int result;
   HWND focus_window;
+  BOOL bRet;

   msh_mousewheel = RegisterWindowMessage (MSH_MOUSEWHEEL);

-  while (GetMessage (&msg, NULL, 0, 0))
+  while ( (bRet = GetMessage (&msg, NULL, 0, 0)) != 0)
     {
+      if (-1 == bRet) {
+        // error, handle?
+      } else {
       if (msg.hwnd == NULL)
        {
          switch (msg.message)
@@ -2501,6 +2598,27 @@
            case WM_NULL:
              /* Produced by complete_deferred_msg; just ignore.  */
              break;
+            case WM_QUERYENDSESSION:
+              //case WM_ENDSESSION: default handling
+              /* This is currently not used since Emacs does not
+                 recieve this message (at least not to the windows
+                 thread) but might be if Emacs is restructured. */
+              {
+                W32Msg wmsg;
+                //printf("w32_msg_pump 1: WM_QUERYENDSESSION\n"); 
fflush(stdout);
+                my_post_msg (&wmsg, 0, WM_EMACS_KILL, 0, 0);
+                break;
+              }
+            case WM_EMACS_KBD_LL:
+              if (msg.wParam) {
+                result = (int) add_LowLevelKeyboardProc();
+              } else {
+                result = (int) remove_LowLevelKeyboardProc();
+              }
+              if (!PostThreadMessage (dwMainThreadId, WM_EMACS_DONE,
+                                      result, 0))
+                abort ();
+              break;
            case WM_EMACS_CREATEWINDOW:
               /* Initialize COM for this window. Even though we don't use it,
                  some third party shell extensions can cause it to be used in
@@ -2583,7 +2701,18 @@
        }
       else
        {
-         DispatchMessage (&msg);
+          BOOL bHandled = FALSE;
+          if (hAccelDyn)
+            if (Vw32_pass_alt_to_system) {
+              bHandled = TranslateAccelerator(msg.hwnd, hAccelDyn, &msg);
+              if (bHandled) {
+                //printf("...handled\n"); fflush(stdout);
+              } else {
+               printf("...TA failed: %s\n", w32_strerror(GetLastError())); 
fflush(stdout);
+              }
+            }
+          if (!bHandled) DispatchMessage (&msg);
+        }
        }

       /* Exit nested loop when our deferred message has completed.  */
@@ -2688,6 +2817,130 @@
   PostThreadMessage (dwWindowsThreadId, WM_NULL, 0, 0);
 }

+
+
+LRESULT
+//HOOKPROC
+LowLevelKeyboardProc(INT nCode, WPARAM wParam, LPARAM lParam)
+{
+  if (nCode == HC_ACTION)
+    {
+      KBDLLHOOKSTRUCT *pkbdllhook = (KBDLLHOOKSTRUCT *)lParam;
+      BOOL bHandle = FALSE;
+      UINT transition_state;
+      UINT previous_state = 0;
+      UINT context_code;
+      UINT extended_key;
+      UINT msg;
+      WPARAM wParamMsg;
+      LPARAM lParamMsg;
+
+      switch (wParam)
+        {
+        case WM_KEYUP:
+        case WM_SYSKEYUP:
+          previous_state = 1;
+        case WM_KEYDOWN:  // This was not in the MS example, why??
+        case WM_SYSKEYDOWN:
+          /* Convert the message and send it on.
+          FIX-ME: This code should be moved down to bHandle part after
+          testing. */
+          transition_state = pkbdllhook->flags >> 7 & 1;
+          context_code     = pkbdllhook->flags >> 5 & 1;
+          extended_key     = pkbdllhook->flags & 1;
+          msg = wParam;
+          wParamMsg = pkbdllhook->vkCode;
+          lParamMsg =
+            pkbdllhook->scanCode << 16
+            | transition_state << 31
+            | previous_state << 30
+            | context_code     << 29
+            | extended_key     << 24
+            | 1; // repeat count
+/*           if (0 != GetFocus()) { */
+/*             if (91 == pkbdllhook->vkCode) { */
+/*               printf("ll wp=%d lp=%x, ts=%d, cc=%d, ek=%d\n", wParamMsg, 
lParamMsg, transition_state, context_code, extended_key); */
+/*               fflush(stdout); */
+/*             } */
+/*           } */
+          switch (pkbdllhook->vkCode)
+            {
+              /* Since Vw32_pass_lwindow_to_system is just a struct
+                 and NILP a comparision with == we do not have to sync
+                 to make this comparision in the windows thread.  The
+                 value might temporarily be wrong, but no crashes can
+                 occor.  (But what about cashing of the value in the
+                 windows thread?) */
+            case VK_LWIN:
+              {
+                bHandle = NILP(Vw32_pass_lwindow_to_system);
+                break;
+              }
+            case VK_RWIN:
+              {
+                bHandle = NILP(Vw32_pass_rwindow_to_system);
+                break;
+              }
+            case VK_CAPITAL:
+              {
+                bHandle = NILP(Vw32_enable_caps_lock);
+                break;
+              }
+            case VK_NUMLOCK:
+              {
+                bHandle = NILP(Vw32_enable_num_lock);
+                break;
+              }
+            }
+        }
+      if (bHandle) {
+        // Just to be sure check we are in the right thread
+        if (GetCurrentThreadId() == dwWindowsThreadId) {
+          // Do we have keyboard focus?
+          HWND hwnd = GetFocus();
+          if (0 != hwnd) {
+            //printf("ll SendMessage(hwnd, msg, %x, %x)\n", wParamMsg, 
lParamMsg); fflush(stdout);
+            SendMessage (hwnd, msg, wParamMsg, lParamMsg);
+            return TRUE;
+          }
+        }
+      }
+
+
+
+    }
+  HHOOK hhook; return CallNextHookEx(hhook, nCode, wParam, lParam);
+}
+
+static HHOOK hLowKBhookW = NULL;
+
+BOOL
+add_LowLevelKeyboardProc()
+{
+  if (NULL != hLowKBhookW) return TRUE;
+  if (GetCurrentThreadId() == dwWindowsThreadId) {
+    hLowKBhookW = SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC) 
LowLevelKeyboardProc, (HINSTANCE) GetModuleHandle(NULL), 0);
+    if (NULL == hLowKBhookW) {
+      printf("SetWindowsHookEx error: %s\n", w32_strerror(GetLastError())); 
fflush(stdout);
+      return FALSE;
+    }
+  } else {
+    printf("add_LowLevelKeyboardProc called from other thread then windows 
thread\n"); fflush(stdout);
+    abort();
+  }
+  return TRUE;
+}
+BOOL
+remove_LowLevelKeyboardProc()
+{
+  if (NULL == hLowKBhookW) return TRUE;
+  if (!UnhookWindowsHookEx(hLowKBhookW)) {
+    printf("UnhookWindowsHookEx error: %s\n", w32_strerror(GetLastError())); 
fflush(stdout);
+  }
+  hLowKBhookW = NULL;
+}
+
+
 DWORD WINAPI
 w32_msg_worker (void *arg)
 {
@@ -2707,8 +2960,33 @@

   /* This is the inital message loop which should only exit when the
      application quits.  */
+
+  /* Below is some test code if someone would like to use it.  The
+     code is written after an example from MS but it does not work ;-)
+  */
+/*   cAccelerators = 1;      // number of accelerators in table */
+/*   lpaccelDyn = (LPACCEL) LocalAlloc(LPTR, cAccelerators * sizeof(ACCEL)); */
+/*   if (!lpaccelDyn) {  */
+/*     printf("LocalAlloc error: %s\n", w32_strerror(GetLastError())); 
fflush(stdout); */
+/*   } else { */
+/*     printf("LocalAlloc ok: %s\n", w32_strerror(GetLastError())); 
fflush(stdout); */
+/*   } */
+/*   lpaccelDyn[0].fVirt = FVIRTKEY; */
+/*   lpaccelDyn[0].key   = VK_F4; */
+/*   lpaccelDyn[0].cmd   = 0; //SC_CLOSE; */
+/*   hAccelDyn = CreateAcceleratorTable(lpaccelDyn, cAccelerators); */
+/*   if (!hAccelDyn) { */
+/*     printf("CreateAcceleratorTable error: %s\n", 
w32_strerror(GetLastError())); fflush(stdout); */
+/*   } else { */
+/*     printf("CreateAcceleratorTable ok: %s\n", 
w32_strerror(GetLastError())); fflush(stdout); */
+/*   } */
+
   w32_msg_pump (&dummy_buf);

+  if (lpaccelDyn) {
+    LocalFree(lpaccelDyn);
+  }
+
   return 0;
 }

@@ -2731,17 +3009,25 @@


 static void
-post_character_message (hwnd, msg, wParam, lParam, modifiers)
+post_character_message (hwnd, msg, wParam, lParam, dwmodifiers)
      HWND hwnd;
      UINT msg;
      WPARAM wParam;
      LPARAM lParam;
-     DWORD  modifiers;
+     DWORD  dwmodifiers;

 {
   W32Msg wmsg;

-  wmsg.dwModifiers = modifiers;
+  //printf("post_char, msg=%d, wp=%x, lp=%x, mod=%x\n", msg, wParam, lParam, 
dwmodifiers); fflush(stdout);
+  // Clear LWIN here to be able to handle it for StickyKeys
+  modifiers[EMACS_LWIN] = modifiers[EMACS_RWIN] = 0;
+
+  // Never post alt_modifier here if system should have ALT
+  if (!NILP (Vw32_pass_alt_to_system)) {
+    dwmodifiers = (dwmodifiers | alt_modifier) ^ alt_modifier;
+  }
+  wmsg.dwModifiers = dwmodifiers;

   /* Detect quit_char and set quit-flag directly.  Note that we
      still need to post a message to ensure the main thread will be
@@ -2803,6 +3089,17 @@
      WPARAM wParam;
      LPARAM lParam;
 {
+  //printf("w32_wnd_proc(hwnd, %d, %x, %x)\n", msg, wParam, lParam); 
fflush(stdout);
+  BOOL bStickyKeysOn = FALSE;
+  STICKYKEYS stick;
+  /* Bit 24 should be "extended key" for key messages. However
+     it seems to have some other functions too when StickyKeys are
+     on. */
+  BOOL bit24  = FALSE;
+  /* Bit 29 is context code for key messages (Alt pressed or not) */
+  //BOOL bit29  = FALSE;
+  static BOOL b_was_menu_pending = FALSE;
+
   struct frame *f;
   struct w32_display_info *dpyinfo = &one_w32_display_info;
   W32Msg wmsg;
@@ -2942,13 +3239,136 @@
       PostMessage (hwnd, WM_KEYDOWN, HIWORD (lParam), 0);
       return (0);

-    case WM_KEYUP:
+    case WM_SYSCOMMAND:
+      //printf("WM_SYSCOMMAND, wpar=%x, lpar=%x\n", wParam, lParam); 
fflush(stdout);
+      goto dflt;
     case WM_SYSKEYUP:
+    case WM_KEYUP:
+/*       b_was_menu_pending = b_menu_pending; */
+/*           if (b_was_menu_pending) { */
+/*             HWND hwndIn = hwnd; */
+/*             UINT msgIn  = msg; */
+/*             WPARAM wParamIn = wParam; */
+/*             LPARAM lParamIn = lParam; */
+/*             //b_menu_pending = FALSE; */
+/*             printf("sending WM_SYSCOMMAND, hwnd=%d, menu=%d\n", hwnd, 
GetMenu(hwnd)); fflush(stdout); */
+/*             SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0); */
+/*             PostMessage (hwndIn, msgIn, wParamIn, lParamIn); */
+/*             return 0; */
+/*           } */
+
+      stick.cbSize = sizeof(STICKYKEYS);
+      if (!SystemParametersInfo(SPI_GETSTICKYKEYS, stick.cbSize, &stick, 0)) {
+        printf("error in spi: %s\n", w32_strerror(GetLastError)); 
fflush(stdout);
+      } else {
+        bStickyKeysOn = stick.dwFlags & SKF_STICKYKEYSON;
+      }
+      if (lParam >> 24 & 1)  bit24  = TRUE;
+/*       { */
+/*         BOOL vkmenu  = FALSE; */
+/*         BOOL vklmenu = FALSE; */
+/*         BOOL vkrmenu = FALSE; */
+/*         BOOL vklwin = FALSE; */
+/*         BOOL vkrwin = FALSE; */
+/*         BOOL vklwset = FALSE; */
+/*         BOOL vkrwset = FALSE; */
+/*         switch (wParam) { */
+/*         case VK_MENU: */
+/*           vkmenu = TRUE; */
+/*           break; */
+/*         case VK_RMENU: */
+/*           vkrmenu = TRUE; */
+/*           break; */
+/*         case VK_LMENU: */
+/*           vklmenu = TRUE; */
+/*           break; */
+/*         case VK_LWIN: */
+/*           vklwin = TRUE; */
+/*           break; */
+/*         case VK_RWIN: */
+/*           vkrwin = TRUE; */
+/*           break; */
+/*         } */
+/*         vklwset = modifier_set(VK_LWIN); */
+/*         vkrwset = modifier_set(VK_RWIN); */
+/*         //if (91 == wParam) */
+/*         //printf("WM_KUP, m=%d wp=%d lp=%x VK_M=%x VK_LM=%x VK_RM=%x 
VK_LW=%x 24=%x st=%d,%d\n", */
+/*         printf("  WM_KUP, m=%d wp=%d lp=%8x VK_LW=%x,%x VK_RW=%x,%x 24=%x 
st=%d,%d\n", */
+/*                //msg, wParam, lParam, vkmenu, vklmenu, vkrmenu, vklwin, */
+/*                msg, wParam, lParam, vklwin, vklwset, vkrwin, vkrwset, */
+/*                bit24,  */
+/*                GetKeyState(wParam), GetAsyncKeyState(wParam) */
+/*                ); fflush(stdout); */
+/*         if (wParam==8) { printf("\n"); fflush(stdout); } */
+/*       } */
+
+      /* If StickyKeys is on we need to record VK_LWIN and VK_RWIN
+         when the system sends the special key up message when
+         pressing next key. */
+      /* FIX-ME: Key state toggling works only with VK_LWIN!  The code
+         below tries to make the use of LWIN and RWIN work in Emacs
+         both when using StickyKeys and not.  When using StickyKeys
+         the keys should be "toggleable". Bit 24 seems to be useful
+         for toggling LWIN, but not for RWIN. For some reason the
+         messages sent in Windows are not the same for LWIN and RWIN.
+         I do not believe that the use of bit 24 this way is
+         documented so this part of the code might break.  In that
+         case the version without bit 24 could be used.  The only
+         thing that is lost is the possibility to toggle the state of
+         LWIN.  From a user perspective this can always be done by
+         pressing C-g since M-C-g is never bound (I hope!). */
+      //if (!((VK_LWIN == wParam || VK_RWIN ==wParam) && bStickyKeysOn))
+      if (!(((VK_LWIN == wParam && 0 == bit24) || VK_RWIN == wParam) && 
bStickyKeysOn))
       record_keyup (wParam, lParam);
       goto dflt;

-    case WM_KEYDOWN:
     case WM_SYSKEYDOWN:
+    case WM_KEYDOWN:
+      stick.cbSize = sizeof(STICKYKEYS);
+      if (!SystemParametersInfo(SPI_GETSTICKYKEYS, stick.cbSize, &stick, 0)) {
+        printf("error in spi: %s\n", w32_strerror(GetLastError)); 
fflush(stdout);
+      } else {
+        bStickyKeysOn = stick.dwFlags & SKF_STICKYKEYSON;
+      }
+      if (lParam >> 24 & 1)  bit24  = TRUE;
+
+/*       { */
+/*         BOOL vkmenu  = FALSE; */
+/*         BOOL vklmenu = FALSE; */
+/*         BOOL vkrmenu = FALSE; */
+/*         BOOL vklwin = FALSE; */
+/*         BOOL vkrwin = FALSE; */
+/*         BOOL vklwset = FALSE; */
+/*         BOOL vkrwset = FALSE; */
+/*         switch (wParam) { */
+/*         case VK_MENU: */
+/*           vkmenu = TRUE; */
+/*           break; */
+/*         case VK_RMENU: */
+/*           vkrmenu = TRUE; */
+/*           break; */
+/*         case VK_LMENU: */
+/*           vklmenu = TRUE; */
+/*           break; */
+/*         case VK_LWIN: */
+/*           vklwin = TRUE; */
+/*           break; */
+/*         case VK_RWIN: */
+/*           vkrwin = TRUE; */
+/*           break; */
+/*         } */
+/*         vklwset = modifier_set(VK_LWIN); */
+/*         vkrwset = modifier_set(VK_RWIN); */
+/*         if (lParam >> 24 & 1)  bit24  = TRUE; */
+/*         //if (91 == wParam) */
+/*         //printf("WM_DWN m=%d w=%d lp=%x VK_M=%x VK_LM=%x VK_RM=%x VK_LW=%x 
B24=%x 29=%d BS=%x, s=%d,%d sy=%d\n", */
+/*         printf("WM_DWN, m=%d wp=%d lp=%10x VK_LW=%x,%x VK_RW=%x,%x 24=%x 
st=%d,%d\n", */
+/*                msg, wParam, lParam, vklwin, vklwset, vkrwin, vkrwset, */
+/*                bit24,  */
+/*                GetKeyState(wParam), GetAsyncKeyState(wParam) */
+/*                ); fflush(stdout); */
+/*       } */
+
       /* Ignore keystrokes we fake ourself; see below.  */
       if (dpyinfo->faked_key == wParam)
        {
@@ -2957,7 +3377,7 @@
             they don't produce WM_CHAR messages).  This ensures that
             indicator lights are toggled promptly on Windows 9x, for
             example.  */
-         if (wParam < 256 && lispy_function_keys[wParam])
+          if (lispy_function_keys[wParam] != 0)
            {
              windows_translate = 1;
              goto translate;
@@ -2966,6 +3386,8 @@
        }

       /* Synchronize modifiers with current keystroke.  */
+      b_was_menu_pending = b_menu_pending;
+      b_menu_pending = FALSE;
       sync_modifiers ();
       record_keydown (wParam, lParam);
       wParam = map_keypad_keys (wParam, (lParam & 0x1000000L) != 0);
@@ -2975,11 +3397,13 @@
       switch (wParam)
        {
        case VK_LWIN:
+          if (NULL == hLowKBhookW) {
          if (NILP (Vw32_pass_lwindow_to_system))
            {
              /* Prevent system from acting on keyup (which opens the
                 Start menu if no other key was pressed) by simulating a
                 press of Space which we will ignore.  */
+                //printf("Sending 255 to prevent\n"); fflush(stdout);
              if (GetAsyncKeyState (wParam) & 1)
                {
                  if (NUMBERP (Vw32_phantom_key_code))
@@ -2990,10 +3414,13 @@
                  keybd_event (key, (BYTE) MapVirtualKey (key, 0), 0, 0);
                }
            }
-         if (!NILP (Vw32_lwindow_modifier))
-           return 0;
+          } else {
+            //printf("Avoided sending 255 to prevent\n"); fflush(stdout);
+          }
+          if (!NILP (Vw32_lwindow_modifier)) return 0;
          break;
        case VK_RWIN:
+          if (NULL == hLowKBhookW) {
          if (NILP (Vw32_pass_rwindow_to_system))
            {
              if (GetAsyncKeyState (wParam) & 1)
@@ -3006,8 +3433,8 @@
                  keybd_event (key, (BYTE) MapVirtualKey (key, 0), 0, 0);
                }
            }
-         if (!NILP (Vw32_rwindow_modifier))
-           return 0;
+          }
+          if (!NILP (Vw32_rwindow_modifier)) return 0;
          break;
        case VK_APPS:
          if (!NILP (Vw32_apps_modifier))
@@ -3018,6 +3445,11 @@
            /* Prevent DefWindowProc from activating the menu bar if an
                Alt key is pressed and released by itself.  */
            return 0;
+          /* By not testing for StickyKeysOn here we get the advantage
+             that holding down Alt and then typing a letter can open
+             the menu. This seems to be the way other apps behave on
+             w32. */
+          b_menu_pending = TRUE;
          windows_translate = 1;
          break;
        case VK_CAPITAL:
@@ -3078,8 +3510,26 @@
            wParam = VK_NUMLOCK;
          break;
        default:
+          if (b_was_menu_pending) {
+            /* FIX-ME: Where should this go? Should it go to the
+               WM_CHAR section where the translated WM_KEY... returns?
+               (Remember to move b_was_menu_pending too!) */
+            HWND hwndIn = hwnd;
+            UINT msgIn  = msg;
+            WPARAM wParamIn = wParam;
+            LPARAM lParamIn = lParam;
+            b_menu_pending = FALSE;
+
+            if(!(modifier_set (VK_LCONTROL) && modifier_set (VK_RMENU))) {
+              //printf("sending WM_SYSCOMMAND, hwnd=%d, menu=%d\n", hwnd, 
GetMenu(hwnd)); fflush(stdout);
+              SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, wParam);
+              return 0;
+            } else {
+              //printf("... no menu, was AltGr...\n"); fflush(stdout);
+            }
+          }
          /* If not defined as a function key, change it to a WM_CHAR message. 
*/
-         if (wParam > 255 || !lispy_function_keys[wParam])
+          if (lispy_function_keys[wParam] == 0)
            {
              DWORD modifiers = construct_console_modifiers ();

@@ -3441,6 +3891,9 @@
       goto dflt;

     case WM_INITMENU:
+      //printf("WM_INITMENU in w32fns.c, hwnd=%d, wParam=%d\n", hwnd, wParam); 
fflush(stdout);
+      //printf("resetting menu_pending 1\n"); fflush(stdout);
+      b_menu_pending = FALSE;
       button_state = 0;
       ReleaseCapture ();
       /* We must ensure menu bar is fully constructed and up to date
@@ -3478,9 +3931,25 @@
        if (find_deferred_msg (hwnd, msg) != NULL)
          abort ();

+        my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
+        {
+          MSG msg;
+          /* Use PeekMessage to avoid blocking infinitely. 2 sec max
+             (this an arbitrary choice). 40 ms is choosen to be as
+             long as possible but still short enough to be
+             "invisible". */
+          // FIX-ME: maybe check for C-g in the loop!
+          int i;
+          for (i = 1; i < 50; i++) {
+            if (PeekMessage (&msg, NULL, WM_EMACS_DONE, WM_EMACS_DONE, 
PM_REMOVE)) break;
+            Sleep(40);
+          }
+        }
         menubar_in_use = 1;

-       return send_deferred_msg (&msg_buf, hwnd, msg, wParam, lParam);
+        //return send_deferred_msg (&msg_buf, hwnd, msg, wParam, lParam);
+
+        return 0;
       }

     case WM_EXITMENULOOP:
@@ -3669,10 +4138,21 @@
       return 0;

     case WM_CLOSE:
+      //printf("w32_wnd_proc WM_CLOSE\n"); fflush(stdout);
       wmsg.dwModifiers = w32_get_modifiers ();
       my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
       return 0;

+    case WM_QUERYENDSESSION:
+      //case WM_ENDSESSION:
+      // I do not think this should happen but I am not sure.
+      {
+        W32Msg wmsg;
+        //printf("w32_wnd_proc: WM_QUERYENDSESSION\n"); fflush(stdout);
+        my_post_msg (&wmsg, 0, WM_EMACS_KILL, 0, 0);
+      }
+      return 0;
+
     case WM_WINDOWPOSCHANGING:
       /* Don't restrict the sizing of tip frames.  */
       if (hwnd == tip_window)
@@ -3726,15 +4206,17 @@
                   However for top/left sizing we will need to fix the X
                   and Y positions as well.  */

+
+                if (wp.showCmd != SW_SHOWMAXIMIZED
+                    && (lppos->flags & SWP_NOMOVE) == 0)
+                  {
+/*                     lppos->cx -= wdiff; */
+/*                     lppos->cy -= hdiff; */
                int cx_mintrack = GetSystemMetrics (SM_CXMINTRACK);
                int cy_mintrack = GetSystemMetrics (SM_CYMINTRACK);

                lppos->cx = max (lppos->cx - wdiff, cx_mintrack);
                lppos->cy = max (lppos->cy - hdiff, cy_mintrack);
-
-               if (wp.showCmd != SW_SHOWMAXIMIZED
-                   && (lppos->flags & SWP_NOMOVE) == 0)
-                 {
                    if (lppos->x != wr.left || lppos->y != wr.top)
                      {
                        lppos->x += wdiff;
@@ -3810,9 +4292,15 @@
     case WM_EMACS_SETWINDOWPOS:
       {
        WINDOWPOS * pos = (WINDOWPOS *) wParam;
+        if (IsZoomed(hwnd)) {
+          return SetWindowPos (hwnd, pos->hwndInsertAfter,
+                               pos->x, pos->y, pos->cx, pos->cy,
+                               pos->flags | SWP_NOMOVE | SWP_NOSIZE);
+        } else {
        return SetWindowPos (hwnd, pos->hwndInsertAfter,
                             pos->x, pos->y, pos->cx, pos->cy, pos->flags);
       }
+      }

     case WM_EMACS_DESTROYWINDOW:
       DragAcceptFiles ((HWND) wParam, FALSE);
@@ -8242,6 +8730,42 @@
   return HOTKEY (vk_code, w32_modifiers);
 }

+DEFUN ("w32-wh-keyboard-ll", Fw32_wh_keyboard_ll,
+       Sw32_wh_keyboard_ll, 1, 1, 0,
+       doc: /* Activate/deactivate Emacs low level keyboard hook.
+A low level Windows keyboard hook must be used to trap keys that other
+applications have registered as hot keys.  If ON is non-nil Emacs low
+level keyboard hook is activated, if ON is nil it is deactivated.
+
+The keys that can be trapped are the left and right windows
+keys. Beware that these are not present on all keyboards.  If
+`w32-pass-lwindow-to-system' and `w32-pass-rwindow-to-system' are nil
+these keys will not be sent to the application that has registered
+them as hot keys if Emacs low level keyboard hook as active.
+
+You should normally not call this function directly.  Use
+`w32-meta-style' instead.  */)
+  (on)
+     Lisp_Object on;
+{
+/*   if (EQ (Vwindow_system, intern ("w32"))) */
+/*   if (EQ (Vwindow_system_version, intern ("w32"))) */
+/*     { */
+      DWORD wp = 1;
+      MSG msg;
+      if (NILP(on)) wp = 0;
+      if (!PostThreadMessage (dwWindowsThreadId, WM_EMACS_KBD_LL, (WPARAM) wp, 
0))
+        abort();
+      GetMessage (&msg, NULL, WM_EMACS_DONE, WM_EMACS_DONE);
+      if (msg.wParam == 0) return Qnil;
+      return Qt;
+/*     } */
+/*   else */
+/*     { */
+/*       return Qnil; */
+/*     } */
+}
+
 DEFUN ("w32-register-hot-key", Fw32_register_hot_key,
        Sw32_register_hot_key, 1, 1, 0,
        doc: /* Register KEY as a hot-key combination.
@@ -9050,6 +9574,7 @@
   defsubr (&Sw32_load_color_file);
   defsubr (&Sw32_send_sys_command);
   defsubr (&Sw32_shell_execute);
+  defsubr (&Sw32_wh_keyboard_ll);
   defsubr (&Sw32_register_hot_key);
   defsubr (&Sw32_unregister_hot_key);
   defsubr (&Sw32_registered_hot_keys);
Index: src/w32menu.c
===================================================================
RCS file: /sources/emacs/emacs/src/w32menu.c,v
retrieving revision 1.94
diff -u -b -r1.94 w32menu.c
--- src/w32menu.c       17 Oct 2007 23:43:50 -0000      1.94
+++ src/w32menu.c       4 Jan 2008 23:56:04 -0000
@@ -2291,8 +2291,7 @@
           /* With UTF-8, & cannot be part of a multibyte character.  */
           for (p = out_string; *p; p++)
             {
-              if (*p == '&')
-                nlen++;
+              // if (*p == '&') nlen++;
             }
         }
       else
Index: src/w32term.h
===================================================================
RCS file: /sources/emacs/emacs/src/w32term.h,v
retrieving revision 1.75
diff -u -b -r1.75 w32term.h
--- src/w32term.h       30 Nov 2007 13:57:46 -0000      1.75
+++ src/w32term.h       5 Jan 2008 19:59:46 -0000
@@ -641,7 +641,8 @@
 #define WM_EMACS_SHOW_CARET            (WM_EMACS_START + 17)
 #define WM_EMACS_HIDE_CARET            (WM_EMACS_START + 18)
 #define WM_EMACS_SETCURSOR             (WM_EMACS_START + 19)
-#define WM_EMACS_END                   (WM_EMACS_START + 20)
+#define WM_EMACS_KBD_LL                (WM_EMACS_START + 20)
+#define WM_EMACS_END                   (WM_EMACS_START + 21)

 #define WND_FONTWIDTH_INDEX    (0)
 #define WND_LINEHEIGHT_INDEX   (4)
;;; w32-meta.el --- Handling of Alt and Windows keys

;; Copyright (C) 2005, 2006, 2007 by Lennart Borgman

;; Author:     Lennart Borgman <lennart DOT borgman DOT 073 AT student DOT lu 
DOT se>
;; Created: 2005-07-30
;; Version: 0.50
;; Last-Updated: Tue Oct 30 18:29:33 2007 (3600 +0100)
;; Keywords: w32 keyboard


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Commentary:
;;
;; This files requires that Emacs has the function
;; `w32-wh-keyboard-ll'. This is not yet part of Emacs but is included
;; in the patched Emacs available on EmacsW32 home page.
;;
;; This file makes it simple to setup the left and right windows keys
;; for use as meta and the Alt key for use by the menus.
;; For more info see `w32-meta-style'.
;;
;; Usage:
;;
;;    (require 'w32-meta)


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; History:
;;


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; This file is not part of Emacs
;;
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation; either version 2, or (at
;; your option) any later version.
;;
;; This program is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;; General Public License for more details.
;;
;; To find out more about the GNU General Public License you can visit
;; Free Software Foundation's website http://www.fsf.org/.  Or, write
;; to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
;; Floor, Boston, MA 02110-1301, USA.


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Code:


(defun w32-meta-set-w32-style (lwindow-meta rwindow-meta)
  "Setup Emacs to feel more familiar to an MS Windows user.
Alt will be used for the menus.  If LWINDOW-META is non-nil then
the left Windows keyboard key will not be passed to MS Windows.
Instead it will be used as Meta in Emacs.  The same applies to
RWINDOW-META.

You can set this through custom with the variable
`w32-meta-style'.  See also `w32-meta-set-emacs-style'.

This is an high level interface to `w32-wh-keyboard-ll',
`w32-pass-lwindow-to-system', `w32-pass-rwindow-to-system',
`w32-pass-alt-to-system' and `w32-alt-is-meta'.

The `w32-wh-keyboard-ll' is not called unless `window-system' is
'w32."
  (interactive (list (y-or-n-p "Use left Windows key (lwindow) as Meta? ")
                     (y-or-n-p "Use right Windows key (rwindow) as Meta? ")))
  (and (eq window-system 'w32)
       (or lwindow-meta
           rwindow-meta)
       (unless (w32-wh-keyboard-ll t)
         (error "w32-wh-keyboard-ll failed add")))
  (when lwindow-meta
    ;;(global-set-key [(lwindow)] 'ESC-prefix)
    (setq w32-lwindow-modifier 'meta)
    (setq w32-pass-lwindow-to-system nil))
  (when rwindow-meta
    ;;(global-set-key [(rwindow)] 'ESC-prefix)
    (setq w32-rwindow-modifier 'meta)
    (setq w32-pass-rwindow-to-system nil))
  (setq w32-pass-alt-to-system t)
  (setq w32-alt-is-meta nil))

(defun w32-meta-set-emacs-style ()
  "Setup Emacs to be more familiar to a non MS Windows user.
Alt will be used for Emacs Meta.  <lwindow> and <rwindow> will be
passed to MS Windows.

See also `w32-meta-set-w32-style'."
  (interactive)
  (when (eq window-system 'w32)
    (unless (w32-wh-keyboard-ll nil)
      (error "w32-wh-keyboard-ll failed remove")))
  (global-set-key [(lwindow)] 'ignore)
  (global-set-key [(rwindow)] 'ignore)
  (setq w32-pass-lwindow-to-system t)
  (setq w32-pass-rwindow-to-system t)
  (setq w32-lwindow-modifier nil)
  (setq w32-rwindow-modifier nil)
  (setq w32-pass-alt-to-system nil)
  (setq w32-alt-is-meta t))


(defcustom w32-meta-style 'emacs
  "Defines how Emacs treats Alt, left and right Windows keys.
For more info see function `w32-meta-set-w32-style'."
  :group 'w32
  :type '(choice
          (const :tag "Emacs default Alt handling+Windows keys default 
handling" emacs)
          (const :tag "MS Windows default Alt handling+left and right Windows 
key as Meta" w32-lr)
          (const :tag "MS Windows default Alt handling+left Windows key as 
Meta" w32-l)
          (const :tag "MS Windows default Alt handling+right Windows key as 
Meta" w32-r)
          (const :tag "MS Windows default Alt handling" w32))
  :set (lambda (symbol value)
         (unless (memq value '(emacs w32-lr w32-l w32-r w32))
           (error "Bad value to w32-meta-style"))
         (set-default symbol value)
         (cond
           ((eq value 'emacs) (w32-meta-set-emacs-style))
           ((eq value 'w32-lr) (w32-meta-set-w32-style t t))
           ((eq value 'w32-l)  (w32-meta-set-w32-style t nil))
           ((eq value 'w32-r)  (w32-meta-set-w32-style nil t))
           ((eq value 'w32)    (w32-meta-set-w32-style nil nil)))))


;;(w32-meta-set-w32-style t nil)

(provide 'w32-meta)

;;; w32-meta.el ends here

reply via email to

[Prev in Thread] Current Thread [Next in Thread]