4

I'm creating my own subclass of NSApplication, and am running into a roadblock. Here is my implementation of the run() method.

override func run() {
    finishLaunching()
    repeat {

        let event = nextEventMatchingMask(0xfffffffffffffff, untilDate: NSDate.distantPast(), inMode: NSDefaultRunLoopMode, dequeue: true)
        if event != nil { sendEvent(event!) }
        updateWindows()

    } while true

}

In my main.swift I have this:

let myApp: MyApplication = MyApplication.sharedApplication() as! MyApplication

let window = NSWindow(contentRect: NSMakeRect(0, 0, 100, 100), styleMask: NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask , backing: .Buffered, defer: false)
window.makeKeyAndOrderFront(nil)

myApp.run()

The reason why I have 0xfffffffffffffff instead of Int(NSEventMask.AnyEventMask.rawValue) is because the latter overflows when converted from UInt64 to Int.

The problem is, when I click on the red close button, the window does not close, and when I select "Quit" from the dock icon menu, it does not quit. Why does this happen?

EDIT: The same problem is discussed in this answer.

EDIT 2: I've created an objective-c version of this code, and everything works fine. I suspect the problem is that I can't use NSAnyEventMask in Swift.

Community
  • 1
  • 1
Orange Mushroom
  • 431
  • 4
  • 16

2 Answers2

1

You should only repeat while self.running. That may explain why your app doesn't quit. If your app is configured to quit when its last window closes, it may also explain why your window doesn't close. The framework may not bother to separately close the window if the whole app is going to quit, anyway.

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • The problem is, what that does is not make it run. I cannot explicitly set `running` without calling `super.run()` – Orange Mushroom Jul 07 '16 at 19:29
  • Hmm. Yeah, I see that Apple's old [GLUT sample code](https://developer.apple.com/legacy/library/samplecode/glut/Listings/GLUTApplication_m.html) resorted to directly setting the superclass's `_running` instance variable, which Swift probably won't allow. So, you could try using KVC to do it: `setValue(true, forKey:"running")`. – Ken Thomases Jul 07 '16 at 23:25
  • I've honestly never heard of that before, could you put that into a code sample? Pasting what you put doesn't work. -- Nevermind what I said, however it syill does not work. – Orange Mushroom Jul 08 '16 at 00:04
0

I seem to have solved the problem, In Objective-C I can just use this, and my app will respond to the quit item in the dock menu.

NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask 
                                    untilDate:nil 
                                       inMode:NSDefaultRunLoopMode 
                                      dequeue:YES];

In Swift if you try to get the next event like that you would do this:

let event = nextEventMatchingMask(Int(NSEventMask.AnyEventMask.rawValue),
                                  untilDate: NSDate.distantPast(), 
                                  inMode: NSDefaultRunLoopMode, 
                                  dequeue: true)

However, you get an overflow error when converting from UInt64 to Int. This seems to be unintended. At first, I tried to resolve this problem by replacing it with 0xfffffffffffffff. This worked fine, and the app would respond to events. But this was not enough, actually. The app needs to also respond to events matching a mask 0x1. I have no idea why, but this allows me to quit and hide my app from the dock menu. (0x0 only allows me to quit.)

So then, the entire run() implementation of a Swift NSApplication subclass is this:

override func run() {
    finishLaunching()
    setValue(true, forKey: "running")

    while true {
        let event = nextEventMatchingMask(0xfffffffffffffff, untilDate: NSDate.distantPast(), inMode: NSDefaultRunLoopMode, dequeue: true)
        let dockEvent = nextEventMatchingMask(0x1, untilDate: NSDate.distantPast(), inMode: NSDefaultRunLoopMode, dequeue: true)

        if dockEvent != nil { sendEvent(dockEvent!) }
        if event != nil { sendEvent(event!) }

        if !running { break }

        updateWindows()
    }
} 
Orange Mushroom
  • 431
  • 4
  • 16