Cocoa event loop blocks while tracking a menu - macos

I maintain a legacy OSX application that instead of calling NSApplicationMain() to process events, runs an event loop on the main thread that looks like this:
...
do {
event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:distantPast
inMode: NSDefaultRunLoopMode dequeue:YES];
[self handleEvent:event];
}
} while(event != nil);
...
Everything works until I activate the application's menu. Once the menu is active, nextEventMatchingMask blocks forever. I can see in lldb that osx starts another modal event loop (carbon?) which causes the cococa's one to block:
frame #2: 0x00007fff92748eb4 CoreFoundation`__CFRunLoopServiceMachPort + 212
frame #3: 0x00007fff9274837b CoreFoundation`__CFRunLoopRun + 1371
frame #4: 0x00007fff92747bd8 CoreFoundation`CFRunLoopRunSpecific + 296
frame #5: 0x00007fff8c11c56f HIToolbox`RunCurrentEventLoopInMode + 235
frame #6: 0x00007fff8c11c2ea HIToolbox`ReceiveNextEventCommon + 431
frame #7: 0x00007fff8c17568a HIToolbox`AcquireNextEventInMode + 54
frame #8: 0x00007fff8c172e2b HIToolbox`IsUserStillTracking(MenuSelectData*, unsigned char*) + 173
frame #9: 0x00007fff8c172a41 HIToolbox`TrackMenuCommon(MenuSelectData&, unsigned char*) + 1661
frame #10: 0x00007fff8c1803c1 HIToolbox`MenuSelectCore(MenuData*, Point, double, unsigned int, OpaqueMenuRef**, unsigned short*) + 510
frame #11: 0x00007fff8c1800fe HIToolbox`_HandleMenuSelection2 + 446
frame #12: 0x00007fff8a9a8de0 AppKit`_NSHandleCarbonMenuEvent + 277
frame #13: 0x00007fff8a8dfd0d AppKit`_DPSNextEvent + 1828
frame #14: 0x00007fff8a8def68 AppKit`-[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 346
Is there anything I can do to prevent this behaviour ?
Unfortunately I cannot rewrite the application so I need a quick and dirty solution to run the main thread while the menu is active.

Related

Why is rotating an NSImageView crashing my app?

I'm making a macOS app and I've got this code used to rotate an image:
didSet {
let degreesToRotate = oldValue - phoneXRotation
phoneImageView.rotate(byDegrees: CGFloat(degreesToRotate))
degreesLabel.isHidden = false
degreesLabel.stringValue = "\(phoneXRotation)"
if phoneXRotation > 70 {
statusLabel.stringValue = "Phone orientation: Right Tilt"
} else if phoneXRotation < -70 {
statusLabel.stringValue = "Phone orientation: Left Tilt"
} else {
statusLabel.stringValue = "Phone orientation: Flat"
}
}
The app will randomly crash in an lldb error. If I comment out the third line that rotate the phone, I have no problems. The conversion from Int to CGFloat shouldn't crash. Any ideas?
Here is the stack trace:
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
frame #0: 0x00007fff8989b44e AppKit`-[NSApplication _crashOnException:] + 109
frame #1: 0x00007fff8996ea32 AppKit`__37+[NSDisplayCycle currentDisplayCycle]_block_invoke.31 + 708
frame #2: 0x00007fff8ba62d37 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
frame #3: 0x00007fff8ba62ca7 CoreFoundation`__CFRunLoopDoObservers + 391
frame #4: 0x00007fff8ba436d9 CoreFoundation`__CFRunLoopRun + 873
frame #5: 0x00007fff8ba43114 CoreFoundation`CFRunLoopRunSpecific + 420
frame #6: 0x00007fff8afa3ebc HIToolbox`RunCurrentEventLoopInMode + 240
frame #7: 0x00007fff8afa3cf1 HIToolbox`ReceiveNextEventCommon + 432
frame #8: 0x00007fff8afa3b26 HIToolbox`_BlockUntilNextEventMatchingListInModeWithFilter + 71
frame #9: 0x00007fff8953ca54 AppKit`_DPSNextEvent + 1120
frame #10: 0x00007fff89cb87ee AppKit`-[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 2796
frame #11: 0x00007fff895313db AppKit`-[NSApplication run] + 926
frame #12: 0x00007fff894fbe0e AppKit`NSApplicationMain + 1237
* frame #13: 0x000000010000691d Dataspeed Mac Project`main at AppDelegate.swift:12
frame #14: 0x00007fffa11f1235 libdyld.dylib`start + 1
frame #15: 0x00007fffa11f1235 libdyld.dylib`start + 1

CGDisplayModeGetWidth/Height() sometimes returns pixels, sometimes points

