summaryrefslogtreecommitdiffstats
path: root/osdep/mac/application.swift
blob: 30f37a6f4988253bfdc7045ca670121bf47dfad3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/*
 * This file is part of mpv.
 *
 * mpv is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * mpv is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
 */

import Cocoa

class Application: NSApplication, NSApplicationDelegate {
    var appHub: AppHub { get { return AppHub.shared } }
    var eventManager: NSAppleEventManager { get { return NSAppleEventManager.shared() } }
    var isBundle: Bool { get { return ProcessInfo.processInfo.environment["MPVBUNDLE"] == "true" } }
    var playbackThreadId: mp_thread!
    var argc: Int32?
    var argv: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?

    override init() {
        super.init()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func sendEvent(_ event: NSEvent) {
        if modalWindow != nil || !appHub.input.processKey(event: event) {
            super.sendEvent(event)
        }
        appHub.input.wakeup()
    }

#if HAVE_MACOS_TOUCHBAR
    override func makeTouchBar() -> NSTouchBar? {
        return appHub.touchBar
    }
#endif

    func application(_ application: NSApplication, open urls: [URL]) {
        appHub.open(urls: urls)
    }

    func applicationWillFinishLaunching(_ notification: Notification) {
        // register quit and exit events
        eventManager.setEventHandler(
            self,
            andSelector: #selector(handleQuit(event:replyEvent:)),
            forEventClass: AEEventClass(kCoreEventClass),
            andEventID: kAEQuitApplication
        )
        atexit_b({
            // clean up after exit() was called
            DispatchQueue.main.async {
                NSApp.hide(NSApp)
                NSApp.setActivationPolicy(.prohibited)
                self.eventManager.removeEventHandler(forEventClass: AEEventClass(kCoreEventClass), andEventID: kAEQuitApplication)
            }
        })
    }

    // quit from App icon, external quit from NSWorkspace
    @objc func handleQuit(event: NSAppleEventDescriptor?, replyEvent: NSAppleEventDescriptor?) {
        // send quit to core, terminates mpv_main called in playbackThread,
        if !appHub.input.command("quit") {
            appHub.log.warning("Could not properly shut down mpv")
            exit(1)
        }
    }

    func setupBundle() {
        if !isBundle { return }

        // started from finder the first argument after the binary may start with -psn_
        if CommandLine.argc > 1 && CommandLine.arguments[1].hasPrefix("-psn_") {
            argc? = 1
            argv?[1] = nil
        }

        let path = (ProcessInfo.processInfo.environment["PATH"] ?? "") +
            ":/usr/local/bin:/usr/local/sbin:/opt/local/bin:/opt/local/sbin"
        appHub.log.verbose("Setting Bundle $PATH to: \(path)")
        _ = path.withCString { setenv("PATH", $0, 1) }
    }

    let playbackThread: @convention(c) (UnsafeMutableRawPointer) -> UnsafeMutableRawPointer? = { (ptr: UnsafeMutableRawPointer) in
        let application: Application = TypeHelper.bridge(ptr: ptr)
        mp_thread_set_name("core/playback")
        let exitCode: Int32 = mpv_main(application.argc ?? 1, application.argv)
        // exit of any proper shut down
        exit(exitCode)
    }

    @objc func main(_ argc: Int32, _ argv: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>) -> Int {
        self.argc = argc
        self.argv = argv

        NSApp = self
        NSApp.delegate = self
        NSApp.setActivationPolicy(isBundle ? .regular : .accessory)
        setupBundle()
        pthread_create(&playbackThreadId, nil, playbackThread, TypeHelper.bridge(obj: self))
        appHub.input.wait()
        NSApp.run()

        // should never be reached
        print("""
            There was either a problem initializing Cocoa or the Runloop was stopped unexpectedly. \
            Please report this issues to a developer.\n
        """)
        pthread_join(playbackThreadId, nil)
        return 1
    }
}