`NSStatusItem` created from `main()` doesn't show up on system status bar - cocoa

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.

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.

#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

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.

Related

how to get windowid from applescript

I am trying to get window id of of every window.
set r to {}
tell application "System Events"
repeat with t in windows of processes
set sid to id of t
set end of r to {title:title of t, id:sid}
end repeat
end tell
r
The above code returns
error "System Events got an error: Can’t get id of item 1 of every window of every process." number -1728 from id of item 1 of every window of every process
How to get the window id of every window?
I wrote a little Objective-C program for you that gets the window owners, window names and window ids:
////////////////////////////////////////////////////////////////////////////////
// windowlist.m
// Mark Setchell
//
// Get list of windows with their WindowOwnerNames, WindowNames and WindowNumbers
//
// Compile with:
// clang windowlist.m -o windowlist -framework coregraphics -framework cocoa
//
// Run with:
// ./windowlist
//
// You can then run "screencapture" to capture that window:
//
// screencapture -l<windowid> -x someFile.[png|jpg|tif]
////////////////////////////////////////////////////////////////////////////////
#include <Cocoa/Cocoa.h>
#include <CoreGraphics/CGWindow.h>
int main(int argc, char **argv)
{
NSArray *windows = (NSArray *)CGWindowListCopyWindowInfo(kCGWindowListExcludeDesktopElements|kCGWindowListOptionOnScreenOnly,kCGNullWindowID);
for(NSDictionary *window in windows){
int WindowNum = [[window objectForKey:(NSString *)kCGWindowNumber] intValue];
NSString* OwnerName = [window objectForKey:(NSString *)kCGWindowOwnerName];
NSString* WindowName= [window objectForKey:(NSString *)kCGWindowName];
printf("%s:%s:%d\n",[OwnerName UTF8String],[WindowName UTF8String],WindowNum);
}
}
Output
./windowlist
Preview:(null):300
Safari:(null):48
Terminal:(null):231
VirtualBox:(null):212
Mail:(null):150
Dropbox:(null):181
Finder:(null):118
Notification Center:(null):83
Google Drive:(null):73
Copy:(null):68
InkServer:(null):49
iTerm:(null):44
Google Drive::69
Copy::66
Dropbox::63
Creative Cloud::57
Spotlight::41
SystemUIServer::33
SystemUIServer:(null):36
SystemUIServer::31
Window Server:Menubar:3
Dock:Dock:23
iTerm:2. bash:190
iTerm:1. bash:336
This is not possible with this code.
In the processes array of System Events the property id of a window is not required to be available in AppleScript, that's the reason why you get the error.
If an application has an AppleScript dictionary and the windows element is provided then all windows have an id property, but not all applications support AppleScript and the non-document based applications don't provide the windows element by default.

OS X main.m running a server style program

