Tk Source Code

View Ticket
Login
Ticket UUID: 1626ed65b879bd76db99ef6abbbe18b64bef95a5
Title: ⌘ ´ (command-acute) in Spanish keyboard → immediate crash (how many years so?)
Type: Bug Version: 8.6
Submitter: juanfal Created on: 2021-02-28 18:57:34
Subsystem: 83. Mac OS X Build Assigned To: marc_culler
Priority: 5 Medium Severity: Critical
Status: Closed Last Modified: 2021-03-02 20:09:40
Resolution: Fixed Closed By: jan.nijtmans
    Closed on: 2021-03-02 20:09:40
Description:
$ wish8.6 (just after entering, I pressed ⌘ ` )
% 2021-02-28 19:53:02.897 wish8.6[44867:5138805] -[TKMenu submenuAction:]: unrecognized selector sent to instance 0x7fdb57ccfe30
2021-02-28 19:53:02.903 wish8.6[44867:5138805] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[TKMenu submenuAction:]: unrecognized selector sent to instance 0x7fdb57ccfe30'
*** First throw call stack:
(
	0   CoreFoundation                      0x00007fff204bcd6f __exceptionPreprocess + 242
	1   libobjc.A.dylib                     0x00007fff201f5bbb objc_exception_throw + 48
	2   CoreFoundation                      0x00007fff2053f4c8 -[NSObject(NSObject) __retain_OA] + 0
	3   CoreFoundation                      0x00007fff20424dcd ___forwarding___ + 1448
	4   CoreFoundation                      0x00007fff20424798 _CF_forwarding_prep_0 + 120
	5   AppKit                              0x00007fff22d79067 -[NSApplication(NSResponder) sendAction:to:from:] + 288
	6   AppKit                              0x00007fff23020469 -[NSCarbonMenuImpl performMenuAction:withTarget:] + 144
	7   AppKit                              0x00007fff22e7afe9 -[NSMenu _performKeyEquivalentWithDelegate:] + 188
	8   AppKit                              0x00007fff22e7ab28 -[NSMenu performKeyEquivalent:] + 71
	9   AppKit                              0x00007fff232d1a8f routeKeyEquivalent + 534
	10  AppKit                              0x00007fff22cdffff -[NSApplication(NSEvent) sendEvent:] + 1147
	11  libtk8.6.dylib                      0x0000000108e9d740 -[TKApplication(TKNotify) sendEvent:] + 124
	12  libtk8.6.dylib                      0x0000000108e9df49 TkMacOSXEventsCheckProc + 397
	13  libtcl8.6.dylib                     0x00000001091caed0 Tcl_DoOneEvent + 302
	14  libtk8.6.dylib                      0x0000000108deebc5 Tk_MainLoop + 33
	15  libtk8.6.dylib                      0x0000000108dfb7d2 Tk_MainEx + 1624
	16  wish8.6                             0x0000000100b12e1c main + 47
	17  libdyld.dylib                       0x00007fff20365f3d start + 1
	18  ???                                 0x0000000000000001 0x0 + 1
)
libc++abi: terminating with uncaught exception of type NSException
Abort trap: 6
User Comments: jan.nijtmans added on 2021-03-02 20:09:40:

Great!

@aivarannamaa, I don't see any way for a workaround using the latest Tk release.... Sorry.


marc_culler (claiming to be Marc Culler) added on 2021-03-02 19:28:40:
Incidentally, it seems that the best way to input a backquote with a Spanish
keyboard is probably to type acute space.  That works, anyway.

marc_culler (claiming to be Marc Culler) added on 2021-03-02 19:19:03:
I think this is resolved now.  I changed the logic so that a dead key
which also has a Command modifier will not be processed by the text
input client.  This eliminated the crash I was seeing at exit.  But the
override of [NSMenu performKeyEquivalent] is still needed.  Having that
callback return NO when it receives a key event with no characters does
not interfere with performing the KeyEquivalent.  Apparently that happens
at some other level in the responder chain, without causing an abort, if
the callback returns NO.  In particular the ⌘` accelerator works correctly
with a Spanish keyboard.

So I merged the fix and will close this ticket now.

jan.nijtmans added on 2021-03-02 16:44:41:

Thanks, @marc, for the explanation. Makes sense! So the solution is not there yet. No problem for now.


marc_culler (claiming to be Marc Culler) added on 2021-03-02 15:55:10:
Actually, the exception in the logic should only be for the Command
modifier.  There are many cases where Option is used to create a key
that behaves the way that acute does on the Spanish keyboard.  The
compose key for a grave accent on a US keyboard is Option-e, for example.

marc_culler (claiming to be Marc Culler) added on 2021-03-02 15:10:30:
@jan: this explains everything, and means that this really is a Tk bug.
It is caused by a subtle aspect of the NSTextInputClient protocol which,
of course, is undocumented by Apple. I will have to rework the patch.

Here is the story.  The Spanish keyboard does not have a backquote key.
The substitute for a backquote is a standalone acute accent.  But the
acute accent key is intended only for use in composition sequences. To
generate a standalone acute accent with a Spanish keyboard, e.g. to type
the shell command:
  echo `ls`
the fake backquotes have to be generated with some sort of composition
sequence, such as acute - left arrow - right arrow.

However, Command-backquote is a built-in accelerator on macOS which cycles
through the open windows of the application.  With a Spanish keyboard Apple
substitutes Command-acute as the accelerator. The crash is happening because
the Command-acute key is initiating a composition sequence, meaning that
the text input system has created an incomplete key event, with no characters,
and cached it to be delivered later, when the next key is pressed.  It has
to wait for the next key so it can figure out which accented letter should
be used as the character for the key event.  But, even though the key event
is incomplete, it is still getting passed to [NSMenu performKeyEquivalent]
which recognizes that the key is an accelerator and passes the incomplete
event on to the menu system.

The acute key should not initiate a composition sequence if it has
modifiers.  In fact, no key with modifiers should ever initiate a composition
sequence. So I think the logic in tkMacOSXKeyEvent.c needs to be adjusted
so as to ensure that is the case.

aivarannamaa added on 2021-03-02 12:23:28:
I guess it will take time until this fix ends up in a release and more time until the release is included in official Python distributions.

Can you suggest a work-around for the end application? I tried capturing all key-presses to see if I can neutralize this situation with a "break", but the event never made to my event handler.

Is there anything else I could try? Some menu options? Or can I change something in the tcl files of Tcl/Tk distribution?

jan.nijtmans added on 2021-03-02 07:22:08:

Actually, it's not the backtick causing the crash it's the 'acute' accent '´' which is causing it. The US keyboard doesn't have that character. Therefore I changed the title of this ticket now.

I tested the fix, and it works fine. Well done! I agree this looks like an Apple bug, most likely already present for a long time.

GTG, just merge when you feel ready. I don't think it's possible to write a test-case for this.


marc_culler (claiming to be Marc Culler) added on 2021-03-01 18:02:31:
OK.  I pushed a fix to a bugfix branch.  I do not think this is a Tk bug.
It is also not caused by the Menu handling not being able to handle chars
larger than 127.  It appears to be an Apple bug, and therefore will never
be fixed.

I found that the callback method [NSMenu performKeyEquivalent] was being
passed a malformed NSEvent of type NSEventTypeKeyDown.  The event had no
characters, which causes an abort somewhere inside of Apple's menu code.
The method should return a boolean value indicating whether to call a menu
accelerator.  The workaround in the bugfix branch is to override the method
for the TKMenu class to make it return NO if the event that it receives has
no characters.  This change caused a crash at exit because the setMarkedText
method in the NSTextInputClient would get called for the TKContentView of
a toplevel which had been destroyed.  So I had to add a guard against that.

These bad events are not generated when pressing Command-backquote on a US
keyboard.  I don't know if there are other ways to generate them.  I also
don't know how long this bug has been around or which versions of macOS
are affected.

jan.nijtmans added on 2021-03-01 09:41:10:

I can also reproduce it, when switching the keyboard to Spanish layout. When debugging tkMacOSXKeyEvent.c, I see:

2021-03-01 10:11:52.044 tktest[4813:30012] -[TKApplication(0x7fb522f28ff0) tkProcessKeyEvent:] repeat=0 mods=100000 char=0 code=33 c=585162096 type=10
2021-03-01 10:11:52.044 tktest[4813:30012] keyDown: Begin compose sequence.
2021-03-01 10:11:52.044 tktest[4813:30012] setMarkedText '´' len =1 range 0 from 1
2021-03-01 10:11:52.044 tktest[4813:30012] insertText '´'	len = 1

So an XEvent with the '´' character (decimal 180) is created, looks OK to me so far.

The ⌘ character has as result that the next character is sent to the Menu. Apparently, the menu handling part cannot handle codes >= 128.

@marc, can you help here?


chrstphrchvz added on 2021-02-28 22:58:07:

I can reproduce this with core-8-6-branch on macOS 10.15 with Spanish keyboard layout:

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   libsystem_kernel.dylib        	0x00007fff7166733a __pthread_kill + 10
1   libsystem_pthread.dylib       	0x00007fff71727e60 pthread_kill + 430
2   libsystem_c.dylib             	0x00007fff715ee808 abort + 120
3   libc++abi.dylib               	0x00007fff6e855458 abort_message + 231
4   libc++abi.dylib               	0x00007fff6e8468bf demangling_terminate_handler() + 262
5   libobjc.A.dylib               	0x00007fff70381a57 _objc_terminate() + 96
6   libc++abi.dylib               	0x00007fff6e854887 std::__terminate(void (*)()) + 8
7   libc++abi.dylib               	0x00007fff6e857387 __cxa_rethrow + 99
8   libobjc.A.dylib               	0x00007fff7037fe1c objc_exception_rethrow + 37
9   com.apple.AppKit              	0x00007fff34b4ac37 -[NSCarbonMenuImpl performMenuAction:withTarget:] + 242
10  com.apple.AppKit              	0x00007fff349abf2d -[NSMenu _performKeyEquivalentWithDelegate:] + 188
11  com.apple.AppKit              	0x00007fff349aba62 -[NSMenu performKeyEquivalent:] + 71
12  com.apple.AppKit              	0x00007fff34de19ab routeKeyEquivalent + 521
13  com.apple.AppKit              	0x00007fff347fd87d -[NSApplication(NSEvent) sendEvent:] + 1161
14  libtk8.6.dylib                	0x0000000102462e5e -[TKApplication(TKNotify) sendEvent:] + 206 (tkMacOSXNotify.c:205)
15  libtk8.6.dylib                	0x00000001024639f8 TkMacOSXEventsCheckProc + 536 (tkMacOSXNotify.c:578)
16  libtcl8.6.dylib               	0x00000001026d9dcd Tcl_DoOneEvent + 493 (tclNotify.c:961)
17  libtk8.6.dylib                	0x000000010232ac59 Tk_MainLoop + 41 (tkEvent.c:2108)
18  libtk8.6.dylib                	0x0000000102342b18 Tk_MainEx + 2376 (tkMain.c:377)
19  wish8.6                       	0x00000001022ecdde main + 78 (tkAppInit.c:89)
20  libdyld.dylib                 	0x00007fff7151fcc9 start + 1