15

I'm integrating a GTK# application into Mac OS X. GTK on Mac OS X is a wrapper over some Cocoa and Carbon fundamentals. We have some platform-specific stuff directly using Carbon global menu APIs (it's more low-level and flexible than Cocoa, and we don't need to be 64-bit).

It seems that GTK swallows up all the keyboard events before Carbon dispatches them as commands. This makes sense, because there is no mapping of Carbon commands into the GTK world. In general, this isn't a problem, because we have a global key event handler and dispatch everything via our own command system. However, this seems to be preventing Cmd-? from opening the Help search menu, and I cannot find a way to do this programmatically.

Menu Manager's MenuSelect function is promising, but I haven't figured out a way to determine the coordinate automatically, and for some reason it only works when I hit the combination twice...

Alternatively, a way to dispatch the Cmd-? keystroke to Carbon's command handling or synthesize the command event directly would be good, but I haven't had any luck in that area.

Carbon's ProcessHICommand isn't any use without a command ID and I can't figure out what it is (if there is one)

Regarding Cocoa, I can get hold of the NSWindow and call InterpretKeyEvents, but I haven't had any luck success synthesizing the NSEvent - it just beeps. The event I'm using is

var evt = NSEvent.KeyEvent (NSEventType.KeyDown, System.Drawing.PointF.Empty,
    NSEventModifierMask.CommandKeyMask | NSEventModifierMask.ShiftKeyMask,
    0, win.WindowNumber, NSGraphicsContext.CurrentContext, "?", "?",
    false, (ushort) keycode);

Keycode is determined from a GTK keymap to be 44. I confirmed that the keycode was correct using a plain MonoMac (Cocoa) app but InterpretKeyEvents did not work with the event in that app either. And I can't find any selector associated with the command.

IanNorton
  • 7,145
  • 2
  • 25
  • 28
Mikayla Hutchinson
  • 16,113
  • 2
  • 44
  • 50
  • 2
    Users have been asking for the Cmd-Shift-? keybinding to open the Help search menu. Somehow, GTK's event loop prevents the built-in handling from working. – Mikayla Hutchinson Nov 18 '10 at 22:56
  • With the cocoa event, have you tried using "/" instead of "?". – Abhi Beckert May 09 '11 at 01:22
  • 4
    On a side note, your statement that carbon is "more low-level and flexible than Cocoa" is incorrect. Carbon is a completely separate API that was designed to be compatible with Mac OS 8/9. It is deprecated and poorly maintained and is not "underneath" cocoa in any way. Cocoa is actually much older than Carbon, even though it's more modern looking at a first glance. You should really be interfacing with Cocoa instead of Carbon. – Abhi Beckert May 09 '11 at 01:24
  • So why do I see HIToolbox calls in the Cocoa menu callstack? – Mikayla Hutchinson May 09 '11 at 15:38
  • Interesting. I guess they must have decided some of Carbon's menu stack is better than the one in Cocoa, so they used it. Still, I wouldn't use any carbon API's in an app that you're going to be working on for a long time. Apple started seriously moving away from Carbon in the last release of Mac OS X, and anyone who writes Carbon code today, should expect it will need to be re-written in Cocoa in the near future. – Abhi Beckert May 10 '11 at 21:43
  • Yeah, it wasn't actually *public* Carbon API, but I think it's reasonable to say that Cocoa's menu system is built on top of primitives from Carbon's HIToolbox menu system. Also, Cocoa *is* certainly built on top of lever-level C APIs (Core Foundation, Obj-C runtime etc). However, I agree that using deprecated Carbon APIs such as the HIToolbox is a bad idea. – Mikayla Hutchinson May 11 '11 at 01:09
  • Carbon is also built on top of Core Foundation. Core Foundation does use C, but only because it is necessary for extra performance, and most of it is compatible with Obj-C code. For example you can cast a `CFDictionaryRef` to `NSDictionary *` or `id` and it will behave exactly the same as an NSDictionary object (there are some subtle differences to make CFDictionary faster than NSDictionary. But they are negligible). – Abhi Beckert May 12 '11 at 00:17

2 Answers2

1

You can use accessibility APIs to fake a press on the menu item.

NSString *helpMenuTitle = [[[[NSApplication sharedApplication] mainMenu] itemWithTag:HELP_MENU_TAG] title];
AXUIElementRef appElement = AXUIElementCreateApplication(getpid());
AXUIElementRef menuBar;
AXError error = AXUIElementCopyAttributeValue(appElement,
                                              kAXMenuBarAttribute,
                                              (CFTypeRef *)&menuBar);
if (error) {
    return;
}

CFIndex count = -1;
error = AXUIElementGetAttributeValueCount(menuBar, kAXChildrenAttribute, &count);
if (error) {
    CFRelease(menuBar);
    return;
}

NSArray *children = nil;
error = AXUIElementCopyAttributeValues(menuBar, kAXChildrenAttribute, 0, count, (CFArrayRef *)&children);
if (error) {
    CFRelease(menuBar);
    return;
}

for (id child in children) {
    AXUIElementRef element = (AXUIElementRef)child;
    id title;
    AXError error = AXUIElementCopyAttributeValue(element,
                                                  kAXTitleAttribute,
                                                  (CFTypeRef *)&title);
    if ([title isEqualToString:helpMenuTitle]) {
        AXUIElementPerformAction(element, kAXPressAction);
        CFRelease(title);
        break;
    }
    CFRelease(title);
}
CFRelease(menuBar);
[children release];
dgatwood
  • 10,129
  • 1
  • 28
  • 49
George
  • 4,189
  • 2
  • 24
  • 23
  • We actually found another solution that involved fixing the GTK toolkit so it wouldn't intercept that keypress but would let Cocoa handle it instead. However, I'm accepting this answer since it's a good solution for anyone willing to depend on Accessibility Frameworks. – Mikayla Hutchinson Dec 24 '13 at 22:58
  • Careful there. This will fail in any language other than English because of the hard-coded @"Help" title. You need to assign a tag value to the help menu in your application's main menu, then grab the menu's actual title and use that instead. – dgatwood Jul 28 '14 at 15:17
1

You could do this via calling from C / Objective-C a AppleScript (GUI) script , that would essentially do the pointing and clicking of a user just as a user would do, to open the help menu program-matically.

MrDaniel
  • 583
  • 1
  • 7
  • 19