I writing my first OS X command line program which is a server style program. It's job is to process various information and respond to other events.
I have the following code in my main.m
int main(int argc, const char * argv[]) {
#autoreleasepool {
PIPieman *pieman = [[[PIPieman alloc] init] autorelease];
[pieman start];
NSRunLoop *loop = [NSRunLoop currentRunLoop];
while (!pieman.finished && [loop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
}
return 0;
}
I got this code from various documents and the basic idea is that once pieman.finished is set to YES, that the program then exits.
The problem I have is that the flag is being set by code inside pieman, but the run loop is not being triggered, so the program does not exit. I've been looking for ways to trigger the run loop and there seem to be a variety, but none that feel like a good solution. For example I could reduce the beforeDate: to a few seconds to cause periodic triggering of the run loop.
My preference would be that something triggers the run loop on the change of the finished boolean value.
Any suggestions?
You need to tell the run loop to return from runMode:beforeDate:. The NSRunLoop class doesn't define a message to do that, but NSRunLoop is built on CFRunLoop, and the CFRunLoopStop function does what you need.
#implementation PIPieman
...
- (void)setFinished:(BOOL)finished {
_finished = finished;
CFRunLoopStop(CFRunLoopGetMain());
}

Create a NSWindow under C++

I made a simple experiment, I wrote the following code in Xcode:
int main(int argc, char** argv)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSApplication* app = [[NSApplication alloc] init];
NSWindow* window = [[NSWindow alloc]
initWithContentRect: NSMakeRect(0, 0, 640, 480)
styleMask: NSTitledWindowMask | NSMiniaturizableWindowMask
backing: NSBackingStoreBuffered
defer: NO];
[window setTitle: #"New Window"];
[window center];
[window makeKeyAndOrderFront:nil];
[app run];
[pool release];
return 0;
}
It runs as expected, a new empty window out there, but if I compile it under terminal with command line:
$ g++ test.mm -framework Cocoa
$ ./a.out
It will breeze at [app run] without the window.
Am I doing wrong? Why it has different behaviors between Xcode and command line? Does somebody can tell me how I can achive the same behaviors in command line?
Thanks in advance.
Taking your code and compiling it works fine for me on a 10.7.2 machine... the window shows up (albeit behind my terminal window and does not appear as a running application).
If you're looking to get your app to show up as a process running in the dock, it has to be properly packaged in a .app bundle. If you do the following:
g++ test.mm -framework Cocoa
mkdir -p cli.app/Contents/MacOS
cp a.out cli.app/Contents/MacOS/cli
and then run cli.app/Contents/MacOS/cli from the command line, your process will show up as a running application.

Memory corruption after using NSOpenPanel

I got a problem that leads to my program reporting malloc corruption when I use NSOpenPanel. My code is mainly C (not using Xcode) and what I do is something like this:
main(..)
{
[NSApplication sharedApplication];
... create window etc, no problem
[NSApp run];
}
Later I call something like this
openFileDialog(..)
{
// tried to create NSAutoreleasePool and things here bit still breaks
NSOpenPanel* open = [NSOpenPanel openPanel];
int res = [open runModal]
...
}
After exiting the function (or a bit later) I will get
test (1948,0x7fff7d852960) malloc: * error for object 0x7ff19b879608:
incorrect checksum for freed object - object was probably modified after being freed.
* set a breakpoint in malloc_error_break to debug
Ideas?

mouseclick script help

I need to get the mouse to click on a spot on the screen, to be specific a flash object in safari....
Id tried to do this with applescript but it didnt work. Then I found this script on the internet.
// File:
// click.m
//
// Compile with:
// gcc -o click click.m -framework ApplicationServices -framework Foundation
//
// Usage:
// ./click -x pixels -y pixels
// At the given coordinates it will click and release.
#import <Foundation/Foundation.h>
#import <ApplicationServices/ApplicationServices.h>
int main(int argc, char *argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSUserDefaults *args = [NSUserDefaults standardUserDefaults];
// grabs command line arguments -x and -y
//
int x = [args integerForKey:#"x"];
int y = [args integerForKey:#"y"];
// The data structure CGPoint represents a point in a two-dimensional
// coordinate system. Here, X and Y distance from upper left, in pixels.
//
CGPoint pt;
pt.x = x;
pt.y = y;
// This is where the magic happens. See CGRemoteOperation.h for details.
//
// CGPostMouseEvent( CGPoint mouseCursorPosition,
// boolean_t updateMouseCursorPosition,
// CGButtonCount buttonCount,
// boolean_t mouseButtonDown, ... )
//
// So, we feed coordinates to CGPostMouseEvent, put the mouse there,
// then click and release.
//
CGPostMouseEvent( pt, 1, 1, 1 );
CGPostMouseEvent( pt, 1, 1, 0 );
[pool release];
return 0;
}
I have only scripted in applescript so I didnt quite understood it
but when I activate it it clicks on the top left
but here is my question, what should I chance in the script to make it click other places than in the top corner
more info about the script on this website: http://hints.macworld.com/article.php?story=2008051406323031
This is stuff that you learn in most introductory programming courses. A complete answer would be very long, so I just tell you a few cornerstones:
The program that you downloaded is not a script
It's objective-C-sourcecode
You need to learn how to work with the Terminal application (the command line).
You need to learn how to invoke commands on the terminal (e.g. gcc)
You have to understand the meaning of the word compile. In this case it's something the author wanted you to do at the command line.
Second step:
// starts a comment in Objective C
gcc ... is the command that should be executed on the command line to compile the program
./click is what you do to invoke the program (after you compiled it :-) )
gcc -o click click.m -framework ApplicationServices -framework Foundation
means:
gcc: Gnu C Compiler
-o click: The program should be named click
click.m: This should be the name of the source code (the file that you called 'script')
hope this helps...

Resources