From 04a084cebc7ad77a6d11839b524f0441f66f7562 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mattias=20Engdeg=C3=A5rd?= <mattiase@acm.org>
Date: Tue, 19 Nov 2019 20:54:49 +0100
Subject: [PATCH] Add ns-{left,right}-alternate-for-char-entry

Allow the left and right Option keys to be used for normal character
entry while having them generate Emacs modifiers for other events by
setting 'ns-left-alternate-for-char-entry' and
'ns-eight-alternate-for-char-entry', respectively, to non-nil.

The variables have the aliases
'ns-{left,right}-option-for-char-entry', following other variables.

* doc/emacs/macos.texi (Mac / GNUstep Basics): Document the variables.
* lisp/cus-start.el (standard): Allow customisation.
* src/nsterm.m (ev_modifiers_helper): Simplify.
(right_modifier): New helper function.
(EV_MODIFIERS2): Add arguments, use helper.
(EV_MODIFIERS): Adapt EV_MODIFIERS2 invocation.
(ns_get_shifted_character, [EmacsView keyDown:]): Use the new variables.
(syms_of_nsterm): Define the new variables.
* etc/NEWS: Mention the new variables.
* lisp/term/ns-win.el: Add variables aliases.
---
 doc/emacs/macos.texi |  9 +++++++
 etc/NEWS             |  6 +++++
 lisp/cus-start.el    |  2 ++
 lisp/term/ns-win.el  |  8 ++++++-
 src/nsterm.m         | 57 +++++++++++++++++++++++++++++++++++---------
 5 files changed, 70 insertions(+), 12 deletions(-)

diff --git a/doc/emacs/macos.texi b/doc/emacs/macos.texi
index d9920957ad..ae2b6c0774 100644
--- a/doc/emacs/macos.texi
+++ b/doc/emacs/macos.texi
@@ -61,6 +61,15 @@ Mac / GNUstep Basics
 ignore them, in which case you get the default behavior of macOS
 accentuation system from the right @key{Option} key.
 
+@vindex ns-left-alternate-for-char-entry
+@vindex ns-right-alternate-for-char-entry
+  To make the left and right @key{Option} keys retain their macOS
+behaviour for entering characters but work as Emacs modifiers with
+function keys and mouse actions, set the
+@code{ns-left-alternate-for-char-entry} and
+@code{ns-right-alternate-for-char-entry} variables, respectively, to a
+non-nil value.
+
   @kbd{S-mouse-1} adjusts the region to the click position,
 just like @kbd{mouse-3} (@code{mouse-save-then-kill}); it does not pop
 up a menu for changing the default face, as @kbd{S-mouse-1} normally
diff --git a/etc/NEWS b/etc/NEWS
index 9dd573bd97..4998e7047e 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -3229,6 +3229,12 @@ Previously it was supported only in the Cygwin-w32 build.
 ** Emacs now handles key combinations involving the macOS "command"
 and "option" modifier keys more correctly.
 
++++
+** New user options 'ns-{left,right}-option-for-char-entry'.
+Set to non-nil to allow the respective macOS "option" keys to be used
+for normal character entry while treating them as Emacs modifiers for
+function keys and mouse events.
+
 ** The special handling of 'frame-title-format' on NS where setting it
 to 't' would enable the macOS proxy icon has been replaced with a
 separate variable, 'ns-use-proxy-icon'.  'frame-title-format' will now
diff --git a/lisp/cus-start.el b/lisp/cus-start.el
index e4b6d8f2d6..81883bf1bd 100644
--- a/lisp/cus-start.el
+++ b/lisp/cus-start.el
@@ -470,6 +470,8 @@ minibuffer-prompt-properties--setter
 		      (const control) (const meta)
 		      (const alt) (const hyper)
 		      (const super)) "23.3")
