aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhilipp Stephani2016-03-30 19:22:56 +0200
committerPhilipp Stephani2018-02-04 20:44:45 +0100
commit8fbf28536ee1169f59206523e2af050916befbf6 (patch)
tree3bb3e08efc13ba21fbedb4bafe17b8023fb48ca7
parentd2630e456923d2bd70fdd59267fe6e3d8eeb69ca (diff)
downloademacs-8fbf28536ee1169f59206523e2af050916befbf6.tar.gz
emacs-8fbf28536ee1169f59206523e2af050916befbf6.zip
Fix handling of modifier keys on macOS
* src/nsterm.m (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. * etc/NEWS: Add NEWS entry.
-rw-r--r--etc/NEWS3
-rw-r--r--lisp/cus-start.el8
-rw-r--r--src/nsterm.m174
3 files changed, 63 insertions, 122 deletions
diff --git a/etc/NEWS b/etc/NEWS
index afd0fba5a19..8fed15af5b2 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -286,6 +286,9 @@ loading messages if requested, and protects against recursive loads.
286** Battery status is now supported in all Cygwin builds. 286** Battery status is now supported in all Cygwin builds.
287Previously it was supported only in the Cygwin-w32 build. 287Previously it was supported only in the Cygwin-w32 build.
288 288
289** Emacs now handles key combinations involving the macOS "command"
290and "option" modifier keys more correctly.
291
289 292
290---------------------------------------------------------------------- 293----------------------------------------------------------------------
291This file is part of GNU Emacs. 294This file is part of GNU Emacs.
diff --git a/lisp/cus-start.el b/lisp/cus-start.el
index 4529fa1ac92..9ba1e105a1b 100644
--- a/lisp/cus-start.el
+++ b/lisp/cus-start.el
@@ -413,6 +413,10 @@ Leaving \"Default\" unchecked is equivalent with specifying a default of
413 ;; msdos.c 413 ;; msdos.c
414 (dos-unsupported-char-glyph display integer) 414 (dos-unsupported-char-glyph display integer)
415 ;; nsterm.m 415 ;; nsterm.m
416 ;;
417 ;; FIXME: Why does ⌃ use nil instead of none? Also the
418 ;; description is confusing; setting it to nil disables ⌃
419 ;; entirely.
416 (ns-control-modifier 420 (ns-control-modifier
417 ns 421 ns
418 (choice (const :tag "No modifier" nil) 422 (choice (const :tag "No modifier" nil)
@@ -429,13 +433,13 @@ Leaving \"Default\" unchecked is equivalent with specifying a default of
429 (const super)) "24.1") 433 (const super)) "24.1")
430 (ns-command-modifier 434 (ns-command-modifier
431 ns 435 ns
432 (choice (const :tag "No modifier" nil) 436 (choice (const :tag "No modifier (work as layout switch)" none)
433 (const control) (const meta) 437 (const control) (const meta)
434 (const alt) (const hyper) 438 (const alt) (const hyper)
435 (const super)) "23.1") 439 (const super)) "23.1")
436 (ns-right-command-modifier 440 (ns-right-command-modifier
437 ns 441 ns
438 (choice (const :tag "No modifier (work as command)" none) 442 (choice (const :tag "No modifier (work as layout switch)" none)
439 (const :tag "Use the value of ns-command-modifier" 443 (const :tag "Use the value of ns-command-modifier"
440 left) 444 left)
441 (const control) (const meta) 445 (const control) (const meta)
diff --git a/src/nsterm.m b/src/nsterm.m
index 4b81ad2a6c9..b7f5a32c098 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)
37#include <time.h> 37#include <time.h>
38#include <signal.h> 38#include <signal.h>
39#include <unistd.h> 39#include <unistd.h>
40#include <stdbool.h>
40 41
41#include <c-ctype.h> 42#include <c-ctype.h>
42#include <c-strcase.h> 43#include <c-strcase.h>
@@ -5944,7 +5945,6 @@ not_in_argv (NSString *arg)
5944@end /* EmacsApp */ 5945@end /* EmacsApp */
5945 5946
5946 5947
5947
5948/* ========================================================================== 5948/* ==========================================================================
5949 5949
5950 EmacsView implementation 5950 EmacsView implementation
@@ -6050,7 +6050,6 @@ not_in_argv (NSString *arg)
6050 int code; 6050 int code;
6051 unsigned fnKeysym = 0; 6051 unsigned fnKeysym = 0;
6052 static NSMutableArray *nsEvArray; 6052 static NSMutableArray *nsEvArray;
6053 int left_is_none;
6054 unsigned int flags = [theEvent modifierFlags]; 6053 unsigned int flags = [theEvent modifierFlags];
6055 6054
6056 NSTRACE ("[EmacsView keyDown:]"); 6055 NSTRACE ("[EmacsView keyDown:]");
@@ -6092,10 +6091,8 @@ not_in_argv (NSString *arg)
6092 6091
6093 if (!processingCompose) 6092 if (!processingCompose)
6094 { 6093 {
6095 /* When using screen sharing, no left or right information is sent, 6094 /* FIXME: What should happen for key sequences with more than
6096 so use Left key in those cases. */ 6095 one character? */
6097 int is_left_key, is_right_key;
6098
6099 code = ([[theEvent charactersIgnoringModifiers] length] == 0) ? 6096 code = ([[theEvent charactersIgnoringModifiers] length] == 0) ?
6100 0 : [[theEvent charactersIgnoringModifiers] characterAtIndex: 0]; 6097 0 : [[theEvent charactersIgnoringModifiers] characterAtIndex: 0];
6101 6098
@@ -6142,131 +6139,47 @@ not_in_argv (NSString *arg)
6142 if (flags & NSEventModifierFlagShift) 6139 if (flags & NSEventModifierFlagShift)
6143 emacs_event->modifiers |= shift_modifier; 6140 emacs_event->modifiers |= shift_modifier;
6144 6141
6145 is_right_key = (flags & NSRightCommandKeyMask) == NSRightCommandKeyMask; 6142 /* The ⌘ and ⌥ modifiers can be either shift-like (for alternate
6146 is_left_key = (flags & NSLeftCommandKeyMask) == NSLeftCommandKeyMask 6143 character input) or control-like (as command prefix). If we
6147 || (! is_right_key && (flags & NSEventModifierFlagCommand) == NSEventModifierFlagCommand); 6144 have only shift-like modifiers, then we should use the
6148 6145 translated characters (returned by the characters method); if
6149 if (is_right_key) 6146 we have only control-like modifiers, then we should use the
6150 emacs_event->modifiers |= parse_solitary_modifier 6147 untranslated characters (returned by the
6151 (EQ (ns_right_command_modifier, Qleft) 6148 charactersIgnoringModifiers method). An annoyance happens if
6152 ? ns_command_modifier 6149 we have both shift-like and control-like modifiers because
6153 : ns_right_command_modifier); 6150 the NSEvent API doesn’t let us ignore only some modifiers.
6154 6151 Therefore we ignore all shift-like modifiers in that
6155 if (is_left_key) 6152 case. */
6156 { 6153
6157 emacs_event->modifiers |= parse_solitary_modifier 6154 /* EV_MODIFIERS2 uses parse_solitary_modifier on all known
6158 (ns_command_modifier); 6155 modifier keys, which returns 0 for shift-like modifiers.
6159 6156 Therefore its return value is the set of control-like
6160 /* if super (default), take input manager's word so things like 6157 modifiers. */
6161 dvorak / qwerty layout work */ 6158 unsigned int control_modifiers = EV_MODIFIERS2 (flags);
6162 if (EQ (ns_command_modifier, Qsuper) 6159 emacs_event->modifiers |= control_modifiers;
6163 && !fnKeysym 6160
6164 && [[theEvent characters] length] != 0) 6161 if (NS_KEYLOG)
6165 { 6162 fprintf (stderr, "keyDown: code =%x\tfnKey =%x\tflags = %x\tmods = %x\n",
6166 /* XXX: the code we get will be unshifted, so if we have 6163 code, fnKeysym, flags, emacs_event->modifiers);
6167 a shift modifier, must convert ourselves */ 6164
6168 if (!(flags & NSEventModifierFlagShift)) 6165 /* If it was a function key or had control-like modifiers, pass
6169 code = [[theEvent characters] characterAtIndex: 0]; 6166 it directly to Emacs. */
6170#if 0
6171 /* this is ugly and also requires linking w/Carbon framework
6172 (for LMGetKbdType) so for now leave this rare (?) case
6173 undealt with.. in future look into CGEvent methods */
6174 else
6175 {
6176 long smv = GetScriptManagerVariable (smKeyScript);
6177 Handle uchrHandle = GetResource
6178 ('uchr', GetScriptVariable (smv, smScriptKeys));
6179 UInt32 dummy = 0;
6180 UCKeyTranslate ((UCKeyboardLayout *) *uchrHandle,
6181 [[theEvent characters] characterAtIndex: 0],
6182 kUCKeyActionDisplay,
6183 (flags & ~NSEventModifierFlagCommand) >> 8,
6184 LMGetKbdType (), kUCKeyTranslateNoDeadKeysMask,
6185 &dummy, 1, &dummy, &code);
6186 code &= 0xFF;
6187 }
6188#endif
6189 }
6190 }
6191
6192 is_right_key = (flags & NSRightControlKeyMask) == NSRightControlKeyMask;
6193 is_left_key = (flags & NSLeftControlKeyMask) == NSLeftControlKeyMask
6194 || (! is_right_key && (flags & NSEventModifierFlagControl) == NSEventModifierFlagControl);
6195
6196 if (is_right_key)
6197 emacs_event->modifiers |= parse_solitary_modifier
6198 (EQ (ns_right_control_modifier, Qleft)
6199 ? ns_control_modifier
6200 : ns_right_control_modifier);
6201
6202 if (is_left_key)
6203 emacs_event->modifiers |= parse_solitary_modifier
6204 (ns_control_modifier);
6205
6206 if (flags & NS_FUNCTION_KEY_MASK && !fnKeysym)
6207 emacs_event->modifiers |=
6208 parse_solitary_modifier (ns_function_modifier);
6209
6210 left_is_none = NILP (ns_alternate_modifier)
6211 || EQ (ns_alternate_modifier, Qnone);
6212
6213 is_right_key = (flags & NSRightAlternateKeyMask)
6214 == NSRightAlternateKeyMask;
6215 is_left_key = (flags & NSLeftAlternateKeyMask) == NSLeftAlternateKeyMask
6216 || (! is_right_key
6217 && (flags & NSEventModifierFlagOption) == NSEventModifierFlagOption);
6218
6219 if (is_right_key)
6220 {
6221 if ((NILP (ns_right_alternate_modifier)
6222 || EQ (ns_right_alternate_modifier, Qnone)
6223 || (EQ (ns_right_alternate_modifier, Qleft) && left_is_none))
6224 && !fnKeysym)
6225 { /* accept pre-interp alt comb */
6226 if ([[theEvent characters] length] > 0)
6227 code = [[theEvent characters] characterAtIndex: 0];
6228 /*HACK: clear lone shift modifier to stop next if from firing */
6229 if (emacs_event->modifiers == shift_modifier)
6230 emacs_event->modifiers = 0;
6231 }
6232 else
6233 emacs_event->modifiers |= parse_solitary_modifier
6234 (EQ (ns_right_alternate_modifier, Qleft)
6235 ? ns_alternate_modifier
6236 : ns_right_alternate_modifier);
6237 }
6238
6239 if (is_left_key) /* default = meta */
6240 {
6241 if (left_is_none && !fnKeysym)
6242 { /* accept pre-interp alt comb */
6243 if ([[theEvent characters] length] > 0)
6244 code = [[theEvent characters] characterAtIndex: 0];
6245 /*HACK: clear lone shift modifier to stop next if from firing */
6246 if (emacs_event->modifiers == shift_modifier)
6247 emacs_event->modifiers = 0;
6248 }
6249 else
6250 emacs_event->modifiers |=
6251 parse_solitary_modifier (ns_alternate_modifier);
6252 }
6253
6254 if (NS_KEYLOG)
6255 fprintf (stderr, "keyDown: code =%x\tfnKey =%x\tflags = %x\tmods = %x\n",
6256 (unsigned) code, fnKeysym, flags, emacs_event->modifiers);
6257
6258 /* if it was a function key or had modifiers, pass it directly to emacs */
6259 if (fnKeysym || (emacs_event->modifiers 6167 if (fnKeysym || (emacs_event->modifiers
6260 && (emacs_event->modifiers != shift_modifier) 6168 && (emacs_event->modifiers != shift_modifier)
6261 && [[theEvent charactersIgnoringModifiers] length] > 0)) 6169 && [[theEvent charactersIgnoringModifiers] length] > 0))
6262/*[[theEvent characters] length] */ 6170/*[[theEvent characters] length] */
6263 { 6171 {
6264 emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT; 6172 emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT;
6173 /* FIXME: What are the next four lines supposed to do? */
6265 if (code < 0x20) 6174 if (code < 0x20)
6266 code |= (1<<28)|(3<<16); 6175 code |= (1<<28)|(3<<16);
6267 else if (code == 0x7f) 6176 else if (code == 0x7f)
6268 code |= (1<<28)|(3<<16); 6177 code |= (1<<28)|(3<<16);
6269 else if (!fnKeysym) 6178 else if (!fnKeysym)
6179 /* FIXME: This seems wrong, characters in the range
6180 [0x80, 0xFF] are not ASCII characters. Can’t we just
6181 use MULTIBYTE_CHAR_KEYSTROKE_EVENT here for all kinds
6182 of characters? */
6270 emacs_event->kind = code > 0xFF 6183 emacs_event->kind = code > 0xFF
6271 ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT; 6184 ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT;
6272 6185
@@ -6277,11 +6190,32 @@ not_in_argv (NSString *arg)
6277 } 6190 }
6278 } 6191 }
6279 6192
6193 /* If we get here, a non-function key without control-like modifiers
6194 was hit. Use interpretKeyEvents, which in turn will call
6195 insertText; see
6196 https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/EventOverview/HandlingKeyEvents/HandlingKeyEvents.html. */
6280 6197
6281 if (NS_KEYLOG && !processingCompose) 6198 if (NS_KEYLOG && !processingCompose)
6282 fprintf (stderr, "keyDown: Begin compose sequence.\n"); 6199 fprintf (stderr, "keyDown: Begin compose sequence.\n");
6283 6200
6201 /* FIXME: interpretKeyEvents doesn’t seem to send insertText if ⌘ is
6202 used as shift-like modifier, at least on El Capitan. Mask it
6203 out. This shouldn’t be needed though; we should figure out what
6204 the correct way of handling ⌘ is. */
6205 if ([theEvent modifierFlags] & NSEventModifierFlagCommand)
6206 theEvent = [NSEvent keyEventWithType:[theEvent type]
6207 location:[theEvent locationInWindow]
6208 modifierFlags:[theEvent modifierFlags] & ~NSEventModifierFlagCommand
6209 timestamp:[theEvent timestamp]
6210 windowNumber:[theEvent windowNumber]
6211 context:nil
6212 characters:[theEvent characters]
6213 charactersIgnoringModifiers:[theEvent charactersIgnoringModifiers]
6214 isARepeat:[theEvent isARepeat]
6215 keyCode:[theEvent keyCode]];
6216
6284 processingCompose = YES; 6217 processingCompose = YES;
6218 /* FIXME: Use [NSArray arrayWithObject:theEvent]? */
6285 [nsEvArray addObject: theEvent]; 6219 [nsEvArray addObject: theEvent];
6286 [self interpretKeyEvents: nsEvArray]; 6220 [self interpretKeyEvents: nsEvArray];
6287 [nsEvArray removeObject: theEvent]; 6221 [nsEvArray removeObject: theEvent];