unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Philipp Stephani <p.stephani2@gmail.com>
To: Eli Zaretskii <eliz@gnu.org>
Cc: adrian.b.robert@gmail.com, 19977@debbugs.gnu.org
Subject: bug#19977: 24.4; Incorrect translation of Super modifier with Ctrl or Meta on OS X
Date: Wed, 30 Mar 2016 17:35:30 +0000	[thread overview]
Message-ID: <CAArVCkSgLnrBUympeaX_eFTfgmMq+dw9HXG0kUuJV-RDU81DGw@mail.gmail.com> (raw)
In-Reply-To: <834mbowuxw.fsf@gnu.org>


[-- Attachment #1.1: Type: text/plain, Size: 1385 bytes --]

Eli Zaretskii <eliz@gnu.org> schrieb am Mi., 30. März 2016 um 04:39 Uhr:

> > From: Philipp Stephani <p.stephani2@gmail.com>
> > Date: Tue, 29 Mar 2016 20:07:55 +0000
> > Cc: Eli Zaretskii <eliz@gnu.org>, 19977@debbugs.gnu.org
> >
> > It seems that this behavior cannot be implemented without resorting to
> UCKeyTranslate. Therefore I'd
> > suggest to fall back to the next best option and ignore all shift-like
> modifiers if control-like modifiers are
> > present, similar to what we're doing with C-S on Unix terminals.
>
> I'm not sure what this means, but if it means something that worked
> before won't, please provide an option to get the old behavior back,
> just in case.
>
>
I've attached a patch that should keep the aforementioned input methods
working (by setting ns-command-modifier to none) and allow Command and
Option to be treated as either shift-like or control-like modifiers.
In my tests input now works as expected with the Dvorak - Querty and
similar input methods if ns-command-modifier is none. Also various key
combinations with Super work now if it's set to super.
One thing that might be unexpected is that e.g. Command-Control-A will be
interpreted as Control-A if ns-command-modifier is none, even if Command-A
would insert something other than A. It seems this is (undesirable)
behavior is actually already present at head.

[-- Attachment #1.2: Type: text/html, Size: 1899 bytes --]

[-- Attachment #2: 0001-Fix-handling-of-modifier-keys-on-OS-X.patch --]
[-- Type: application/octet-stream, Size: 13651 bytes --]

From 008e71c08cbc06ac2d3ee3b353fe363ba620f4fd Mon Sep 17 00:00:00 2001
From: Philipp Stephani <phst@google.com>
Date: Wed, 30 Mar 2016 19:22:56 +0200
Subject: [PATCH] Fix handling of modifier keys on OS X
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* src/nsterm.m (is_shift_modifier, has_shift_modifiers): New helper
functions.
(keyDown:): Distinguish between shift-like and control-like modifier
keys.  Allow treating ⌘ as shift-like modifier (e.g. for the
Gujarati – QUERTY input method, where ⌘ switches to QUERTY.)

* lisp/cus-start.el (standard): Change nil to none for
ns-command-modifier; update description.
---
 lisp/cus-start.el |   8 ++-
 src/nsterm.m      | 208 ++++++++++++++++++++++++------------------------------
 2 files changed, 98 insertions(+), 118 deletions(-)

diff --git a/lisp/cus-start.el b/lisp/cus-start.el
index 5be61ce..12269c1 100644
--- a/lisp/cus-start.el
+++ b/lisp/cus-start.el
@@ -389,6 +389,10 @@ minibuffer-prompt-properties--setter
 	     ;; msdos.c
 	     (dos-unsupported-char-glyph display integer)
 	     ;; nsterm.m
+             ;;
+             ;; FIXME: Why does ⌃ use nil instead of none?  Also the
+             ;; description is confusing; setting it to nil disables ⌃
+             ;; entirely.
 	     (ns-control-modifier
 	      ns
 	      (choice (const :tag "No modifier" nil)
@@ -405,13 +409,13 @@ minibuffer-prompt-properties--setter
 		      (const super)) "24.1")
 	     (ns-command-modifier
 	      ns
-	      (choice (const :tag "No modifier" nil)
+	      (choice (const :tag "No modifier (work as layout switch)" none)
 		      (const control) (const meta)
 		      (const alt) (const hyper)
 		      (const super)) "23.1")
 	     (ns-right-command-modifier
 	      ns
-	      (choice (const :tag "No modifier (work as command)" none)
+	      (choice (const :tag "No modifier (work as layout switch)" none)
 		      (const :tag "Use the value of ns-command-modifier"
 			     left)
 		      (const control) (const meta)
diff --git a/src/nsterm.m b/src/nsterm.m
index 4048ac4..ebccb27 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -37,6 +37,7 @@ GNUstep port and post-20 update by Adrian Robert (arobert@cogsci.ucsd.edu)
 #include <time.h>
 #include <signal.h>
 #include <unistd.h>
+#include <stdbool.h>
 
 #include <c-ctype.h>
 #include <c-strcase.h>
@@ -5670,6 +5671,43 @@ not_in_argv (NSString *arg)
 @end  /* EmacsApp */
 
 
+static bool
+is_shift_modifier (NSEventModifierFlags flags,
+                   NSEventModifierFlags generic_mask,
+                   NSEventModifierFlags left_mask,
+                   NSEventModifierFlags right_mask,
+                   Lisp_Object left_option,
+                   Lisp_Object right_option)
+{
+  bool is_right_key = (flags & right_mask) == right_mask;
+  bool is_left_key = (flags & left_mask) == left_mask
+    || (! is_right_key && (flags & generic_mask) == generic_mask);
+
+  unsigned int modifiers = 0;
+  if (is_right_key)
+    modifiers |= parse_solitary_modifier (EQ (right_option, Qleft)
+                                          ? left_option : right_option);
+  if (is_left_key)
+    modifiers |= parse_solitary_modifier (left_option);
+
+  /* parse_solitary_modifier returns 0 for a shift-like modifier.  */
+  return (is_left_key || is_right_key) && modifiers == 0;
+}
+
+static bool
+has_shift_modifiers (NSEventModifierFlags flags)
+{
+  /* Check only Command, Option, and Shift modifiers.  Control is
+     never a shift-like modifier key.  */
+  return is_shift_modifier (flags, NSCommandKeyMask,
+                            NSLeftCommandKeyMask, NSRightCommandKeyMask,
+                            ns_command_modifier, ns_right_command_modifier)
+    || is_shift_modifier (flags, NSAlternateKeyMask,
+                          NSLeftAlternateKeyMask, NSRightAlternateKeyMask,
+                          ns_alternate_modifier, ns_right_alternate_modifier)
+    || (flags & NSShiftKeyMask) == NSShiftKeyMask;
+}
+
 
 /* ==========================================================================
 
@@ -5812,10 +5850,8 @@ not_in_argv (NSString *arg)
 
   if (!processingCompose)
     {
-      /* When using screen sharing, no left or right information is sent,
-         so use Left key in those cases.  */
-      int is_left_key, is_right_key;
-
+      /* FIXME: What should happen for key sequences with more than
+         one character?  */
       code = ([[theEvent charactersIgnoringModifiers] length] == 0) ?
         0 : [[theEvent charactersIgnoringModifiers] characterAtIndex: 0];
 
@@ -5862,131 +5898,50 @@ not_in_argv (NSString *arg)
       if (flags & NSShiftKeyMask)
         emacs_event->modifiers |= shift_modifier;
 
-      is_right_key = (flags & NSRightCommandKeyMask) == NSRightCommandKeyMask;
-      is_left_key = (flags & NSLeftCommandKeyMask) == NSLeftCommandKeyMask
-        || (! is_right_key && (flags & NSCommandKeyMask) == NSCommandKeyMask);
+      /* The ⌘ and ⌥ modifiers can be either shift-like (for alternate
+         character input) or control-like (as command prefix).  If we
+         have only shift-like modifiers, then we should use the
+         translated characters (returned by the characters method); if
+         we have only control-like modifiers, then we should use the
+         untranslated characters (returned by the
+         charactersIgnoringModifiers method).  An annoyance happens if
+         we have both shift-like and control-like modifiers because
+         the NSEvent API doesn’t let us ignore only some modifiers.
+         Therefore we ignore all shift-like modifiers in that
+         case.  */
+
+      /* EV_MODIFIERS2 uses parse_solitary_modifier on all known
+         modifier keys, which returns 0 for shift-like modifiers.
+         Therefore its return value is the set of control-like
+         modifiers.  */
+      unsigned int control_modifiers = EV_MODIFIERS2 (flags);
+      bool shift_modifiers =
+        control_modifiers ? false : has_shift_modifiers (flags);
+
+      emacs_event->modifiers |= control_modifiers;
 
-      if (is_right_key)
-        emacs_event->modifiers |= parse_solitary_modifier
-          (EQ (ns_right_command_modifier, Qleft)
-           ? ns_command_modifier
-           : ns_right_command_modifier);
-
-      if (is_left_key)
-        {
-          emacs_event->modifiers |= parse_solitary_modifier
-            (ns_command_modifier);
-
-          /* if super (default), take input manager's word so things like
-             dvorak / qwerty layout work */
-          if (EQ (ns_command_modifier, Qsuper)
-              && !fnKeysym
-              && [[theEvent characters] length] != 0)
-            {
-              /* XXX: the code we get will be unshifted, so if we have
-                 a shift modifier, must convert ourselves */
-              if (!(flags & NSShiftKeyMask))
-                code = [[theEvent characters] characterAtIndex: 0];
-#if 0
-              /* this is ugly and also requires linking w/Carbon framework
-                 (for LMGetKbdType) so for now leave this rare (?) case
-                 undealt with.. in future look into CGEvent methods */
-              else
-                {
-                  long smv = GetScriptManagerVariable (smKeyScript);
-                  Handle uchrHandle = GetResource
-                    ('uchr', GetScriptVariable (smv, smScriptKeys));
-                  UInt32 dummy = 0;
-                  UCKeyTranslate ((UCKeyboardLayout*)*uchrHandle,
-                                 [[theEvent characters] characterAtIndex: 0],
-                                 kUCKeyActionDisplay,
-                                 (flags & ~NSCommandKeyMask) >> 8,
-                                 LMGetKbdType (), kUCKeyTranslateNoDeadKeysMask,
-                                 &dummy, 1, &dummy, &code);
-                  code &= 0xFF;
-                }
-#endif
-            }
-        }
-
-      is_right_key = (flags & NSRightControlKeyMask) == NSRightControlKeyMask;
-      is_left_key = (flags & NSLeftControlKeyMask) == NSLeftControlKeyMask
-        || (! is_right_key && (flags & NSControlKeyMask) == NSControlKeyMask);
-
-      if (is_right_key)
-          emacs_event->modifiers |= parse_solitary_modifier
-              (EQ (ns_right_control_modifier, Qleft)
-               ? ns_control_modifier
-               : ns_right_control_modifier);
-
-      if (is_left_key)
-        emacs_event->modifiers |= parse_solitary_modifier
-          (ns_control_modifier);
-
-      if (flags & NS_FUNCTION_KEY_MASK && !fnKeysym)
-          emacs_event->modifiers |=
-            parse_solitary_modifier (ns_function_modifier);
-
-      left_is_none = NILP (ns_alternate_modifier)
-        || EQ (ns_alternate_modifier, Qnone);
-
-      is_right_key = (flags & NSRightAlternateKeyMask)
-        == NSRightAlternateKeyMask;
-      is_left_key = (flags & NSLeftAlternateKeyMask) == NSLeftAlternateKeyMask
-        || (! is_right_key
-            && (flags & NSAlternateKeyMask) == NSAlternateKeyMask);
-
-      if (is_right_key)
-        {
-          if ((NILP (ns_right_alternate_modifier)
-               || EQ (ns_right_alternate_modifier, Qnone)
-               || (EQ (ns_right_alternate_modifier, Qleft) && left_is_none))
-              && !fnKeysym)
-            {   /* accept pre-interp alt comb */
-              if ([[theEvent characters] length] > 0)
-                code = [[theEvent characters] characterAtIndex: 0];
-              /*HACK: clear lone shift modifier to stop next if from firing */
-              if (emacs_event->modifiers == shift_modifier)
-                emacs_event->modifiers = 0;
-            }
-          else
-            emacs_event->modifiers |= parse_solitary_modifier
-              (EQ (ns_right_alternate_modifier, Qleft)
-               ? ns_alternate_modifier
-               : ns_right_alternate_modifier);
-        }
-
-      if (is_left_key) /* default = meta */
-        {
-          if (left_is_none && !fnKeysym)
-            {   /* accept pre-interp alt comb */
-              if ([[theEvent characters] length] > 0)
-                code = [[theEvent characters] characterAtIndex: 0];
-              /*HACK: clear lone shift modifier to stop next if from firing */
-              if (emacs_event->modifiers == shift_modifier)
-                emacs_event->modifiers = 0;
-            }
-          else
-              emacs_event->modifiers |=
-                parse_solitary_modifier (ns_alternate_modifier);
-        }
-
-  if (NS_KEYLOG)
-    fprintf (stderr, "keyDown: code =%x\tfnKey =%x\tflags = %x\tmods = %x\n",
-             code, fnKeysym, flags, emacs_event->modifiers);
+      if (NS_KEYLOG)
+        fprintf (stderr, "keyDown: code =%x\tfnKey =%x\tflags = %x\tmods = %x\n",
+                 code, fnKeysym, flags, emacs_event->modifiers);
 
-      /* if it was a function key or had modifiers, pass it directly to emacs */
+      /* If it was a function key or had control-like modifiers, pass
+         it directly to Emacs.  */
       if (fnKeysym || (emacs_event->modifiers
                        && (emacs_event->modifiers != shift_modifier)
                        && [[theEvent charactersIgnoringModifiers] length] > 0))
 /*[[theEvent characters] length] */
         {
           emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT;
+          /* FIXME: What are the next four lines supposed to do?  */
           if (code < 0x20)
             code |= (1<<28)|(3<<16);
           else if (code == 0x7f)
             code |= (1<<28)|(3<<16);
           else if (!fnKeysym)
+            /* FIXME: This seems wrong, characters in the range
+               [0x80, 0xFF] are not ASCII characters.  Can’t we just
+               use MULTIBYTE_CHAR_KEYSTROKE_EVENT here for all kinds
+               of characters?  */
             emacs_event->kind = code > 0xFF
               ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT;
 
@@ -5997,11 +5952,32 @@ not_in_argv (NSString *arg)
         }
     }
 
+  /* If we get here, a non-function key without control-like modifiers
+     was hit.  Use interpretKeyEvents, which in turn will call
+     insertText; see
+     https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/EventOverview/HandlingKeyEvents/HandlingKeyEvents.html.  */
 
   if (NS_KEYLOG && !processingCompose)
     fprintf (stderr, "keyDown: Begin compose sequence.\n");
 
+  /* FIXME: interpretKeyEvents doesn’t seem to send insertText if ⌘ is
+     used as shift-like modifier, at least on El Capitan.  Mask it
+     out.  This shouldn’t be needed though; we should figure out what
+     the correct way of handling ⌘ is.  */
+  if ([theEvent modifierFlags] & NSCommandKeyMask)
+    theEvent = [NSEvent keyEventWithType:[theEvent type]
+                                location:[theEvent locationInWindow]
+                           modifierFlags:[theEvent modifierFlags] & ~NSCommandKeyMask
+                               timestamp:[theEvent timestamp]
+                            windowNumber:[theEvent windowNumber]
+                                 context:[theEvent context]
+                              characters:[theEvent characters]
+                        charactersIgnoringModifiers:[theEvent charactersIgnoringModifiers]
+                               isARepeat:[theEvent isARepeat]
+                                 keyCode:[theEvent keyCode]];
+
   processingCompose = YES;
+  /* FIXME: Use [NSArray arrayWithObject:theEvent]?  */
   [nsEvArray addObject: theEvent];
   [self interpretKeyEvents: nsEvArray];
   [nsEvArray removeObject: theEvent];
-- 
2.7.4


  reply	other threads:[~2016-03-30 17:35 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-03-01 16:36 bug#19977: 24.4; Incorrect translation of Super modifier with Ctrl or Meta on OS X Philipp Stephani
2016-03-29 10:17 ` Philipp Stephani
2016-03-29 15:10   ` Eli Zaretskii
2016-03-29 15:29     ` Philipp Stephani
2016-03-29 15:45       ` Philipp Stephani
2016-03-29 15:59         ` Eli Zaretskii
2016-03-29 16:31           ` Philipp Stephani
2016-03-29 16:38             ` Philipp Stephani
2016-03-29 16:57               ` Eli Zaretskii
2016-03-29 17:19                 ` Adrian Robert
2016-03-29 17:44                   ` Philipp Stephani
2016-03-29 17:56                     ` Adrian Robert
2016-03-29 19:43                       ` Philipp Stephani
2016-03-29 20:07                         ` Philipp Stephani
2016-03-30  2:39                           ` Eli Zaretskii
2016-03-30 17:35                             ` Philipp Stephani [this message]
2017-12-26 17:42                               ` Alan Third
2017-12-26 20:14                                 ` Philipp Stephani
2017-12-26 21:16                                   ` Alan Third
2018-02-04 19:07                                     ` Philipp Stephani
2018-02-04 20:18                                       ` Alan Third
2018-02-05  8:02                                         ` Philipp Stephani

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAArVCkSgLnrBUympeaX_eFTfgmMq+dw9HXG0kUuJV-RDU81DGw@mail.gmail.com \
    --to=p.stephani2@gmail.com \
    --cc=19977@debbugs.gnu.org \
    --cc=adrian.b.robert@gmail.com \
    --cc=eliz@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).