kovidgoyal / kitty

Cross-platform, fast, feature-rich, GPU based terminal
https://sw.kovidgoyal.net/kitty/
GNU General Public License v3.0
22.38k stars 910 forks source link

Mac OS cmd+esc / shift+cmd+esc shortcuts to switch window stopped working in 0.24.0 #4501

Closed fgoepel closed 2 years ago

fgoepel commented 2 years ago

Describe the bug In 0.23.1 and below pressing cmd+esc/shift+cmd+esc would cycle between windows, as it does in all other Mac OS applications. This does not work anymore in 0.24.0/0.24.1.

It looks like 2b9408c217fb6e540aa950364a789362c7b6e571 is the culprit.

Environment details kitty 0.24.0 created by Kovid Goyal Darwin imac 18.7.0 Darwin Kernel Version 18.7.0: Tue Jun 22 19:37:08 PDT 2021; root:xnu-4903.278.70~1/RELEASE_X86_64 x86_64 ProductName: Mac OS X ProductVersion: 10.14.6 BuildVersion: 18G9323

page-down commented 2 years ago

Doesn't cmd+` switch windows in the same application under macOS?

fgoepel commented 2 years ago

It seems I customized that shortcut in the OS settings.

I've tried to see if I could change the kitty config to make this work e.g. like this:

map cmd+escape next_window
map shift+cmd+escape previous_window

But that doesn't seem to work.

kovidgoyal commented 2 years ago

works fine for me with:

1) kitty -o 'map cmd+esc next_window' 2) Press ctrl+shift+enter to create a new kitty window 3) press cmd+esc switches to the first window

Note that in kitty there are kitty windows and OS windows. An OS window can contian multiple kitty windows.

kovidgoyal commented 2 years ago

And just for completeness the change in behavior comes because cmd+esc is no longer passed to cocoa, in order to make it mappable inside kitty. These were IIRC changes by @page-down see is_modified_special_key()

fgoepel commented 2 years ago

I see. However, I want to switch between OS windows. How do I achieve this now?

Luflosi commented 2 years ago

Is it enough to unmap the keyboard shortcut in kitty by mapping it to no_op?

fgoepel commented 2 years ago

Would it be possible to add an option to disable the hijacking of these keys so that the OS level mappings can continue to work?

page-down commented 2 years ago

According to your description, I configured the key to switch between windows of the same application in macOS as cmd+esc.

At this point, the OS window cannot be switched in kitty. Such potential concerns were anticipated at the time of the discussion.

Mentioned in the comments:

// really one should use [[NSUserDefaults standardUserDefaults] valueForDefaultsDomain:@"com.apple.symbolichotkeys" key:@"AppleSymbolicHotKeys"] // to get the list of global shortcuts and pass through the important ones,

To solve it, we need to read all the keys that the user has configured globally in the system. Then leave those keys untouched. This is indeed too much work. I don't have a good idea yet.


I implemented nth_os_window in this version, and I was wondering if I needed to implement next_os_window, because there is this system feature, so I didn't do it.

Luflosi commented 2 years ago

Would it be possible to only hijack the keyboard shortcut if it is actually used in kitty?

fgoepel commented 2 years ago

To solve it, we need to read all the keys that the user has configured globally in the system. Then leave those keys untouched. This is indeed too much work. I don't have a good idea yet.

You could add a "macos_hijack_escape" option or something similar to control this maybe?

kovidgoyal commented 2 years ago

On Wed, Jan 12, 2022 at 08:44:08AM -0800, Luflosi wrote:

Would it be possible to only hijack the keyboard shortcut if it is actually used in kitty?

In theory, sure. Practically, it would be pretty painful given how keyboard handling works in cocoa.