+	     (ns-left-alternate-for-char-entry ns boolean "27.1")
+	     (ns-right-alternate-for-char-entry ns boolean "27.1")
 	     (ns-function-modifier
 	      ns
 	      (choice (const :tag "No modifier (work as function)" none)
diff --git a/lisp/term/ns-win.el b/lisp/term/ns-win.el
index 1841e679ac..432fa6040d 100644
--- a/lisp/term/ns-win.el
+++ b/lisp/term/ns-win.el
@@ -541,11 +541,17 @@ ns-alternate-modifier
 (defvar ns-right-alternate-modifier)
 (defvar ns-right-command-modifier)
 (defvar ns-right-control-modifier)
+(defvar ns-left-alternate-for-char-entry)
+(defvar ns-right-alternate-for-char-entry)
 
 ;; You say tomAYto, I say tomAHto..
 (with-no-warnings
   (defvaralias 'ns-option-modifier 'ns-alternate-modifier)
-  (defvaralias 'ns-right-option-modifier 'ns-right-alternate-modifier))
+  (defvaralias 'ns-right-option-modifier 'ns-right-alternate-modifier)
+  (defvaralias 'ns-left-option-for-char-entry
+               'ns-left-alternate-for-char-entry)
+  (defvaralias 'ns-right-option-for-char-entry
+               'ns-right-alternate-for-char-entry))
 
 (defun ns-do-hide-emacs ()
   (interactive)
diff --git a/src/nsterm.m b/src/nsterm.m
index e1d745e332..15cd79d97c 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -364,8 +364,7 @@ - (NSColor *)colorUsingDefaultColorSpace
   if (flags & either_mask)
     {
       BOOL left_key = (flags & left_mask) == left_mask;
-      BOOL right_key = (flags & right_mask) == right_mask
-        && ! EQ (right_modifier, Qleft);
+      BOOL right_key = (flags & right_mask) == right_mask;
 
       if (right_key)
         modifiers |= parse_solitary_modifier (right_modifier);
@@ -380,7 +379,14 @@ - (NSColor *)colorUsingDefaultColorSpace
   return modifiers;
 }
 
-#define EV_MODIFIERS2(flags)                                            \
+/* Effective right modifier, since RIGHT_MOD can be `left'.  */
+static Lisp_Object
+right_modifier (Lisp_Object left_mod, Lisp_Object right_mod)
+{
+  return EQ (right_mod, Qleft) ? left_mod : right_mod;
+}
+
+#define EV_MODIFIERS2(flags, left_alt, right_alt)                       \
   (((flags & NSEventModifierFlagHelp) ?                                 \
     hyper_modifier : 0)                                                 \
    | ((flags & NSEventModifierFlagShift) ?                              \
@@ -391,19 +397,24 @@ - (NSColor *)colorUsingDefaultColorSpace
                           NSRightControlKeyMask,                        \
                           NSEventModifierFlagControl,                   \
                           ns_control_modifier,                          \
-                          ns_right_control_modifier)                    \
+                          right_modifier (ns_control_modifier,          \
+                                          ns_right_control_modifier))   \
    | ev_modifiers_helper (flags, NSLeftCommandKeyMask,                  \
                           NSRightCommandKeyMask,                        \
                           NSEventModifierFlagCommand,                   \
                           ns_command_modifier,                          \
-                          ns_right_command_modifier)                    \
+                          right_modifier (ns_command_modifier,          \
+                                          ns_right_command_modifier))   \
    | ev_modifiers_helper (flags, NSLeftAlternateKeyMask,                \
                           NSRightAlternateKeyMask,                      \
                           NSEventModifierFlagOption,                    \
-                          ns_alternate_modifier,                        \
-                          ns_right_alternate_modifier))
+                          left_alt ? ns_alternate_modifier : Qnil,      \
+                          (right_alt                                    \
+                           ? right_modifier (ns_alternate_modifier,     \
+                                             ns_right_alternate_modifier) \
+                           : Qnil)))
 
-#define EV_MODIFIERS(e) EV_MODIFIERS2 ([e modifierFlags])
+#define EV_MODIFIERS(e) EV_MODIFIERS2 ([e modifierFlags], true, true)
 
 #define EV_UDMODIFIERS(e)                                      \
     ((([e type] == NSEventTypeLeftMouseDown) ? down_modifier : 0)       \
@@ -2630,13 +2641,15 @@ so some key presses (TAB) are swallowed by the system.  */
   NSTRACE ("ns_get_shifted_character");
 
   if ((flags & NSRightAlternateKeyMask) == NSRightAlternateKeyMask
-      && (EQ (ns_right_alternate_modifier, Qnone)
+      && (ns_right_alternate_for_char_entry
+          || EQ (ns_right_alternate_modifier, Qnone)
           || (EQ (ns_right_alternate_modifier, Qleft)
               && EQ (ns_alternate_modifier, Qnone))))
     modifiers |= rightOptionKey;
 
   if ((flags & NSLeftAlternateKeyMask) == NSLeftAlternateKeyMask
-      && EQ (ns_alternate_modifier, Qnone))
+      && (ns_left_alternate_for_char_entry
+          || EQ (ns_alternate_modifier, Qnone)))
     modifiers |= optionKey;
 
   if ((flags & NSRightCommandKeyMask) == NSRightCommandKeyMask
@@ -6287,7 +6300,9 @@ In that case we use UCKeyTranslate (ns_get_shifted_character)
          modifier keys, which returns 0 for shift-like modifiers.
          Therefore its return value is the set of control-like
          modifiers.  */
-      emacs_event->modifiers = EV_MODIFIERS2 (flags);
+      bool left_alt = fnKeysym || !ns_left_alternate_for_char_entry;
+      bool right_alt = fnKeysym || !ns_right_alternate_for_char_entry;
+      emacs_event->modifiers = EV_MODIFIERS2 (flags, left_alt, right_alt);
 
       /* Function keys (such as the F-keys, arrow keys, etc.) set
          modifiers as though the fn key has been pressed when it
@@ -9414,6 +9429,26 @@ Convert an X font name (XLFD) to an NS font name.
 at all, allowing it to be used at a lower level for accented character entry.");
   ns_right_alternate_modifier = Qleft;
 
+  DEFVAR_BOOL ("ns-left-alternate-for-char-entry",
+               ns_left_alternate_for_char_entry,
+               "Whether the left option or alternate key is used for character entry.\n\
+When non-nil, the left alternate / option key works as an Emacs modifier for\n\
+function keys and mouse actions but not with character-generating keys.\n\
+When nil, the key is interpreted by Emacs for all events.\n\
+In either case, `ns-alternate-modifier' describes what Emacs modifier\n\
+is being generated.");
+  ns_left_alternate_for_char_entry = false;
+
+  DEFVAR_BOOL ("ns-right-alternate-for-char-entry",
+               ns_right_alternate_for_char_entry,
+               "Whether the right option or alternate key is used for character entry.\n\
+When non-nil, the right alternate / option key works as an Emacs modifier for\n\
+function keys and mouse actions but not with character-generating keys.\n\
+When nil, the key is interpreted by Emacs for all events.\n\
+In either case, `ns-right-alternate-modifier' describes what Emacs modifier\n\
+is being generated.");
+  ns_right_alternate_for_char_entry = false;
+
   DEFVAR_LISP ("ns-command-modifier", ns_command_modifier,
                "This variable describes the behavior of the command key.\n\
 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
-- 
2.21.0 (Apple Git-122)