According to Apple, both CGDisplayModeGetWidth() and CGDisplayModeGetHeight() should return points instead of pixels starting in macOS 10.8. But Apple's word on those APIs isn't consistent because here they say that the functions return pixels and not points.
This confusion is also reflected in practice because both functions only seem to return points sometimes, not all the time. Sometimes they also return pixels. Consider this example:
CGDirectDisplayID id = CGMainDisplayID();
CFArrayRef modes = CGDisplayCopyAllDisplayModes(id, NULL);
CGDisplayModeRef mode;
int c = 0, k, n;
n = CFArrayGetCount(modes);
for(k = 0; k < n; k++) {
mode = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, k);
printf("GOT SIZE: %d %d\n", (int) CGDisplayModeGetWidth(mode), (int) CGDisplayModeGetHeight(mode));
}
CFRelease(modes);
The code iterates over all available screen modes. In this example, the output is in pixels.
When using this code, however, the output is in points:
CGDirectDisplayID id = CGMainDisplayID();
mode = CGDisplayCopyDisplayMode(id);
printf("NEW GOT SIZE: %d %d\n", (int) CGDisplayModeGetWidth(mode), (int) CGDisplayModeGetHeight(mode));
CGDisplayModeRelease(mode);
But why? Why do CGDisplayModeGetWidth() and CGDisplayModeGetHeight() return pixels in the first code snippet and points in the second? This is confusing me.
To make things even more complicated, starting with macOS 10.8 there are two new APIs, namely CGDisplayModeGetPixelWidth() and CGDisplayModeGetPixelHeight(). These always return pixels, but I still don't understand why CGDisplayModeGetWidth() and CGDisplayModeGetHeight() return pixels in the first code snippet above... is this a bug?
EDIT
Here is the output for my 1680x1050 monitor. I am using Quartz Debug to put the monitor in 840x525 screen mode to do Retina tests. You can see that the output of the first code snippet must be in pixels because it returns modes such as 1680x1050 which would correspond to 3360x2100 pixels if it were points. Another proof that the first code snippet returns pixels not points lies in the fact that the screen mode the monitor is currently in (i.e. 840x525) isn't returned at all. Only the second code snippet returns this mode.
GOT SIZE: 1680 1050
GOT SIZE: 1152 870
GOT SIZE: 1280 1024
GOT SIZE: 1024 768
GOT SIZE: 1024 768
GOT SIZE: 1024 768
GOT SIZE: 832 624
GOT SIZE: 800 600
GOT SIZE: 800 600
GOT SIZE: 800 600
GOT SIZE: 800 600
GOT SIZE: 640 480
GOT SIZE: 640 480
GOT SIZE: 640 480
GOT SIZE: 640 480
GOT SIZE: 1280 1024
GOT SIZE: 1280 960
GOT SIZE: 848 480
GOT SIZE: 1280 960
GOT SIZE: 1360 768
GOT SIZE: 800 500
GOT SIZE: 1024 640
GOT SIZE: 1280 800
GOT SIZE: 1344 1008
GOT SIZE: 1344 840
GOT SIZE: 1600 1000
--------------------------
NEW GOT SIZE: 840 525

Using SKTexture:withImageNamed in a for loop to load animations crashes after playing 30 + animation

