-
-
Save Wowfunhappy/b09900c5420767651338a57692348e31 to your computer and use it in GitHub Desktop.
| #import <Foundation/Foundation.h> | |
| #import <AppKit/AppKit.h> | |
| #import "ZKSwizzle.h" | |
| @interface myNSApplication : NSApplication | |
| @end | |
| @implementation myNSApplication | |
| - (void)sendEvent:(NSEvent *)event { | |
| // Firefox does not handle user-defined key equivalents properly | |
| // https://bugzilla.mozilla.org/show_bug.cgi?id=1333781 | |
| // Check if the event is a key down event with a modifier key. (Otherwise, it can't be a keyboard shortcut.) | |
| if ( | |
| [event type] == NSKeyDown && | |
| [event modifierFlags] & (NSCommandKeyMask | NSAlternateKeyMask | NSControlKeyMask) | |
| //We don't check NSShiftKeyMask because shortcuts aren't allowed to use Shift as the only modifier key. | |
| ) { | |
| // Query user-defined key equivalents | |
| NSDictionary *userKeyEquivalents = [[NSUserDefaults standardUserDefaults] objectForKey:@"NSUserKeyEquivalents"]; | |
| if (userKeyEquivalents) { | |
| // Check if the event matches any user-defined key equivalents | |
| for (NSString *menuItemTitle in userKeyEquivalents) { | |
| NSString *shortcut = userKeyEquivalents[menuItemTitle]; | |
| if ([self event:event matchesShortcut:shortcut]) { | |
| [self performKeyEquivalent:event]; | |
| [self performActionForItemWithTitle:menuItemTitle inMenu:[NSApp mainMenu]]; | |
| return; | |
| } | |
| } | |
| } | |
| } | |
| // Pass event to Firefox to handle normally. | |
| ZKOrig(void, event); | |
| } | |
| - (BOOL)event:(NSEvent *)event matchesShortcut:(NSString *)shortcut { | |
| // Convert the shortcut string to a key equivalent and modifier mask | |
| NSString *characterKey = [shortcut substringFromIndex:[shortcut length] - 1]; | |
| NSString *modifierString = [shortcut substringToIndex:[shortcut length] - 1]; | |
| NSUInteger modifierMask = 0; | |
| for (int i = 0; i < [modifierString length]; i++) { | |
| switch ([modifierString characterAtIndex:i]) { | |
| case '@': | |
| modifierMask |= NSCommandKeyMask; | |
| break; | |
| case '~': | |
| modifierMask |= NSAlternateKeyMask; | |
| break; | |
| case '^': | |
| modifierMask |= NSControlKeyMask; | |
| break; | |
| case '$': | |
| modifierMask |= NSShiftKeyMask; | |
| break; | |
| } | |
| } | |
| // Compare with the event | |
| return ( | |
| [[[event charactersIgnoringModifiers] lowercaseString] isEqualToString:characterKey] && | |
| ([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) == modifierMask | |
| ); | |
| } | |
| - (void)performActionForItemWithTitle:(NSString *)title inMenu:(NSMenu *)menu { | |
| for (NSMenuItem *menuItem in [menu itemArray]) { | |
| if ([menuItem hasSubmenu]) { | |
| [self performActionForItemWithTitle:title inMenu:[menuItem submenu]]; | |
| } else { | |
| //Ensure three periods ("...") matches the elipses character ("…"). | |
| title = [title stringByReplacingOccurrencesOfString:@"..." withString:@"…"]; | |
| NSString *menuItemTitle = [[menuItem title] stringByReplacingOccurrencesOfString:@"..." withString:@"…"]; | |
| if ([menuItemTitle isEqualToString:title]) { | |
| [[menuItem menu] update]; //highlight menu | |
| [[menuItem menu] performActionForItemAtIndex:[[menuItem menu] indexOfItem:menuItem]]; | |
| return; | |
| } | |
| } | |
| } | |
| } | |
| @end | |
| @interface myNSWindow : NSWindow | |
| @end | |
| @implementation myNSWindow | |
| - (BOOL)makeFirstResponder:(NSResponder *)responder { | |
| //Initialize menus to ensure: | |
| //1. Every item can be triggerred by its key equivalents | |
| //2. Every item can appear in the search results of the `Help` search box. | |
| [self initializeSubmenusOf: [NSApp mainMenu]]; | |
| //We should also listen for menu changes. Unfortunately, the below code will crash Firefox. | |
| //Todo: figure out how to do this properly! | |
| /*[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(initializeSubmenusOf:) name:@"NSMenuDidAddItemNotification" object:[NSApp mainMenu]]; | |
| [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(initializeSubmenusOf:) name:@"NSMenuDidRemoveItemNotification" object:[NSApp mainMenu]];*/ | |
| return ZKOrig(BOOL, responder); | |
| } | |
| - (void)initializeSubmenusOf:(NSMenu *)menu { | |
| [[menu delegate] performSelector:@selector(menuWillOpen:) withObject:menu]; | |
| [[menu delegate] performSelector:@selector(menuDidClose:) withObject:menu]; | |
| for (NSMenuItem *menuItem in [menu itemArray]) { | |
| if ([menuItem hasSubmenu]) { | |
| [self initializeSubmenusOf:[menuItem submenu]]; | |
| } | |
| [self fixUpMenuItem: menuItem]; | |
| } | |
| } | |
| - (void)fixUpMenuItem:(NSMenuItem *)menuItem { | |
| // In Firefox, `Select All` menu item is initially disabled for some reason | |
| if (![menuItem isEnabled] && [[menuItem title] isEqualToString:NSLocalizedString(@"Select All", nil)]) { | |
| [menuItem setEnabled:YES]; | |
| } | |
| } | |
| @end | |
| @implementation NSObject (main) | |
| + (void)load { | |
| ZKSwizzle(myNSApplication, NSApplication); | |
| ZKSwizzle(myNSWindow, NSWindow); | |
| } | |
| @end | |
| int main() {} |
Default shortcuts still work if nothing else is assigned to them...
Although this doesn't match the behavior of proper cocoa apps,
it's not terrible and I don't see an easy way to fix it.
It is terrible though. I keep hitting Cmd-M by mistake for some reason in daily usage of the Mac, so I reassigned Minimize to Cmd-Opt-Ctrl-Shift-M or something like that, but in Firefox, the app I probably use the most, Cmd-M still minimizes the window.
It is terrible though. I keep hitting Cmd-M by mistake for some reason in daily usage of the Mac, so I reassigned Minimize to Cmd-Opt-Ctrl-Shift-M or something like that, but in Firefox, the app I probably use the most, Cmd-M still minimizes the window.
That’s an annoying use case I didn’t think of! Open to ideas on how to resolve this one.
@isolinear I can use DYLD_INSERT_LIBRARIES, but I'm on a truly ancient version of macOS (10.9). I think on 10.15 you should be able to use
DYLD_INSERT_LIBRARIESif you disable System Integrity Protection, is that turned on? If you want SIP, I think you could also use insert_dylib to add the library to the Firefox binary; you may need to re-sign Firefox afterwards. However, I'm unfortunately not familiar with modern macOS anymore, I know Apple has added tons of restrictions on code injection and it's one of the reasons I'm on this ancient version.