The easiest solution for the OP is to implement next_os_window or to use the default cmd+` shortcut for this.

page-down commented 2 years ago

@shado23 For now, you can use the kitten below, although it is a bit different from the system's functionality.

~/.config/kitty/next_os_window.py

from kitty.fast_data_types import current_os_window, focus_os_window

def main(args):
    pass

from kittens.tui.handler import result_handler
@result_handler(no_ui=True)
def handle_result(args, answer, target_window_id, boss):
    l = list(boss.os_window_map.keys())
    osw_id = current_os_window()
    if osw_id in l:
        i = l.index(osw_id) + 1
        if i >= len(l):
            i = 0
        focus_os_window(l[i], True)
map cmd+esc kitten next_os_window.py
page-down commented 2 years ago

I tried the new changes and found that the keys are not repeatable, for example holding Backspace does not continuously delete text.

Pressing ctrl+d sometimes results in d being entered. It seems to be the same problem.

EDIT: It seems to be my problem. EDIT2: The problem does seem to exist.

kovidgoyal commented 2 years ago

I cannot replicate, can you provide more detail? I tried the steps:

1) run kitty --debug-input 2) hold down a key 3) a bunch of aaaa are present at the prompt 4) hold down delete key 5) the letters from 3 are deleted

page-down commented 2 years ago

kitty --config=NONE --debug-input cat

No input source (IME) activated.

NSEvent: type=KeyDown loc=(562,695.016) time=22221.3 flags=0x100 win=0x7f7b2a7e20e0 winNum=1602 ctxt=0x0 chars="a" unmodchars="a" repeat=0 keyCode=0
NSEvent: type=KeyDown loc=(562,695.016) time=22221.7 flags=0x100 win=0x7f7b2a7e20e0 winNum=1602 ctxt=0x0 chars="a" unmodchars="a" repeat=1 keyCode=0
NSEvent: type=KeyDown loc=(562,695.016) time=22221.7 flags=0x100 win=0x7f7b2a7e20e0 winNum=1602 ctxt=0x0 chars="a" unmodchars="a" repeat=1 keyCode=0
NSEvent: type=KeyDown loc=(562,695.016) time=22221.8 flags=0x100 win=0x7f7b2a7e20e0 winNum=1602 ctxt=0x0 chars="a" unmodchars="a" repeat=1 keyCode=0
NSEvent: type=KeyDown loc=(562,695.016) time=22221.8 flags=0x100 win=0x7f7b2a7e20e0 winNum=1602 ctxt=0x0 chars="a" unmodchars="a" repeat=1 keyCode=0
NSEvent: type=KeyDown loc=(562,695.016) time=22221.8 flags=0x100 win=0x7f7b2a7e20e0 winNum=1602 ctxt=0x0 chars="a" unmodchars="a" repeat=1 keyCode=0
NSEvent: type=KeyDown loc=(562,695.016) time=22221.9 flags=0x100 win=0x7f7b2a7e20e0 winNum=1602 ctxt=0x0 chars="a" unmodchars="a" repeat=1 keyCode=0
NSEvent: type=KeyDown loc=(562,695.016) time=22221.9 flags=0x100 win=0x7f7b2a7e20e0 winNum=1602 ctxt=0x0 chars="a" unmodchars="a" repeat=1 keyCode=0
NSEvent: type=KeyUp loc=(562,695.016) time=22221.9 flags=0x100 win=0x7f7b2a7e20e0 winNum=1602 ctxt=0x0 chars="a" unmodchars="a" repeat=0 keyCode=0
Press: native_key: 0x0 (a) glfw_key: 0x61 mods: none char_count: 1 deadKeyState: 0 repeat: 0 
    insertText: a replacementRange: (9223372036854775807, 0)
text: 0x61 glfw_key: a marked_text: ()
on_key_input: glfw key: 0x61 native_code: 0x0 action: PRESS mods: none text: 'a' state: 0 updateIMEPosition: left=2.000000, top=2.000000, width=7.000000, height=13.000000
sent key as text to child
on_key_input: glfw key: 0xe062 native_code: 0x3b action: PRESS mods: ctrl text: '' state: 0 updateIMEPosition: left=9.000000, top=2.000000, width=7.000000, height=13.000000
kovidgoyal commented 2 years ago

That log looks like there is no key window present, try generating it again with my latest commit to confirm.

page-down commented 2 years ago
---------------- key down -------------------
NSEvent: type=KeyDown loc=(1000,435.016) time=22776.1 flags=0x100 win=0x7f7bbf491e60 winNum=1659 ctxt=0x0 chars="a" unmodchars="a" repeat=0 keyCode=0
---------------- key down -------------------
NSEvent: type=KeyDown loc=(1000,435.016) time=22776.5 flags=0x100 win=0x7f7bbf491e60 winNum=1659 ctxt=0x0 chars="a" unmodchars="a" repeat=1 keyCode=0
---------------- key down -------------------
NSEvent: type=KeyDown loc=(1000,435.016) time=22776.6 flags=0x100 win=0x7f7bbf491e60 winNum=1659 ctxt=0x0 chars="a" unmodchars="a" repeat=1 keyCode=0
---------------- key down -------------------
NSEvent: type=KeyDown loc=(1000,435.016) time=22776.6 flags=0x100 win=0x7f7bbf491e60 winNum=1659 ctxt=0x0 chars="a" unmodchars="a" repeat=1 keyCode=0
---------------- key down -------------------
NSEvent: type=KeyDown loc=(1000,435.016) time=22776.6 flags=0x100 win=0x7f7bbf491e60 winNum=1659 ctxt=0x0 chars="a" unmodchars="a" repeat=1 keyCode=0
---------------- key down -------------------
NSEvent: type=KeyDown loc=(1000,435.016) time=22776.7 flags=0x100 win=0x7f7bbf491e60 winNum=1659 ctxt=0x0 chars="a" unmodchars="a" repeat=1 keyCode=0
----------------- key up --------------------
NSEvent: type=KeyUp loc=(1000,435.016) time=22776.7 flags=0x100 win=0x7f7bbf491e60 winNum=1659 ctxt=0x0 chars="a" unmodchars="a" repeat=0 keyCode=0
Release: native_key: 0x0 (a) glfw_key: 0x61 mods: none 
on_key_input: glfw key: 0x61 native_code: 0x0 action: RELEASE mods: none text: '' state: 0 updateIMEPosition: left=2.000000, top=2.000000, width=7.000000, height=13.000000
ignoring as keyboard mode does not support encoding this event
on_key_input: glfw key: 0xe062 native_code: 0x3b action: PRESS mods: ctrl text: '' state: 0 updateIMEPosition: left=2.000000, top=2.000000, width=7.000000, height=13.000000
ignoring as keyboard mode does not support encoding this event
---------------- key down -------------------
NSEvent: type=KeyDown loc=(1000,435.016) time=22779.8 flags=0x40101 win=0x7f7bbf491e60 winNum=1659 ctxt=0x0 chars="" unmodchars="c" repeat=0 keyCode=8
----------------- key up --------------------
NSEvent: type=KeyUp loc=(1000,435.016) time=22779.9 flags=0x40101 win=0x7f7bbf491e60 winNum=1659 ctxt=0x0 chars="" unmodchars="c" repeat=0 keyCode=8
Release: native_key: 0x8 (c) glfw_key: 0x63 mods: ctrl

Hmm... ctrl+c does not work now.

kovidgoyal commented 2 years ago

I dont see how that log is possible, do you have this commit: https://github.com/kovidgoyal/kitty/commit/682eb7d80249f43cf7981cf5a780c4bfd2f60308

page-down commented 2 years ago

Yes. 682eb7d80249f43cf7981cf5a780c4bfd2f60308.

I don't think I have any other software that would affect this.

kovidgoyal commented 2 years ago

then modify the key_down block to print that it is indeed calling contentWindow keyDown and then add some prints in keyDown in cocoa_window.m to confirm that it is being called and where it is returning.

page-down commented 2 years ago
NSEvent* (^keydown_block)(NSEvent*) = ^ NSEvent* (NSEvent* event) {
...
// now check if there is a useful apple shortcut
int global_shortcut = is_active_apple_global_shortcut(event);
// <--- Never reached here.
is_active_apple_global_shortcut(NSEvent *event) {
if (global_shortcuts == nil) build_global_shortcuts_lookup();
// <--- Never reached here.
build_global_shortcuts_lookup(void) {
...
// <--- Never reached here.
global_shortcuts = [[NSDictionary dictionaryWithDictionary:temp] retain];  

It looks like ended up inside here. [symbolic_hotkeys enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop)

kovidgoyal commented 2 years ago

weird how is that even possible. Try an asan build is there some memmory corruption happeing?

page-down commented 2 years ago

make asan

    debug_key("---> before global_shortcuts = ...\n");
    global_shortcuts = [[NSDictionary dictionaryWithDictionary:temp] retain];
    ...
    debug_key("---> end build_global_shortcuts_lookup\n");
static int
is_active_apple_global_shortcut(NSEvent *event) {
    if (global_shortcuts == nil) build_global_shortcuts_lookup();
    debug_key("---> after build_global_shortcuts_lookup\n");
        debug_key("-----> before is_active_apple_global_shortcut\n");
        // now check if there is a useful apple shortcut
        int global_shortcut = is_active_apple_global_shortcut(event);
        debug_key("-----> after is_active_apple_global_shortcut\n");
---------------- key down -------------------
NSEvent: type=KeyDown loc=(1318,189.016) time=25543.7 flags=0x100 win=0x61400003f240 winNum=2196 ctxt=0x0 chars="a" unmodchars="a" repeat=0 keyCode=0
-----> before is_active_apple_global_shortcut
----------------- key up --------------------
NSEvent: type=KeyUp loc=(1318,189.016) time=25543.7 flags=0x100 win=0x61400003f240 winNum=2196 ctxt=0x0 chars="a" unmodchars="a" repeat=0 keyCode=0
Release: native_key: 0x0 (a) glfw_key: 0x61 mods: none
kovidgoyal commented 2 years ago

I changed it to use a for loop instead fo block for enumeration, see if that helps.

page-down commented 2 years ago

It does not appear to be due to the "block" problem.

After I caught the exception:

// key=164
-[__NSCFArray objectAtIndexedSubscript:]: index (2) beyond bounds (2)

    0   CoreFoundation                      0x00007ff806020f0b __exceptionPreprocess + 242
    1   libobjc.A.dylib                     0x00007ff805d81b9d objc_exception_throw + 48
    2   CoreFoundation                      0x00007ff8060dc434 _CFThrowFormattedException + 202
    3   CoreFoundation                      0x00007ff8060dd763 _NSArrayRaiseInsertNilException + 0
    4   CoreFoundation                      0x00007ff805fc76d1 -[__NSCFArray objectAtIndexedSubscript:] + 154
    5   glfw-cocoa.so                       0x000000010215c360 ___glfwPlatformInit_block_invoke + 1280

At this point I can run it. I'll try some more key combinations and hope there are no more potential misses.

kovidgoyal commented 2 years ago

I have added code to validate the parameters array

page-down commented 2 years ago

I think I can update these definitions when I have time, but there are two right now that maybe need more work.

Maybe we can also check the type, I see that all the types are standard except 164.

164 = {
    ...
        type = modifier;
<key>type</key>
<string>standard</string>

I tried the following steps and could not switch OS windows, is it normal on your system?

---------------- key down -------------------
NSEvent: type=KeyDown loc=(963,695.016) time=33498.8 flags=0x100108 win=0x7f84ca148020 winNum=2805 ctxt=0x0 chars=" unmodchars=" repeat=0 keyCode=53
Press: native_key: 0x35 (<cc>) glfw_key: 0xe000 mods: super char_count: 1 deadKeyState: 0 repeat: 0
    doCommandBySelector: (noop:)
text: <none> glfw_key: ESCAPE marked_text: ()
on_key_input: glfw key: 0xe000 native_code: 0x35 action: PRESS mods: super text: '' state: 0 updateIMEPosition: left=282.000000, top=15.000000, width=7.000000, height=13.000000
sent encoded key to child
----------------- key up --------------------
NSEvent: type=KeyUp loc=(963,695.016) time=33498.9 flags=0x100108 win=0x7f84ca148020 winNum=2805 ctxt=0x0 chars=" unmodchars=" repeat=0 keyCode=53
Release: native_key: 0x35 (<cc>) glfw_key: 0xe000 mods: super
on_key_input: glfw key: 0xe000 native_code: 0x35 action: RELEASE mods: super text: '' state: 0 updateIMEPosition: left=282.000000, top=15.000000, width=7.000000, height=13.000000
kovidgoyal commented 2 years ago

On Thu, Jan 13, 2022 at 04:14:45AM -0800, page-down wrote:

I think I can update these definitions when I have time, but there are two right now that maybe need more work.

Maybe we can also check the type, I see that all the types are standard except 164.

164 = {
    ...
        type = modifier;
<key>type</key>
<string>standard</string>

I tried the following steps and could not switch OS windows, is it normal on your system?

  • quit kitty
  • set cmd+esc to Move focus to next window in system preferences
  • kitty --config=NONE --debug-input
  • cmd+n
  • cmd+esc

See https://github.com/kovidgoyal/kitty/commit/79fd01093a13b735eb97b584310ace18c548185e

page-down commented 2 years ago

Thanks. Now it works fine.

I have a question about the response of a key combination.

Is this a key release event normal?

on_key_input: glfw key: 0xe064 native_code: 0x37 action: PRESS mods: super text: '' state: 0 updateIMEPosition: left=2.000000, top=323.000000, width=8.000000, height=17.000000
sent encoded key to child
on_key_input: glfw key: 0xe061 native_code: 0x38 action: PRESS mods: shift+super text: '' state: 0 updateIMEPosition: left=2.000000, top=374.000000, width=8.000000, height=17.000000
sent encoded key to child
on_key_input: glfw key: 0x61 native_code: 0x0 action: RELEASE mods: none text: '' state: 0 updateIMEPosition: left=2.000000, top=425.000000, width=8.000000, height=17.000000
sent encoded key to child
----------------- key up --------------------
NSEvent: type=KeyUp loc=(488,444.016) time=41479.7 flags=0x100 win=0x7fdb6276a9e0 winNum=3405 ctxt=0x0 chars=" unmodchars=" repeat=0 keyCode=53
Release: native_key: 0x35 (<cc>) glfw_key: 0xe000 mods: none
Shift+LEFT_SHIFT PRESS 
CSI 57441 ; 2 u

Shift+Super+LEFT_SUPER PRESS 
CSI 57444 ; 10 u

a RELEASE 
CSI 97 ; 1 : 3 u
kovidgoyal commented 2 years ago

Check in flagsChanged in cocoa_window.m that function should possibly be changed to only send events if the key is one of the modifier keys.

kovidgoyal commented 2 years ago

See https://github.com/kovidgoyal/kitty/commit/5e5cd1acc864a5363d26d7f517d39df3b8b6e648

page-down commented 2 years ago

Is this a key release event normal?

Check in flagsChanged in cocoa_window.m See 5e5cd1a

Tested the latest changes and this issue no longer occurs.

page-down commented 2 years ago

I found that the following global functions need special treatment.

When there is no shift modifier key combination in global_shortcuts, a new shortcut with shift should be added at the same time.

Otherwise Shift+XXX (e.g. Shift+Cmd+`) does not work.