My code below creates a Texture from an image name from file lookups in a plist like (Duck1_1,Duck1_2,Duck1_3,etc) it loops through them then adds them to a mutable array. The mutable array is then used to animate with the textures stored and played with an SKAction. This works fine for the animations until 30-40 are played then it crashes and says too many files open and it can't open the files anymore. Any ideas on how to stop this from happening?
thanks
//loadPlistForAnimationWithNames function
NSMutableArray *animationToReturn = [NSMutableArray array];
for(NSString *frameNumber in animationFrameNumbers) {
NSString *frameName = [NSString stringWithFormat:#"%#%#.png",animationFramePrefix,frameNumber];
if(frameName!=nil)
[animationToReturn addObject:[SKTexture textureWithImageNamed:frameName]];
}
SKAction *action=nil;
action=[SKAction animateWithTextures:animationToReturn timePerFrame:animationDelay resize:NO restore:NO];
SKAction *repeatAction=nil;
repeatAction = [SKAction repeatAction:action count:1];
[animationToReturn removeAllObjects];
action=nil;
animationFrameNumbers=nil;
return repeatAction;
//Call to loadPlistForAnimationWithNames and run animation
SKAction *action=[animalObject loadPlistForAnimationWithNames:#"duckAnim1" andClassName:NSStringFromClass([self class])];
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"Duck1_1"];
sprite.position=CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
sprite.size=self.frame.size;
[sprite setZPosition:3];
[self addChild:sprite];
[sprite runAction:action];
Here is the Xcode error I get when it crashes:
: ImageIO: CGImageReadCreateDataWithMappedFile 'open' failed '/private/var/mobile/Containers/Bundle/Application/782E98C3-0452-43EF-BF4D-401773D5FDCD/WildNoizesLite.app/Duck1_1.png'
error = 24 (Too many open files)
Backtrace:
* thread #1: tid = 0x301c4, 0x00000001944ab270 libsystem_kernel.dylib__pthread_kill + 8, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
frame #0: 0x00000001944ab270 libsystem_kernel.dylib__pthread_kill + 8
frame #1: 0x0000000194549170 libsystem_pthread.dylibpthread_kill + 112
frame #2: 0x0000000194422b18 libsystem_c.dylibabort + 112
frame #3: 0x00000001934b5418 libc++abi.dylibabort_message + 116
frame #4: 0x00000001934d4b8c libc++abi.dylibdefault_terminate_handler() + 304
frame #5: 0x0000000193ce83c0 libobjc.A.dylib_objc_terminate() + 128
frame #6: 0x00000001934d1bb4 libc++abi.dylibstd::__terminate(void (*)()) + 16
frame #7: 0x00000001934d173c libc++abi.dylib__cxa_rethrow + 144
frame #8: 0x0000000193ce8294 libobjc.A.dylibobjc_exception_rethrow + 44
frame #9: 0x0000000181ef5384 CoreFoundationCFRunLoopRunSpecific + 572
frame #10: 0x000000018b94b6fc GraphicsServicesGSEventRunModal + 168
frame #11: 0x0000000186af2f40 UIKitUIApplicationMain + 1488
* frame #12: 0x0000000100174fa0 Wild Noizesmain(argc=1, argv=0x000000016fd6f9d8) + 124 at main.m:16
frame #13: 0x0000000194392a08 libdyld.dylib`start + 4

App freezes when resizing window?

I'm working on a menubar application and I've made a custom (borderless) NSWindow which I've added programmatically. For some reason when I try to resize the window, it freezes and I'm unable to resize the window and open the menu-bar menu. However I can still move the window but not click things in it. The weird thing is that there is no error message at all, even though I turned on breakpoints and NSZombieMode. So I've no idea what the problem could be.
// customWindow.m
- (id)initWithContentRect:(NSRect)contentRect
styleMask:(NSUInteger)aStyle
backing:(NSBackingStoreType)bufferingType
defer:(BOOL)flag {
// Using NSBorderlessWindowMask results in a window without a title bar.
self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask | NSResizableWindowMask backing:NSBackingStoreBuffered defer:NO];
if (self != nil) {
if (![[SharedClass sharedManager] lastColor]) {
[self setBackgroundColor:[NSColor colorWithRed:(float)252/255 green:(float)249/255 blue:(float)151/255 alpha:1]];
[[SharedClass sharedManager] setLastColor:self.backgroundColor];
} else {
[self setBackgroundColor: [[SharedClass sharedManager] lastColor]];
}
[self setLevel:NSNormalWindowLevel];
// Start with no transparency for all drawing into the window
// Turn off opacity so that the parts of the window that are not drawn into are transparent.
[self setOpaque:NO];
// [self setBackgroundColor:[NSColor clearColor]];
[self setHasShadow:YES];
[self setMovableByWindowBackground:YES];
[self makeKeyAndOrderFront:theTextView];
panel = [NSColorPanel sharedColorPanel];
[panel setTarget:self];
[panel setColor:self.backgroundColor];
[panel setAction:#selector(colorUpdate:)];
[panel setShowsAlpha:YES];
[self setDelegate:self];
NSRect cFrame = [self frame];
NSView *theView = [[NSView alloc] initWithFrame:cFrame];
[self setContentView:theView];
[self setMinSize:CGSizeMake(100, 100)];
}
return self;
}
- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize {
exit.frame = NSMakeRect(2,(frameSize.height-12), 10, 10);
palette.frame = NSMakeRect((frameSize.width-24),(frameSize.height-12), 10, 10);
locker.frame = NSMakeRect((frameSize.width-12),(frameSize.height-12), 10, 10);
theTextView.frame = NSRectFromCGRect(CGRectMake(0, 8, frameSize.width, (frameSize.height-20)));
return frameSize;
}
- (void)windowDidResize:(NSNotification *)notification{
NSLog(#"resized");
}
Stack trace :
0 DDHotKey 0x00000001000044ed -[CustomWindow windowDidResize:] + 61
1 CoreFoundation 0x00007fff9034245c __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 12
2 CoreFoundation 0x00007fff90232634 _CFXNotificationPost + 3140
3 Foundation 0x00007fff8d7f59d1 -[NSNotificationCenter postNotificationName:object:userInfo:] + 66
4 AppKit 0x00007fff89117770 -[NSWindow _setFrameCommon:display:stashSize:] + 2885
5 AppKit 0x00007fff891d4b73 -[NSWindow setFrame:display:animate:] + 641
6 DDHotKey 0x000000010000a8c0 -[DDHotKeyAppDelegate setFrame:animated:] + 416
7 DDHotKey 0x00000001000091ae __53-[DDHotKeyAppDelegate applicationDidFinishLaunching:]_block_invoke + 2318
8 AppKit 0x00007fff895e5233 GlobalObserverHandler + 117
9 HIToolbox 0x00007fff88d0ab6c _ZL23DispatchEventToHandlersP14EventTargetRecP14OpaqueEventRefP14HandlerCallRec + 1260
10 HIToolbox 0x00007fff88d09fae _ZL30SendEventToEventTargetInternalP14OpaqueEventRefP20OpaqueEventTargetRefP14HandlerCallRec + 386
11 HIToolbox 0x00007fff88d09e22 SendEventToEventTargetWithOptions + 43
12 HIToolbox 0x00007fff88d46a9e _ZL29ToolboxEventDispatcherHandlerP25OpaqueEventHandlerCallRefP14OpaqueEventRefPv + 1762
13 HIToolbox 0x00007fff88d0b295 _ZL23DispatchEventToHandlersP14EventTargetRecP14OpaqueEventRefP14HandlerCallRec + 3093
14 HIToolbox 0x00007fff88d09fae _ZL30SendEventToEventTargetInternalP14OpaqueEventRefP20OpaqueEventTargetRefP14HandlerCallRec + 386
15 HIToolbox 0x00007fff88d1fcb6 SendEventToEventTarget + 40
16 AppKit 0x00007fff890d71a2 _DPSNextEvent + 3001
17 AppKit 0x00007fff890d5f68 -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 346
18 AppKit 0x00007fff890cbbf3 -[NSApplication run] + 594
19 AppKit 0x00007fff89048354 NSApplicationMain + 1832
20 DDHotKey 0x0000000100007312 main + 34
21 libdyld.dylib 0x00007fff8bd335c9 start + 1

Why is the adjustScroll: method being called twice per scroll in NSScrollView?

I have overridden the adjustScroll: method within the documentView of my NSScrollView in order to investigate how to use it to constrain large scroll movements.
What I am seeing is that for each scroll "event" prior to calling the normal scrolling notifications (which I am listening to elsewhere) it calls the adjustScroll method twice(!) and the first time it always seems to call it with a rectangle of size (1,1) - strange?
My method is as follows:
override func adjustScroll(newVisible: NSRect) -> NSRect {
println("Document: adjustScroll: \(newVisible)")
return super.adjustScroll(newVisible)
}
With the results of the println being as follows:
Document: adjustScroll: (1.0, 0.0, 1.0, 1.0)
Document: adjustScroll: (1.0, 0.0, 1436.0, 855.0)
I only have one "Document" object in my view hierarchy and there is only one scrollView...perplexed.
EDIT
I have printed out the stack within the adjustScroll call. The relevant parts are as follows for each of the two calls:
(1.0,1.0)
[0 Grid 0x000000010007d697 _TToFC4Grid8Document12adjustScrollfS0_FVSC6CGRectS1_ + 87,
1 AppKit 0x00007fff84368070 -[_NSScrollingConcurrentMainThreadSynchronizer _scrollToCononicalOrigin] + 655,
2 AppKit 0x00007fff84367a51 -[_NSScrollingConcurrentMainThreadSynchronizer _synchronize:completionHandler:] + 167,
3 AppKit 0x00007fff84367963 __80-[_NSScrollingConcurrentMainThreadSynchronizer initWithSharedData:constantData:]_block_invoke + 144,
4 libdispatch.dylib 0x000000010057ed43 _dispatch_client_callout + 8,
...
(1436.0, 855.0)
[0 Grid 0x000000010007d697 _TToFC4Grid8Document12adjustScrollfS0_FVSC6CGRectS1_ + 87,
1 AppKit 0x00007fff84206f8d -[NSClipView _scrollTo:animateScroll:flashScrollerKnobs:] + 1302,
2 AppKit 0x00007fff84368209 -[_NSScrollingConcurrentMainThreadSynchronizer _scrollToCononicalOrigin] + 1064,
3 AppKit 0x00007fff84367a51 -[_NSScrollingConcurrentMainThreadSynchronizer _synchronize:completionHandler:] + 167,
4 AppKit 0x00007fff84367963 __80-[_NSScrollingConcurrentMainThreadSynchronizer initWithSharedData:constantData:]_block_invoke + 144,
5 libdispatch.dylib 0x000000010057ed43 _dispatch_client_callout + 8,
...

Resources