summaryrefslogtreecommitdiffstats
path: root/video/out/mac/view.swift
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/mac/view.swift')
-rw-r--r--video/out/mac/view.swift297
1 files changed, 297 insertions, 0 deletions
diff --git a/video/out/mac/view.swift b/video/out/mac/view.swift
new file mode 100644
index 0000000000..a54f1da948
--- /dev/null
+++ b/video/out/mac/view.swift
@@ -0,0 +1,297 @@
+/*
+ * 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 View: NSView {
+ unowned var common: Common
+ var mpv: MPVHelper? { get { return common.mpv } }
+
+ var tracker: NSTrackingArea?
+ var hasMouseDown: Bool = false
+
+ override var isFlipped: Bool { return true }
+ override var acceptsFirstResponder: Bool { return true }
+
+
+ init(frame: NSRect, common com: Common) {
+ common = com
+ super.init(frame: frame)
+ autoresizingMask = [.width, .height]
+ wantsBestResolutionOpenGLSurface = true
+ registerForDraggedTypes([ .fileURLCompat, .URLCompat, .string ])
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ override func updateTrackingAreas() {
+ if let tracker = self.tracker {
+ removeTrackingArea(tracker)
+ }
+
+ tracker = NSTrackingArea(rect: bounds,
+ options: [.activeAlways, .mouseEnteredAndExited, .mouseMoved, .enabledDuringMouseDrag],
+ owner: self, userInfo: nil)
+ // here tracker is guaranteed to be none-nil
+ addTrackingArea(tracker!)
+
+ if containsMouseLocation() {
+ cocoa_put_key_with_modifiers(SWIFT_KEY_MOUSE_LEAVE, 0)
+ }
+ }
+
+ override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
+ guard let types = sender.draggingPasteboard.types else { return [] }
+ if types.contains(.fileURLCompat) || types.contains(.URLCompat) || types.contains(.string) {
+ return .copy
+ }
+ return []
+ }
+
+ func isURL(_ str: String) -> Bool {
+ // force unwrapping is fine here, regex is guarnteed to be valid
+ let regex = try! NSRegularExpression(pattern: "^(https?|ftp)://[^\\s/$.?#].[^\\s]*$",
+ options: .caseInsensitive)
+ let isURL = regex.numberOfMatches(in: str,
+ options: [],
+ range: NSRange(location: 0, length: str.count))
+ return isURL > 0
+ }
+
+ override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
+ let pb = sender.draggingPasteboard
+ guard let types = pb.types else { return false }
+
+ if types.contains(.fileURLCompat) || types.contains(.URLCompat) {
+ if let urls = pb.readObjects(forClasses: [NSURL.self]) as? [URL] {
+ let files = urls.map { $0.absoluteString }
+ EventsResponder.sharedInstance().handleFilesArray(files)
+ return true
+ }
+ } else if types.contains(.string) {
+ guard let str = pb.string(forType: .string) else { return false }
+ var filesArray: [String] = []
+
+ for val in str.components(separatedBy: "\n") {
+ let url = val.trimmingCharacters(in: .whitespacesAndNewlines)
+ let path = (url as NSString).expandingTildeInPath
+ if isURL(url) {
+ filesArray.append(url)
+ } else if path.starts(with: "/") {
+ filesArray.append(path)
+ }
+ }
+ EventsResponder.sharedInstance().handleFilesArray(filesArray)
+ return true
+ }
+ return false
+ }
+
+ override func acceptsFirstMouse(for event: NSEvent?) -> Bool {
+ return true
+ }
+
+ override func becomeFirstResponder() -> Bool {
+ return true
+ }
+
+ override func resignFirstResponder() -> Bool {
+ return true
+ }
+
+ override func mouseEntered(with event: NSEvent) {
+ if mpv?.mouseEnabled() ?? true {
+ cocoa_put_key_with_modifiers(SWIFT_KEY_MOUSE_ENTER, 0)
+ }
+ common.updateCursorVisibility()
+ }
+
+ override func mouseExited(with event: NSEvent) {
+ if mpv?.mouseEnabled() ?? true {
+ cocoa_put_key_with_modifiers(SWIFT_KEY_MOUSE_LEAVE, 0)
+ }
+ common.titleBar?.hide()
+ common.setCursorVisiblility(true)
+ }
+
+ override func mouseMoved(with event: NSEvent) {
+ if mpv?.mouseEnabled() ?? true {
+ signalMouseMovement(event)
+ }
+ common.titleBar?.show()
+ }
+
+ override func mouseDragged(with event: NSEvent) {
+ if mpv?.mouseEnabled() ?? true {
+ signalMouseMovement(event)
+ }
+ }
+
+ override func mouseDown(with event: NSEvent) {
+ if mpv?.mouseEnabled() ?? true {
+ signalMouseDown(event)
+ }
+ }
+
+ override func mouseUp(with event: NSEvent) {
+ if mpv?.mouseEnabled() ?? true {
+ signalMouseUp(event)
+ }
+ common.window?.isMoving = false
+ }
+
+ override func rightMouseDown(with event: NSEvent) {
+ if mpv?.mouseEnabled() ?? true {
+ signalMouseDown(event)
+ }
+ }
+
+ override func rightMouseUp(with event: NSEvent) {
+ if mpv?.mouseEnabled() ?? true {
+ signalMouseUp(event)
+ }
+ }
+
+ override func otherMouseDown(with event: NSEvent) {
+ if mpv?.mouseEnabled() ?? true {
+ signalMouseDown(event)
+ }
+ }
+
+ override func otherMouseUp(with event: NSEvent) {
+ if mpv?.mouseEnabled() ?? true {
+ signalMouseUp(event)
+ }
+ }
+
+ override func magnify(with event: NSEvent) {
+ event.phase == .ended ?
+ common.windowDidEndLiveResize() : common.windowWillStartLiveResize()
+
+ common.window?.addWindowScale(Double(event.magnification))
+ }
+
+ func signalMouseDown(_ event: NSEvent) {
+ signalMouseEvent(event, MP_KEY_STATE_DOWN)
+ if event.clickCount > 1 {
+ signalMouseEvent(event, MP_KEY_STATE_UP)
+ }
+ }
+
+ func signalMouseUp(_ event: NSEvent) {
+ signalMouseEvent(event, MP_KEY_STATE_UP)
+ }
+
+ func signalMouseEvent(_ event: NSEvent, _ state: UInt32) {
+ hasMouseDown = state == MP_KEY_STATE_DOWN
+ let mpkey = getMpvButton(event)
+ cocoa_put_key_with_modifiers((mpkey | Int32(state)), Int32(event.modifierFlags.rawValue))
+ }
+
+ func signalMouseMovement(_ event: NSEvent) {
+ var point = convert(event.locationInWindow, from: nil)
+ point = convertToBacking(point)
+ point.y = -point.y
+
+ common.window?.updateMovableBackground(point)
+ if !(common.window?.isMoving ?? false) {
+ mpv?.setMousePosition(point)
+ }
+ }
+
+ func preciseScroll(_ event: NSEvent) {
+ var delta: Double
+ var cmd: Int32
+
+ if abs(event.deltaY) >= abs(event.deltaX) {
+ delta = Double(event.deltaY) * 0.1
+ cmd = delta > 0 ? SWIFT_WHEEL_UP : SWIFT_WHEEL_DOWN
+ } else {
+ delta = Double(event.deltaX) * 0.1
+ cmd = delta > 0 ? SWIFT_WHEEL_RIGHT : SWIFT_WHEEL_LEFT
+ }
+
+ mpv?.putAxis(cmd, delta: abs(delta))
+ }
+
+ override func scrollWheel(with event: NSEvent) {
+ if !(mpv?.mouseEnabled() ?? true) {
+ return
+ }
+
+ if event.hasPreciseScrollingDeltas {
+ preciseScroll(event)
+ } else {
+ let modifiers = event.modifierFlags
+ let deltaX = modifiers.contains(.shift) ? event.scrollingDeltaY : event.scrollingDeltaX
+ let deltaY = modifiers.contains(.shift) ? event.scrollingDeltaX : event.scrollingDeltaY
+ var mpkey: Int32
+
+ if abs(deltaY) >= abs(deltaX) {
+ mpkey = deltaY > 0 ? SWIFT_WHEEL_UP : SWIFT_WHEEL_DOWN
+ } else {
+ mpkey = deltaX > 0 ? SWIFT_WHEEL_RIGHT : SWIFT_WHEEL_LEFT
+ }
+
+ cocoa_put_key_with_modifiers(mpkey, Int32(modifiers.rawValue))
+ }
+ }
+
+ func containsMouseLocation() -> Bool {
+ var topMargin: CGFloat = 0.0
+ let menuBarHeight = NSApp.mainMenu?.menuBarHeight ?? 23.0
+
+ guard let window = common.window else { return false }
+ guard var vF = window.screen?.frame else { return false }
+
+ if window.isInFullscreen && (menuBarHeight > 0) {
+ topMargin = TitleBar.height + 1 + menuBarHeight
+ }
+
+ vF.size.height -= topMargin
+
+ let vFW = window.convertFromScreen(vF)
+ let vFV = convert(vFW, from: nil)
+ let pt = convert(window.mouseLocationOutsideOfEventStream, from: nil)
+
+ var clippedBounds = bounds.intersection(vFV)
+ if !window.isInFullscreen {
+ clippedBounds.origin.y += TitleBar.height
+ clippedBounds.size.height -= TitleBar.height
+ }
+ return clippedBounds.contains(pt)
+ }
+
+ func canHideCursor() -> Bool {
+ guard let window = common.window else { return false }
+ return !hasMouseDown && containsMouseLocation() && window.isKeyWindow
+ }
+
+ func getMpvButton(_ event: NSEvent) -> Int32 {
+ let buttonNumber = event.buttonNumber
+ switch (buttonNumber) {
+ case 0: return SWIFT_MBTN_LEFT
+ case 1: return SWIFT_MBTN_RIGHT
+ case 2: return SWIFT_MBTN_MID
+ case 3: return SWIFT_MBTN_BACK
+ case 4: return SWIFT_MBTN_FORWARD
+ default: return SWIFT_MBTN9 + Int32(buttonNumber - 5)
+ }
+ }
+}