How can I tell my Cocoa application to quit from within the application itself? - cocoa

I'm looking for a good way to tell my Cocoa application to quit itself. Rest assured that this will not be used for production code. I'm just looking for an easy way to run one test and then close the application during debugging.
I have found that exit(0); will close the app, but it bypasses all of the normal application exit procedures, and I would like to keep all of them in place.
Essentially I want things to work as if a user pulled "Quit" from the menu, but I want it to happen automatically after I have finished with my test.
My code currently looks like this:
#if (SUPERFANCY_TESTING_MODE)
[self doSomething];
exit(0); // <-- I need something better to go here
#endif

You can pretty much rest assured that your app is going to get killed at least some of the time. Thus, defending against exits the like of exit(0); is required.
However, NSApplication implements the -terminate: method.
[NSApp terminate: nil]; ought to do what you want.
I would generally suggest posting it via -performSelector:afterDelay: with a delay of 0.0 to force it to happen at the top of the next pass through the event loop.
Example:
[NSApp performSelector:#selector(terminate:) withObject:nil afterDelay:0.0];

Related

How check for Command-Period in a tight loop?

I'm implementing a scripting language where the user might be causing an endless loop by accident. I want to give the user the opportunity to cancel such a runaway loop by holding down the command key while typing the period (".") key.
Currently, once for every line, I check for cancellation with this code:
NSEvent * evt = [[NSApplication sharedApplication] nextEventMatchingMask: NSKeyDownMask untilDate: [NSDate date] inMode: WILDScriptExecutionEventLoopMode dequeue: YES];
if( evt )
{
NSString * theKeys = [evt charactersIgnoringModifiers];
if( (evt.modifierFlags & NSCommandKeyMask) && theKeys.length > 0 && [theKeys characterAtIndex: 0] == '.' )
{
// +++ cancel script execution here.
}
}
The problem with this is that it eats any keyboard events that the user might be typing while the script is running, even though scripts should be able to check for keypresses. Also, it doesn't dequeue the corresponding NSKeyUp events. But if I tell it to dequeue key up events as well, it might dequeue the keyUp for a keypress that was being held before my script started and my application might never find out the key was released.
Also, I would like to not dequeue any events until I know it is actually a cancel event, but there is no separate dequeue call, and it feels unreliable to just assume the frontmost event on a second call will be the same one. And even if it is guaranteed to be the first, that would mean that the user typing an 'a' and then Cmd-. would mean I only ever see the 'a' and never the Cmd-. behind it if I don't dequeue events.
Is there a better option than going to the old Carbon stand-by GetKeys()? Fortunately, that seems to be available in 64 bit.
Also, I'm thinking about adding an NSStatusItem that adds a button to cancel the script to the menu bar or so. But how would I process events in a way that doesn't let the user e.g. select a menu while a script expects to be ruler of the main thread?
Any suggestions? Recommendations?
Using -addLocalMonitorForEventsMatchingMask: as Dave suggests is probably the easiest way to go about this, yes.
I just wanted to add that despite your unreliable feeling, the event queue is really a queue, and events don't change order. It is perfectly safe (and standard practice in event loops) to call -nextEventMatchingMask:inMode:dequeue:NO, examine the event, determine that it is one you want to deal with, and then call -nextEventMatchingMask:inMode:dequeue:YES in order to consume it. Just make sure that your mask and mode are identical between the two calls.
I would suggest using an event monitor. Since you're asking NSApp for events, it would seem that you're running the script in the current process, so you only have to monitor events in your own process (and not globally).
There are several ways to do this (subclassing NSApplication and overriding -sendEvent:, putting in an event tap, etc), but the easiest way to do this would be with a local event monitor:
id eventHandler = [NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDown handler:^(NSEvent *event) {
// check the runloop mode
// check for cmd-.
// abort the script if necessary
return event;
}];
When you're all done monitoring for events, don't forget to unregister your monitor:
[NSEvent removeMonitor:eventHandler];
So there's +[NSEvent modifierFlags] which is intended as a replacement for GetKeys(). It doesn't cover your use case of the period key though, sadly.
The core problem here with the event queue is you want to be able to search it, which isn't something the API exposes. The only workaround to that I can think of is to dequeue all events into an array, checking for a Command-. event, and then re-queue them all using postEvent:atStart:. Not pretty.
Perhaps as an optimisation you could use +[NSEvent modifierFlags] to only check the event queue when the command key is held down, but that sounds open to race conditions to me.
So final suggestion, override -postEvent:atStart: (on either NSApplication or NSWindow) and see if you can fish out the desired info there. I think at worst it could be interesting for debugging.

cocoa - executing stuff, need to open window, get input, then continue w/o reentering runloop

Is there any way to do this?
Right now, I get called, I'm doing things, I have to open a window to ask for input, then I have to FINISH doing things before I return from the original call.
If I enter the runloop for the window, it never ends or returns control to me.
What am I missing here?
I got it -- [NSApp runModalForWindow: window] -- that runs JUST the loop for the window, and when you stop it, control returns to you, leaving the main run loop undisturbed.
What I was doing was [NSApp run], which runs everything, and so when the window issued the stop, everything did.

How to properly quit application, call exit(0)?

I'm not sure which is the right way, right now I'm just calling exit(0) when a user clicks on the exit button.
Use NSApplication's terminate method. For example:
[NSApp terminate:self]
Documentation is available here.
The previously given answer is deprecated since 10.10 and this question is the first thing that turns up for cocoa quit application, so use this instead (Swift):
NSApplication.sharedApplication().terminate(self)
Note: As of now it's the following:
NSApplication.shared().terminate(self)
I always terminate App the safe way.
[NSApp performSelector:#selector(terminate:) withObject:nil afterDelay:0.0];
This will put the event in the next loop.

Updating the progress indicator while downloading firmware to the device

I am developing a cocoa application which downloads firmware to the device. The progress of downloading is showed using NSProgressIndicator. I call the -incrementBy: method of NSProgressIndicator after DeviceRequestTO method in a while loop. But the progress indicator gets updated only after the entire firmware is written to the device. It shows 100% completion at one go itself. So I added the -displayIfNeeded method of NSView class. Now it shows progress smoothly but this too occurs after the firmware download is complete. How can I achieve the progress indication and write operation simultaneously?
Following is the code:
while(1)
{
int result = (*dev)->DeviceRequestTO(dev, &request);
printf("\nBlocks Written Successfully: %d",DfuBlockCnt);
[refToSelf performSelectorOnMainThread:#selector(notifyContent)
withObject:nil
waitUntilDone:NO];
}
//In main thread
- (void)notifyContent{
[dnldIndicator incrementBy:1];
[self displayIfNeeded];
}
The method you need to call is setNeedsDisplay:, not displayIfNeeded. The latter means “send yourself display if somebody has sent you setNeedsDisplay:YES”. If you don't do that last part, the view doesn't know it should display, and displayIfNeeded will do nothing.
And once you add the setNeedsDisplay: message, you may be able to cut out the displayIfNeeded message, as the framework sends that message to the window (and, hence, to all its views) periodically anyway.
Your code looks exactly like some that I use for updating UIProgressIndicators and NSProgressIndicators on the Mac and iPhone, code that works perfectly for me. I'm assuming, like menumachine, that your while loop exists on a background thread (created using performSelectorInBackground:withObject: or NSThread's detachNewThreadSelector:toTarget:withObject:).
Are the minValue and maxValue of the progress indicator set correctly (0 and 100 or whatever your scale is)?
How frequently do updates occur? Maybe you're sending too many events too quickly and the UI is not having a chance to update properly.
This code should work, as far as I can tell.

RBSplitView has delayed reload of autosaved view positions

I really enjoy using RBSplitView, an open source replacement for NSSplitView, but I have a problem in my shipping app and am experiencing it again in a new project.
The problem is I'm telling the RBSplitView to autosave its position state by giving it an autosave name. When my app launches the RBSplitView doesn't seem to honor the saved state till a second after the window is drawn.
I've spent the night trying to debug the behavior but have had little success. Anyone out there use this lib and have some advice?
You can scrub this quicktime movie to the issue at work:
http://media.clickablebliss.com/billable/interface_experiments/rbsplitview_delayed_autosave_reload2.mov
I've still been unable to figure out why this is happening but I do have a workaround.
First, make sure your main window is not visible at launch and then at the end of applicationDidFinishLaunching in your app delegate add something like:
[mainWindow performSelector:#selector(makeKeyAndOrderFront:) withObject:self afterDelay: 0.1];
The delay is the key. If you just tell the window to makeKeyAndOrderFront: I still see the issue. However as long as it has a beat of time it looks good.
This likely is happening because the RBSplitView instance needs to wait until it's first moment to get to set its frame to the autosaved value, which happens to be after the user can see it. This 0.0-delay trick simply delays showing the window until the very next runloop, which gives the split view a chance to do its magic (and other views) so that when the user sees the window, it's already nice and sexy. So just do the delay at 0.0 and you'll be fine.
I have a similar, but slightly different workaround in my app that uses RBSplitView. In applicationDidFinishLaunching:, I call adjustSubviews on the split view before calling makeKeyAndOrderFront: on the window that contains it. This seems to knock the split view in to order before it gets displayed on the screen.

Resources