kSHKMoveFocusToActiveOrNextWindow
kSHKMoveFocusToNextWindow
kovidgoyal commented 2 years ago

On Fri, Jan 14, 2022 at 05:00:03AM -0800, page-down wrote:

I found that the following global functions need special treatment.

When there is no shift modifier key combination in global_shortcuts, a new shortcut with shift should be added at the same time.

Otherwise Shift+XXX (e.g. Shift+Cmd+`) does not work.

kSHKMoveFocusToActiveOrNextWindow
kSHKMoveFocusToNextWindow

Not sure I follow. Afre you saying the shift variant is not present in the symbolic hotkeys preferences but is supposed to work anyway?

page-down commented 2 years ago

Yes, for example, Cmd+Esc is set in the system settings to switch to the next window. Pressing Shift+Cmd+Esc at this point should switch to the previous window.

However, I can't switch windows even if I set Shift+Cmd+Backtick explicitly now. I need to check it again.

kovidgoyal commented 2 years ago

On Fri, Jan 14, 2022 at 07:02:34AM -0800, page-down wrote:

Yes, for example, Cmd+Esc is set in the system settings to switch to the next window. Pressing Shift+Cmd+Esc at this point should switch to the previous window.

why? on my macOS with default shortcuts pressing shift+cmd+` has no effect. So if you set cmd+esc why should shift+cmd+esc have an effect?

page-down commented 2 years ago

why? on my macOS with default shortcuts pressing shift+cmd+` has no effect.

By default, if "Cmd+`" is set to switch windows, then when you press the shortcut it switches to the next window.

At this point "Shift+Cmd+`" works fine in other applications on my system (as always) and will cycle to the previous window in the reverse order.

kovidgoyal commented 2 years ago

ah ok, it didnt work for me because I was doing it over vnc. Should be simple enough to implement in build_global_shortcut_lookup. If you see the shortcut is movetonextwindow and doesnt have shift in its modifiers, add shift to the mods and create an extra entry.

page-down commented 2 years ago

I found the reason why the shortcut key with Shift does not work. When checking if it is a global shortcut, the shift key transformed character is used.

global_shortcuts: (Shift+Cmd+`)

"c:120000:96"

When the Shift modifier key shortcut is pressed, the following (Shift+Cmd+~) is checked. (Not found.)

"c:120000:126"

So even if I set it directly to "Shift+Cmd+`" it doesn't work. Is there an existing function to convert Shift modified characters?

kovidgoyal commented 2 years ago

the code uses charactersIgnoringModifiers for this reason, however, it seems this function does not ignore shift. I dont know of any function that does this out of the box. The closes is UCKeyTranslate() I believe, see the various function in coca_window.m that call this function, one of them might work.

kovidgoyal commented 2 years ago

I have committed a fix for dealing with the shifted versions of those two shortcuts.

page-down commented 2 years ago

I have committed a fix for dealing with the shifted versions of those two shortcuts.

I confirmed that when "Cmd+`" is configured, using the shift modifier key works fine.

However, there are still problems with using shortcuts with the shift key that I mentioned earlier.

... I can't switch windows even if I set Shift+Cmd+Backtick explicitly ...

---------------- key down -------------------
NSEvent: type=KeyDown loc=(729,702.016) time=13597.9 flags=0x12010a win=0x7fec9a44d810 winNum=972 ctxt=0x0 chars="`" unmodchars="~" repeat=0 keyCode=50
Press: native_key: 0x32 (`) glfw_key: 0x60 mods: shift+super char_count: 1 deadKeyState: 0 repeat: 0 
    TextInputCtx: doCommandBySelector: (noop:)
text: <none> glfw_key: ` marked_text: ()
on_key_input: glfw key: 0x60 native_code: 0x32 action: PRESS mods: shift+super text: '' state: 0 updateIMEPosition: left=2.000000, top=663.000000, width=8.000000, height=17.000000
sent encoded key to child
----------------- key up --------------------
NSEvent: type=KeyUp loc=(729,702.016) time=13598.0 flags=0x12010a win=0x7fec9a44d810 winNum=972 ctxt=0x0 chars="`" unmodchars="~" repeat=0 keyCode=50
Release: native_key: 0x32 (`) glfw_key: 0x60 mods: shift+super 
on_key_input: glfw key: 0x60 native_code: 0x32 action: RELEASE mods: shift+super text: '' state: 0 updateIMEPosition: left=2.000000, top=663.000000, width=8.000000, height=17.000000
sent encoded key to child

Here is what I tested.

kovidgoyal commented 2 years ago

On Fri, Jan 14, 2022 at 09:20:13PM -0800, page-down wrote:

Here is what I tested.

  • kSHKMoveFocusToActiveOrNextWindow
    • Using the default shortcut Ctrl+F4, does not work and key events are sent to child.

https://github.com/kovidgoyal/kitty/commit/c473df4393cd25a2356fe93e7ed355a84ecebf29

  • kSHKMoveFocusToNextWindow
    • Using the default shortcut "Cmd+`", everything works fine, including the shift modifier key.
    • Using the shortcut "Shift+Cmd+`", does not work.

Dont care about this.

page-down commented 2 years ago

Thanks, I tested it and it works.

About the other issues, maybe I'll take a look later. To be honest I don't really care either. However there are still some keys that don't work, so maybe that can be dealt with.

fgoepel commented 2 years ago

Finally found some time to test a nightly build. I can confirm it works as desired now. Thanks again for the quick fix.