0

I'm trying to make a simple macOS Cocoa application using NSStatusItem to create a clickable icon on the system status bar. However, when I launch my application, I get this warning and the icon doesn't show up:

2020-03-03 14:43:11.564 Mocha_bug_example[936:39572] CGSGetActiveMenuBarDrawingStyle((CGSConnectionID)[NSApp contextID], &sCachedMenuBarDrawingStyle) returned error 268435459 on line 46 in NSStatusBarMenuBarDrawingStyle _NSStatusBarGetCachedMenuBarDrawingStyle(void)

Here's a minimal reproducible example for my application:

#import <AppKit/AppKit.h>

NSStatusItem* statusItem;

int main (int argc, char* argv[]) {
        statusItem = [NSStatusBar.systemStatusBar statusItemWithLength: -1];
        statusItem.button.title = @"foobar";
        statusItem.visible = YES;

        [NSApplication.sharedApplication run];
        return 0;
}

I compiled and ran the example like this:

MacBook-Air-5:Mocha ericreed$ clang -o Mocha_bug_example -framework AppKit -fobjc-arc Mocha_bug_example.m
MacBook-Air-5:Mocha ericreed$ ./Mocha_bug_example
2020-03-03 14:43:11.564 Mocha_bug_example[936:39572] CGSGetActiveMenuBarDrawingStyle((CGSConnectionID)[NSApp contextID], &sCachedMenuBarDrawingStyle) returned error 268435459 on line 46 in NSStatusBarMenuBarDrawingStyle _NSStatusBarGetCachedMenuBarDrawingStyle(void)
[Application hung until I pressed Ctrl+C]
^C
MacBook-Air-5:Mocha ericreed$ 

Note: disabling automatic reference counting and adding [statusItem release]; after calling run as this similar question suggested made no visible difference.

Eric Reed
  • 377
  • 1
  • 6
  • 21

3 Answers3

3

This is how to add status bar item to command line app mac osx cocoa

Adapting apodidae's answer to Swift. Just put this in the main.swift file:

let app = NSApplication()
let statusItem = NSStatusBar.system.statusItem(withLength: -1)
statusItem.button!.title = "Hello, world!"
app.run()

I don't understand the finer details of the NSReleasePool as apodidae included, but it works for me without that.

David Wolever
  • 148,955
  • 89
  • 346
  • 502
2
#import <Cocoa/Cocoa.h>

int main(){
 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
 NSApplication *application = [NSApplication sharedApplication];
 NSStatusItem* statusItem;
 statusItem = [NSStatusBar.systemStatusBar statusItemWithLength: -1];
 statusItem.button.title = @"foobar";
 statusItem.visible = YES;
 [application run];
 [pool drain];
 return 0;
}

Save file with the name 'statusBar_SO.m'

Compile from Terminal: clang statusBar_SO.m -framework Cocoa -o statusBar && ./statusBar

apodidae
  • 1,988
  • 2
  • 5
  • 9
  • this is exactly what i needed! see my separate answer for the swift version. – Andrew Barker Apr 28 '20 at 00:00
  • Interesting... Since I'm using automatic reference counting, the `NSAutoreleasePool` shouldn't be necessary in my case. My issue might've been using AppKit instead of Cocoa, not sure. I'll give this a shot as well. – Eric Reed Apr 28 '20 at 14:34
1

This is not the kind of thing you can do in main().

Except for extrememly unusual situations, you should never modify the main() that comes with the application template, and it must call NSApplicationMain():

int main(int argc, char *argv[])
{
    // start the application
    return NSApplicationMain(argc, (const char **) argv);
}

The Cocoa framework doesn't get initialized until you call NSApplicationMain() and is generally unusable until then.

This kind of setup should be done in applicationWillFinishLaunching or applicationDidFinishLaunching.

Update

The original poster is not using Xcode and is willing to brave the wilderness alone. ;)

This also implies that their application bundle will not have a main NIB file that would normally create and connect the application delegate object, main menu, and so forth.

There are intrepid individuals who have braved this territory and you can read about it in Creating a Cocoa application without NIB files.

James Bucanek
  • 3,299
  • 3
  • 14
  • 30
  • Makes sense. So I should restructure my application to use an object following `NSApplicationDelegate` with those functions and assign it to `NSApplication.sharedApplication.delegate`? – Eric Reed Mar 04 '20 at 19:10
  • (I'll accept your answer once I confirm that was the issue) – Eric Reed Mar 04 '20 at 19:54
  • 1
    Yes, that's exactly what you should do. And if you used one of the standard Cocoa application templates, that code (an application delegate object connected to the `NSApplication.delegate` property) should have already been created. All you need to do is add the methods to the delegate class. – James Bucanek Mar 04 '20 at 23:15
  • Sorry I've been busy. I've done some restructuring to get my app to launch using `NSApplicationMain` -- I had to add an `Info.plist` with an `NSPrincipalClass`. I also created a delegate and used `NSApplication.setDelegate:delegate` to assign it to the app. The app's icon shows up but the delegate functions don't appear to get called. Here's my repo: https://github.com/HewwoCraziness/Mocha_bug_example (Thanks for all the help by the way) – Eric Reed Mar 09 '20 at 13:12
  • Again, __stop trying to do things in `main()`__. Forget `main()`. `main()` belongs to Cocoa. A Cocoa app creates *and connects* the app delegate object via the main `NIB` file, during its initialization. At this point I'd suggest you just start over with a new project using the standard Cocoa application template. It's all you need and it does all the right things. *Then* modify the delegate class it creates for you. – James Bucanek Mar 09 '20 at 17:11
  • I should clarify... I'm not using Xcode due to disk space constraints and compilation issues in the past. I was just wondering if there's a way to do something like this without using Xcode/Interface Builder at all. (Maybe setting NSPrincipalClass to a subclass of NSApplication and overriding the subclass' init() would work?) – Eric Reed Mar 09 '20 at 17:25
  • I just don't know how to assign the delegate without doing it in main() or using a NIB from Interface Builder. – Eric Reed Mar 09 '20 at 17:27
  • 1
    Usually you use the main NIB. Creating a Cocoa app "the hard way" is ... well ... hard. There are a lot of details, but subclassing `NSApplication` that creates and connects the delegate object there would be one solution. Check out [Creating a Cocoa application without NIB files](https://stackoverflow.com/questions/2997333/creating-a-cocoa-application-without-nib-files). Not recommended, but *good luck*! – James Bucanek Mar 09 '20 at 18:09
  • Awesome, thanks. (Not sure if you'd want that in your answer as an alternate option.) Sorry for the confusion. – Eric Reed Mar 09 '20 at 18:32