├── .github └── workflows │ └── main.yml ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── AccessibilityExt │ ├── AXError.swift │ ├── AXExt │ │ ├── AXNotification.swift │ │ ├── AXNotificationInfoKey.swift │ │ ├── AXObserver.swift │ │ ├── AXRole.swift │ │ ├── AXSubrole.swift │ │ ├── AXUIElement.swift │ │ ├── AXUIElementAction.swift │ │ ├── AXUIElementAttribute.swift │ │ └── AXValue.swift │ └── AXType.swift ├── CoreGraphicsExt │ ├── CGType.swift │ ├── Geometry │ │ ├── CATransform3D.swift │ │ ├── CGAffineTransform.swift │ │ ├── CGFloat.swift │ │ ├── CGPoint.swift │ │ ├── CGRect.swift │ │ ├── CGSize.swift │ │ └── CGVector.swift │ └── WindowServices │ │ ├── CGWindow.swift │ │ └── CGWindowInfoKeys.swift ├── CoreTextExt │ ├── CTTollFreeBridge.swift │ ├── CTType.swift │ └── CTTypeExt │ │ ├── CTAttributedStringKey.swift │ │ ├── CTFont.swift │ │ ├── CTFontAttributeKey.swift │ │ ├── CTFontCollection.swift │ │ ├── CTFontCollectionOptionKey.swift │ │ ├── CTFontManager.swift │ │ ├── CTFontNameKey.swift │ │ ├── CTFontPriority.swift │ │ ├── CTFontTraitKey.swift │ │ ├── CTFrame.swift │ │ ├── CTFrameAttributeKey.swift │ │ ├── CTFramesetter.swift │ │ ├── CTLine.swift │ │ ├── CTParagraphStyle.swift │ │ ├── CTRubyAnnotation.swift │ │ └── CTRun.swift ├── IOKitExt │ ├── HID │ │ ├── CallBack.swift │ │ ├── IOHIDExt │ │ │ ├── HIDElementKey.swift │ │ │ ├── HIDEnums.swift │ │ │ ├── HIDPage.swift │ │ │ ├── HIDPropertyKey.swift │ │ │ ├── HIDUsage.swift │ │ │ ├── IOHIDDevice.swift │ │ │ ├── IOHIDElement.swift │ │ │ ├── IOHIDManager.swift │ │ │ ├── IOHIDQueue.swift │ │ │ ├── IOHIDTransaction.swift │ │ │ └── IOHIDValue.swift │ │ └── IOHIDType.swift │ └── IOError.swift ├── ImageIOExt │ ├── ImageIOExt │ │ ├── CGImageAuxiliaryData.swift │ │ ├── CGImageDestination.swift │ │ ├── CGImageMetadata.swift │ │ ├── CGImageMetadataFormatSpecificDictionary.swift │ │ ├── CGImageMetadataTag.swift │ │ ├── CGImagePropertyName.swift │ │ ├── CGImageSource.swift │ │ └── CGImageSourceOption.swift │ └── ImageIOType.swift └── SwiftCF │ ├── CFObject.swift │ ├── CFStringKey.swift │ ├── CFType.swift │ ├── CFTypeExt │ ├── CFAllocator.swift │ ├── CFArray.swift │ ├── CFAttributedString.swift │ ├── CFAttributedStringKey.swift │ ├── CFBoolean.swift │ ├── CFData.swift │ ├── CFDictionary.swift │ ├── CFError.swift │ ├── CFNumber.swift │ ├── CFRange.swift │ ├── CFRunLoop.swift │ ├── CFString.swift │ ├── CFStringTokenizer.swift │ └── CFStringTransformer.swift │ ├── Cast │ ├── CFBridge.swift │ ├── CFCast.swift │ ├── _CFConvertible.swift │ └── _CFTollFreeBridgeable.swift │ ├── Mach │ └── KernelError.swift │ └── Utilities.swift └── Tests ├── LinuxMain.swift └── SwiftCFTests ├── SwiftCFTests.swift └── TollFreeBridgingTests.swift /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | 7 | macOS: 8 | runs-on: macOS-latest 9 | steps: 10 | - uses: actions/checkout@v1 11 | - run: swift test 12 | 13 | iOS: 14 | runs-on: macOS-latest 15 | steps: 16 | - uses: actions/checkout@v1 17 | - run: | 18 | set -o pipefail 19 | xcodebuild test \ 20 | -scheme SwiftCF \ 21 | -destination "platform=iOS Simulator,name=iPhone 12" | xcpretty 22 | 23 | Linux: 24 | strategy: 25 | matrix: 26 | swift_version: ['5.1', '5.2', '5.3'] 27 | runs-on: ubuntu-latest 28 | container: 29 | image: swift:${{ matrix.swift_version }} 30 | steps: 31 | - uses: actions/checkout@v1 32 | - run: swift build 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2019 Xander Deng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "SwiftCF", 7 | products: [ 8 | .library( 9 | name: "SwiftCF", 10 | targets: ["SwiftCF", "CoreGraphicsExt", "CoreTextExt", "IOKitExt", "ImageIOExt", "AccessibilityExt"]), 11 | ], 12 | targets: [ 13 | .target(name: "SwiftCF"), 14 | .target(name: "CoreGraphicsExt", dependencies: ["SwiftCF"]), 15 | .target(name: "CoreTextExt", dependencies: ["SwiftCF"]), 16 | .target(name: "IOKitExt", dependencies: ["SwiftCF"]), 17 | .target(name: "ImageIOExt", dependencies: ["SwiftCF"]), 18 | .target(name: "AccessibilityExt", dependencies: ["SwiftCF"]), 19 | .testTarget(name: "SwiftCFTests", dependencies: ["SwiftCF"]), 20 | ] 21 | ) 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftCF 2 | 3 | [![Github CI Status](https://github.com/ddddxxx/SwiftCF/workflows/CI/badge.svg)](https://github.com/ddddxxx/SwiftCF/actions) 4 | 5 | Using CoreFoundation based framework in Swift painlessly. 6 | 7 | Wrapped frameworks: 8 | 9 | - [x] CoreFoundation 10 | - [x] CoreGraphics 11 | - [x] CoreText 12 | - [x] ImageIO 13 | - [x] IOKit 14 | - [x] HID 15 | - [x] ApplicationServices 16 | - [x] Accessibility (`AXUIElement`) 17 | - [ ] CoreMedia 18 | - [ ] CoreVideo 19 | - [ ] CFNetwork 20 | - [ ] Security 21 | - [ ] CoreServices 22 | - [ ] VideoToolbox 23 | - [ ] SystemConfiguration 24 | 25 | ## Usage 26 | 27 | ### Dynamic casting to CoreFoundation types via the type-id mechanism. 28 | 29 | ```swift 30 | import SwiftCF 31 | 32 | let str: Any = "foo" as CFString 33 | 34 | let s0 = str as? CFString 35 | // ❗️ Conditional downcast to CoreFoundation type 'CFString' will always succeed 36 | 37 | let s1: CFString = cfCast(str) // or 38 | let s2 = cfCast(str, to: CFString.self) // or 39 | let s3 = CFString.cast(str) 40 | // ✅ 41 | ``` 42 | 43 | ### Toll-Free-Bridging 44 | 45 | ```swift 46 | let cfStr: CFString = .from("foo") 47 | let nsStr: NSString = cfStr.asNS() 48 | let str: String = cfStr.asSwift() 49 | ``` 50 | 51 | ### Convenient Extensions 52 | 53 | ```swift 54 | let str = "foo" as CFString 55 | 56 | str.length 57 | // Replace CFStringGetLength(str) 58 | 59 | CFStringTokenizer.create(string: str) 60 | // Replace 😱 CFStringTokenizerCreate(kCFAllocatorDefault, str, CFRange(location: 0, length: CFStringGetLength(str)), kCFStringTokenizerUnitWord, CFLocaleGetSystem()) 61 | ``` 62 | 63 | ### Swift Protocol Conformance 64 | 65 | ```swift 66 | let arr = [1, 3.14, "foo"] as CFArray 67 | 68 | for item in arr { 69 | // 😃 CFArray now conforms to RandomAccessCollection. 70 | } 71 | ``` 72 | 73 | ## Requirements 74 | 75 | - Swift 5.0+ 76 | 77 | ## License 78 | 79 | SwiftCF is available under the MIT license. See the [LICENSE file](LICENSE). 80 | -------------------------------------------------------------------------------- /Sources/AccessibilityExt/AXError.swift: -------------------------------------------------------------------------------- 1 | #if canImport(ApplicationServices) 2 | 3 | import ApplicationServices 4 | 5 | extension AXError: Error {} 6 | 7 | extension AXError { 8 | 9 | @usableFromInline func throwIfError() throws { 10 | if self != .success { 11 | throw self 12 | } 13 | } 14 | } 15 | 16 | #endif // canImport(ApplicationServices) 17 | -------------------------------------------------------------------------------- /Sources/AccessibilityExt/AXExt/AXNotificationInfoKey.swift: -------------------------------------------------------------------------------- 1 | #if canImport(ApplicationServices) 2 | 3 | import SwiftCF 4 | import ApplicationServices 5 | 6 | extension AXNotification { 7 | 8 | public struct InfoKey: CFStringKey { 9 | 10 | public let rawValue: CFString 11 | 12 | public init(_ key: CFString) { 13 | rawValue = key 14 | } 15 | } 16 | } 17 | 18 | private func key(_ key: String) -> AXNotification.InfoKey { 19 | return key as CFString as AXNotification.InfoKey 20 | } 21 | 22 | public extension AXNotification.InfoKey { 23 | /// Notification info key used to specify an element. 24 | static let uiElements = key(kAXUIElementsKey) 25 | /// Notification info key used to specify a priority for the notification. See AXPriority. 26 | static let priority = key(kAXPriorityKey) 27 | /// Notification info key used to specify an announcement to be spoken with a notification. 28 | static let announcement = key(kAXAnnouncementKey) 29 | /// Notification info key used to specify the title of an element to be spoken with a notification. 30 | static let uiElementTitle = key(kAXUIElementTitleKey) 31 | } 32 | 33 | #endif // canImport(ApplicationServices) 34 | -------------------------------------------------------------------------------- /Sources/AccessibilityExt/AXExt/AXObserver.swift: -------------------------------------------------------------------------------- 1 | #if canImport(ApplicationServices) 2 | 3 | import SwiftCF 4 | import ApplicationServices 5 | 6 | public extension AXObserver { 7 | 8 | /// Creates a new observer that can receive notifications from the specified 9 | /// application. 10 | /// 11 | /// When an observed notification is received, it is passed to 12 | /// AXObserverCallback. 13 | /// 14 | /// If unsuccessful, AXObserverCreate may throw one of the following error 15 | /// codes, among others: 16 | /// 17 | /// - kAXErrorIllegalArgument: One or more of the arguments is an illegal 18 | /// value. 19 | /// - kAXErrorFailure: There is some sort of system memory failure. 20 | /// 21 | /// - Parameters: 22 | /// - application: The process ID of the application. 23 | /// - callback: The callback function. 24 | /// - Throws: Throws AXError if failed. 25 | /// - Returns: An AXObserverRef representing the observer object. 26 | @inlinable static func create(application: pid_t, callback: @escaping AXObserverCallback) throws -> AXObserver { 27 | var result: AXObserver? 28 | try AXObserverCreate(application, callback, &result).throwIfError() 29 | return result! 30 | } 31 | 32 | /// Creates a new observer that can receive notifications with an 33 | /// information dictionary from the specified application. 34 | /// 35 | /// When an observed notification is received, it is passed to 36 | /// AXObserverCallbackWithInfo. 37 | /// 38 | /// If unsuccessful, AXObserverCreateWithInfoCallback may throw one of the 39 | /// following error codes, among others: 40 | /// 41 | /// - kAXErrorIllegalArgument: One or more of the arguments is an illegal 42 | /// value. 43 | /// - kAXErrorFailure: There is some sort of system memory failure. 44 | /// 45 | /// - Parameters: 46 | /// - application: The process ID of the application. 47 | /// - callback: The callback function. 48 | /// - Throws: Throws AXError if failed. 49 | /// - Returns: An AXObserverRef representing the observer object. 50 | @inlinable static func create(application: pid_t, callback: @escaping AXObserverCallbackWithInfo) throws -> AXObserver { 51 | var result: AXObserver? 52 | try AXObserverCreateWithInfoCallback(application, callback, &result).throwIfError() 53 | return result! 54 | } 55 | 56 | /// Registers the specified observer to receive notifications from the 57 | /// specified accessibility object. 58 | /// 59 | /// If unsuccessful, AXObserverAddNotification may throw one of the 60 | /// following error codes, among others: 61 | /// 62 | /// - kAXErrorInvalidUIElementObserver: The observer is not a valid 63 | /// AXObserverRef type. 64 | /// - kAXErrorIllegalArgument: One or more of the arguments is an illegal 65 | /// value or the length of the notification name is greater than 1024. 66 | /// - kAXErrorNotificationUnsupported: The accessibility object does not 67 | /// support notifications (note that the system-wide accessibility object 68 | /// does not support notifications). 69 | /// - kAXErrorNotificationAlreadyRegistered: The notification has already 70 | /// been registered. 71 | /// - kAXErrorCannotComplete: The function cannot complete because messaging 72 | /// has failed in some way. 73 | /// - kAXErrorFailure: There is some sort of system memory failure. 74 | /// 75 | /// - Parameters: 76 | /// - notification: The name of the notification to observe. 77 | /// - element: The accessibility object for which to observe notifications. 78 | /// - refcon: Application-defined data passed to the callback when it is called. 79 | /// - Throws: Throws AXError if failed. 80 | @inlinable func addNotification(_ notification: AXNotification, element: AXUIElement, refcon: UnsafeMutableRawPointer?) throws { 81 | try AXObserverAddNotification(self, element, notification as CFString, refcon).throwIfError() 82 | } 83 | 84 | /// Removes the specified notification from the list of notifications the 85 | /// observer wants to receive from the accessibility object. 86 | /// 87 | /// If unsuccessful, AXObserverRemoveNotification may throw one of the 88 | /// following error codes, among others: 89 | /// 90 | /// - kAXErrorInvalidUIElementObserver: The observer is not a valid 91 | /// AXObserverRef type. 92 | /// - kAXErrorIllegalArgument: One or more of the arguments is an illegal 93 | /// value or the length of the notification name is greater than 1024. 94 | /// - kAXErrorNotificationUnsupported: The accessibility object does not 95 | /// support notifications (note that the system-wide accessibility object 96 | /// does not support notifications). 97 | /// - kAXErrorNotificationNotRegistered: This observer has not registered 98 | /// for any notifications. 99 | /// - kAXErrorCannotComplete: The function cannot complete because messaging 100 | /// has failed in some way. 101 | /// - kAXErrorFailure: There is some sort of system memory failure. 102 | /// 103 | /// - Parameters: 104 | /// - notification: The name of the notification to remove from the list 105 | /// of observed notifications. 106 | /// - element: The accessibility object for which this observer observes 107 | /// notifications. 108 | /// - Throws: Throws AXError if failed. 109 | @inlinable func removeNotification(_ notification: AXNotification, element: AXUIElement) throws { 110 | try AXObserverRemoveNotification(self, element, notification as CFString).throwIfError() 111 | } 112 | 113 | /// Returns the observer's run loop source. 114 | /// 115 | /// The observer must be added to a run loop before it can receive 116 | /// notifications. Note that releasing the AXObserverRef automatically 117 | /// removes the run loop source from the run loop (you can also do this 118 | /// explicitly by calling CFRunLoopRemoveSource(_:_:_:)). 119 | /// 120 | /// runLoopSource might be used in code in this way: 121 | /// 122 | /// CFRunLoopAddSource(CFRunLoopGetCurrent(), observer.runLoopSource, kCFRunLoopDefaultMode); 123 | @inlinable var runLoopSource: CFRunLoopSource { 124 | return AXObserverGetRunLoopSource(self) 125 | } 126 | } 127 | 128 | #endif // canImport(ApplicationServices) 129 | -------------------------------------------------------------------------------- /Sources/AccessibilityExt/AXExt/AXRole.swift: -------------------------------------------------------------------------------- 1 | #if canImport(ApplicationServices) 2 | 3 | import SwiftCF 4 | import ApplicationServices 5 | 6 | public struct AXRole: CFStringKey { 7 | 8 | public let rawValue: CFString 9 | 10 | public init(_ key: CFString) { 11 | rawValue = key 12 | } 13 | } 14 | 15 | private func role(_ key: String) -> AXRole { 16 | return key as CFString as AXRole 17 | } 18 | 19 | public extension AXRole { 20 | static let application = role(kAXApplicationRole) 21 | static let systemWide = role(kAXSystemWideRole) 22 | static let window = role(kAXWindowRole) 23 | static let sheet = role(kAXSheetRole) 24 | static let drawer = role(kAXDrawerRole) 25 | static let growArea = role(kAXGrowAreaRole) 26 | static let image = role(kAXImageRole) 27 | static let unknown = role(kAXUnknownRole) 28 | static let button = role(kAXButtonRole) 29 | static let radioButton = role(kAXRadioButtonRole) 30 | static let checkBox = role(kAXCheckBoxRole) 31 | static let popUpButton = role(kAXPopUpButtonRole) 32 | static let menuButton = role(kAXMenuButtonRole) 33 | static let tabGroup = role(kAXTabGroupRole) 34 | static let table = role(kAXTableRole) 35 | static let column = role(kAXColumnRole) 36 | static let row = role(kAXRowRole) 37 | static let outline = role(kAXOutlineRole) 38 | static let browser = role(kAXBrowserRole) 39 | static let scrollArea = role(kAXScrollAreaRole) 40 | static let scrollBar = role(kAXScrollBarRole) 41 | static let radioGroup = role(kAXRadioGroupRole) 42 | static let list = role(kAXListRole) 43 | static let group = role(kAXGroupRole) 44 | static let valueIndicator = role(kAXValueIndicatorRole) 45 | static let comboBox = role(kAXComboBoxRole) 46 | static let slider = role(kAXSliderRole) 47 | static let incrementor = role(kAXIncrementorRole) 48 | static let busyIndicator = role(kAXBusyIndicatorRole) 49 | static let progressIndicator = role(kAXProgressIndicatorRole) 50 | static let relevanceIndicator = role(kAXRelevanceIndicatorRole) 51 | static let toolbar = role(kAXToolbarRole) 52 | static let disclosureTriangle = role(kAXDisclosureTriangleRole) 53 | static let textField = role(kAXTextFieldRole) 54 | static let textArea = role(kAXTextAreaRole) 55 | static let staticText = role(kAXStaticTextRole) 56 | static let menuBar = role(kAXMenuBarRole) 57 | static let menuBarItem = role(kAXMenuBarItemRole) 58 | static let menu = role(kAXMenuRole) 59 | static let menuItem = role(kAXMenuItemRole) 60 | static let splitGroup = role(kAXSplitGroupRole) 61 | static let splitter = role(kAXSplitterRole) 62 | static let colorWell = role(kAXColorWellRole) 63 | static let timeField = role(kAXTimeFieldRole) 64 | static let dateField = role(kAXDateFieldRole) 65 | static let helpTag = role(kAXHelpTagRole) 66 | static let matte = role(kAXMatteRole) 67 | static let dockItem = role(kAXDockItemRole) 68 | static let ruler = role(kAXRulerRole) 69 | static let rulerMarker = role(kAXRulerMarkerRole) 70 | static let grid = role(kAXGridRole) 71 | static let levelIndicator = role(kAXLevelIndicatorRole) 72 | static let cell = role(kAXCellRole) 73 | static let layoutArea = role(kAXLayoutAreaRole) 74 | static let layoutItem = role(kAXLayoutItemRole) 75 | static let handle = role(kAXHandleRole) 76 | static let popover = role(kAXPopoverRole) 77 | } 78 | 79 | #endif // canImport(ApplicationServices) 80 | -------------------------------------------------------------------------------- /Sources/AccessibilityExt/AXExt/AXSubrole.swift: -------------------------------------------------------------------------------- 1 | #if canImport(ApplicationServices) 2 | 3 | import SwiftCF 4 | import ApplicationServices 5 | 6 | public struct AXSubrole: CFStringKey { 7 | 8 | public let rawValue: CFString 9 | 10 | public init(_ key: CFString) { 11 | rawValue = key 12 | } 13 | } 14 | 15 | private func subrole(_ key: String) -> AXSubrole { 16 | return key as CFString as AXSubrole 17 | } 18 | 19 | /// standard subroles 20 | public extension AXSubrole { 21 | static let closeButton = subrole(kAXCloseButtonSubrole) 22 | static let minimizeButton = subrole(kAXMinimizeButtonSubrole) 23 | static let zoomButton = subrole(kAXZoomButtonSubrole) 24 | static let toolbarButton = subrole(kAXToolbarButtonSubrole) 25 | static let fullScreenButton = subrole(kAXFullScreenButtonSubrole) 26 | static let secureTextField = subrole(kAXSecureTextFieldSubrole) 27 | static let tableRow = subrole(kAXTableRowSubrole) 28 | static let outlineRow = subrole(kAXOutlineRowSubrole) 29 | static let unknown = subrole(kAXUnknownSubrole) 30 | } 31 | /// new subroles 32 | public extension AXSubrole { 33 | static let standardWindow = subrole(kAXStandardWindowSubrole) 34 | static let dialog = subrole(kAXDialogSubrole) 35 | static let systemDialog = subrole(kAXSystemDialogSubrole) 36 | static let floatingWindow = subrole(kAXFloatingWindowSubrole) 37 | static let systemFloatingWindow = subrole(kAXSystemFloatingWindowSubrole) 38 | static let decorative = subrole(kAXDecorativeSubrole) 39 | static let incrementArrow = subrole(kAXIncrementArrowSubrole) 40 | static let decrementArrow = subrole(kAXDecrementArrowSubrole) 41 | static let incrementPage = subrole(kAXIncrementPageSubrole) 42 | static let decrementPage = subrole(kAXDecrementPageSubrole) 43 | static let sortButton = subrole(kAXSortButtonSubrole) 44 | static let searchField = subrole(kAXSearchFieldSubrole) 45 | static let timeline = subrole(kAXTimelineSubrole) 46 | static let ratingIndicator = subrole(kAXRatingIndicatorSubrole) 47 | static let contentList = subrole(kAXContentListSubrole) 48 | /// superceded by kAXDescriptionListSubrole in OS X 10.9 49 | static let definitionList = subrole(kAXDefinitionListSubrole) 50 | /// OS X 10.9 and later 51 | static let descriptionList = subrole(kAXDescriptionListSubrole) 52 | static let toggle = subrole(kAXToggleSubrole) 53 | static let `switch` = subrole(kAXSwitchSubrole) 54 | } 55 | 56 | /// dock subroles 57 | public extension AXSubrole { 58 | static let applicationDockItem = subrole(kAXApplicationDockItemSubrole) 59 | static let documentDockItem = subrole(kAXDocumentDockItemSubrole) 60 | static let folderDockItem = subrole(kAXFolderDockItemSubrole) 61 | static let minimizedWindowDockItem = subrole(kAXMinimizedWindowDockItemSubrole) 62 | static let uRLDockItem = subrole(kAXURLDockItemSubrole) 63 | static let dockExtraDockItem = subrole(kAXDockExtraDockItemSubrole) 64 | static let trashDockItem = subrole(kAXTrashDockItemSubrole) 65 | static let separatorDockItem = subrole(kAXSeparatorDockItemSubrole) 66 | static let processSwitcherList = subrole(kAXProcessSwitcherListSubrole) 67 | } 68 | 69 | #endif // canImport(ApplicationServices) 70 | -------------------------------------------------------------------------------- /Sources/AccessibilityExt/AXExt/AXUIElementAction.swift: -------------------------------------------------------------------------------- 1 | #if canImport(ApplicationServices) 2 | 3 | import SwiftCF 4 | import ApplicationServices 5 | 6 | extension AXUIElement { 7 | 8 | /// Many UIElements have a set of actions that they can perform. Actions are 9 | /// designed to be simple. Actions roughly correspond to things you could do 10 | /// with a single click of the mouse on the UIElement. Buttons and menu 11 | /// items, for example, have a single action: push or pick, respectively. A 12 | /// scroll bar has several actions: page up, page down, up one line, down 13 | /// one line. 14 | public struct Action: CFStringKey { 15 | 16 | public let rawValue: CFString 17 | 18 | public init(_ key: CFString) { 19 | rawValue = key 20 | } 21 | } 22 | } 23 | 24 | private func action(_ key: String) -> AXUIElement.Action { 25 | return key as CFString as AXUIElement.Action 26 | } 27 | 28 | /// Standard Actions 29 | public extension AXUIElement.Action { 30 | 31 | /// Simulate clicking the UIElement, such as a button. 32 | static let press = action(kAXPressAction) 33 | 34 | /// Increment the value of the UIElement. 35 | static let increment = action(kAXIncrementAction) 36 | 37 | /// Decrement the value of the UIElement. 38 | static let decrement = action(kAXDecrementAction) 39 | 40 | /// Simulate pressing Return in the UIElement, such as a text field. 41 | /// 42 | /// Don't know if this is still correct. Is this what used to be kAXAcceptAction? 43 | static let confirm = action(kAXConfirmAction) 44 | 45 | /// Simulate a Cancel action, such as hitting the Cancel button. 46 | static let cancel = action(kAXCancelAction) 47 | 48 | /// Show alternate or hidden UI. 49 | /// 50 | /// This is often used to trigger the same change that would occur on a mouse hover. 51 | static let showAlternateUI = action(kAXShowAlternateUIAction) 52 | 53 | /// Show default UI. 54 | /// 55 | /// This is often used to trigger the same change that would occur when a mouse hover ends. 56 | static let showDefaultUI = action(kAXShowDefaultUIAction) 57 | } 58 | 59 | /// New Actions 60 | public extension AXUIElement.Action { 61 | static let raise = action(kAXRaiseAction) 62 | static let showMenu = action(kAXShowMenuAction) 63 | } 64 | 65 | /// Obsolete Actions 66 | public extension AXUIElement.Action { 67 | /// Select the UIElement, such as a menu item. 68 | static let pick = action(kAXPickAction) 69 | } 70 | 71 | #endif // canImport(ApplicationServices) 72 | -------------------------------------------------------------------------------- /Sources/AccessibilityExt/AXExt/AXValue.swift: -------------------------------------------------------------------------------- 1 | #if canImport(ApplicationServices) 2 | 3 | import ApplicationServices 4 | 5 | public extension AXValue { 6 | 7 | /// Encodes a structure pointed to by valuePtr into a CFTypeRef. 8 | @inlinable static func create(type: AXValueType, value: Any) -> AXValue? { 9 | var value = value 10 | return AXValueCreate(type, &value) 11 | } 12 | 13 | /// Returns the structure type encoded in value. If the type is not 14 | /// recognized, it returns kAXValueIllegalType. 15 | @inlinable var type: AXValueType { 16 | return AXValueGetType(self) 17 | } 18 | 19 | /// Decodes the structure stored in value. If the type is not recognized, 20 | /// nil is returned. 21 | @inlinable var value: Any? { 22 | let type = self.type 23 | switch type { 24 | case .cgPoint, AXValueType(rawValue: kAXValueCGPointType)!: 25 | var value = CGPoint() 26 | let success = AXValueGetValue(self, type, &value) 27 | precondition(success) 28 | return value 29 | case .cgSize, AXValueType(rawValue: kAXValueCGSizeType)!: 30 | var value = CGSize() 31 | let success = AXValueGetValue(self, type, &value) 32 | precondition(success) 33 | return value 34 | case .cgRect, AXValueType(rawValue: kAXValueCGRectType)!: 35 | var value = CGRect() 36 | let success = AXValueGetValue(self, type, &value) 37 | precondition(success) 38 | return value 39 | case .cfRange, AXValueType(rawValue: kAXValueCFRangeType)!: 40 | var value = CFRange() 41 | let success = AXValueGetValue(self, type, &value) 42 | precondition(success) 43 | return value 44 | case .axError, AXValueType(rawValue: kAXValueAXErrorType)!: 45 | var value = AXError.success 46 | let success = AXValueGetValue(self, type, &value) 47 | precondition(success) 48 | return value 49 | case .illegal, AXValueType(rawValue: kAXValueIllegalType)!: 50 | return nil 51 | @unknown default: 52 | return nil 53 | } 54 | 55 | } 56 | } 57 | 58 | @available(macOS 10.11, *) 59 | public extension AXValue { 60 | 61 | @inlinable static func cgPoint(_ value: CGPoint) -> AXValue { 62 | return AXValue.create(type: .cgPoint, value: value)! 63 | } 64 | 65 | @inlinable static func cgSize(_ value: CGSize) -> AXValue { 66 | return AXValue.create(type: .cgSize, value: value)! 67 | } 68 | 69 | @inlinable static func cgRect(_ value: CGRect) -> AXValue { 70 | return AXValue.create(type: .cgRect, value: value)! 71 | } 72 | 73 | @inlinable static func cfRange(_ value: CFRange) -> AXValue { 74 | return AXValue.create(type: .cfRange, value: value)! 75 | } 76 | 77 | @inlinable static func axError(_ value: AXError) -> AXValue { 78 | return AXValue.create(type: .axError, value: value)! 79 | } 80 | } 81 | 82 | #endif // canImport(ApplicationServices) 83 | -------------------------------------------------------------------------------- /Sources/AccessibilityExt/AXType.swift: -------------------------------------------------------------------------------- 1 | #if canImport(ApplicationServices) 2 | 3 | import SwiftCF 4 | import ApplicationServices 5 | 6 | extension AXUIElement: CFType { 7 | 8 | public static let typeID = AXUIElementGetTypeID() 9 | } 10 | 11 | extension AXObserver: CFType { 12 | 13 | public static let typeID = AXObserverGetTypeID() 14 | } 15 | 16 | extension AXValue: CFType { 17 | 18 | public static let typeID = AXValueGetTypeID() 19 | } 20 | 21 | #endif // canImport(ApplicationServices) 22 | -------------------------------------------------------------------------------- /Sources/CoreGraphicsExt/CGType.swift: -------------------------------------------------------------------------------- 1 | #if canImport(CoreGraphics) 2 | 3 | import SwiftCF 4 | import CoreGraphics 5 | 6 | extension CGColor: CFType {} 7 | extension CGColorConversionInfo: CFType {} 8 | extension CGColorSpace: CFType {} 9 | extension CGContext: CFType {} 10 | extension CGDataConsumer: CFType {} 11 | extension CGDataProvider: CFType {} 12 | extension CGFont: CFType {} 13 | extension CGFunction: CFType {} 14 | extension CGImage: CFType {} 15 | extension CGLayer: CFType {} 16 | extension CGPath: CFType {} 17 | extension CGPattern: CFType {} 18 | extension CGPDFDocument: CFType {} 19 | extension CGPDFPage: CFType {} 20 | extension CGShading: CFType {} 21 | 22 | #if os(macOS) 23 | 24 | extension CGDisplayMode: CFType {} 25 | extension CGDisplayStream: CFType {} 26 | extension CGEvent: CFType {} 27 | extension CGEventSource: CFType {} 28 | extension CGPSConverter: CFType {} 29 | 30 | #endif // os(macOS) 31 | 32 | #endif // canImport(CoreGraphics) 33 | -------------------------------------------------------------------------------- /Sources/CoreGraphicsExt/Geometry/CATransform3D.swift: -------------------------------------------------------------------------------- 1 | #if canImport(QuartzCore) 2 | 3 | import QuartzCore 4 | 5 | public extension CATransform3D { 6 | 7 | @inlinable var cameraDistance: CGFloat { 8 | get { 9 | return -1 / m34 10 | } 11 | set { 12 | m34 = -1 / newValue 13 | } 14 | } 15 | 16 | @inlinable var isIdentity: Bool { 17 | return CATransform3DIsIdentity(self) 18 | } 19 | 20 | // MARK: create 21 | 22 | @inlinable init() { 23 | self = .identity 24 | } 25 | 26 | @inlinable static func translation(x tx: CGFloat = 0, y ty: CGFloat = 0, z tz: CGFloat = 0) -> CATransform3D { 27 | return CATransform3DMakeTranslation(tx, ty, tz) 28 | } 29 | 30 | @inlinable static func scale(x sx: CGFloat = 1, y sy: CGFloat = 1, z sz: CGFloat = 1) -> CATransform3D { 31 | return CATransform3DMakeScale(sx, sy, sz) 32 | } 33 | 34 | @inlinable static func rotation(angle: CGFloat, x: CGFloat = 0, y: CGFloat = 0, z: CGFloat = 0) -> CATransform3D { 35 | return CATransform3DMakeRotation(angle, x, y, z) 36 | } 37 | 38 | @inlinable static var identity: CATransform3D { 39 | return CATransform3DIdentity 40 | } 41 | 42 | // MARK: affine 43 | 44 | @inlinable init(_ affineTransform: CGAffineTransform) { 45 | self = CATransform3DMakeAffineTransform(affineTransform) 46 | } 47 | 48 | @inlinable var isAffine: Bool { 49 | return CATransform3DIsAffine(self) 50 | } 51 | 52 | @inlinable var affineTransform: CGAffineTransform { 53 | return CATransform3DGetAffineTransform(self) 54 | } 55 | 56 | // MARK: transform 57 | 58 | @inlinable var inverse: CATransform3D { 59 | return CATransform3DInvert(self) 60 | } 61 | 62 | @inlinable func translatedBy(x tx: CGFloat = 0, y ty: CGFloat = 0, z tz: CGFloat = 0) -> CATransform3D { 63 | return CATransform3DTranslate(self, tx, ty, tz) 64 | } 65 | 66 | @inlinable func scaledBy(x sx: CGFloat = 1, y sy: CGFloat = 1, z sz: CGFloat = 1) -> CATransform3D { 67 | return CATransform3DScale(self, sx, sy, sz) 68 | } 69 | 70 | @inlinable func rotatedBy(angle: CGFloat, x: CGFloat = 0, y: CGFloat = 0, z: CGFloat = 0) -> CATransform3D { 71 | return CATransform3DRotate(self, angle, x, y, z) 72 | } 73 | 74 | @inlinable func transformed(by t2: CATransform3D) -> CATransform3D { 75 | return CATransform3DConcat(t2, self) 76 | } 77 | 78 | @inlinable func concatenating(_ t2: CATransform3D) -> CATransform3D { 79 | return CATransform3DConcat(self, t2) 80 | } 81 | 82 | // MARK: mutate 83 | 84 | @inlinable mutating func invert() { 85 | self = inverse 86 | } 87 | 88 | @inlinable mutating func scaleBy(x sx: CGFloat = 1, y sy: CGFloat = 1, z sz: CGFloat = 1) { 89 | self = scaledBy(x: sx, y: sy, z: sz) 90 | } 91 | 92 | @inlinable mutating func rotateBy(angle: CGFloat, x: CGFloat, y: CGFloat, z: CGFloat) { 93 | self = rotatedBy(angle: angle, x: x, y: y, z: z) 94 | } 95 | 96 | @inlinable mutating func transforme(by t2: CATransform3D) { 97 | self = transformed(by: t2) 98 | } 99 | 100 | @inlinable mutating func translateBy(x tx: CGFloat = 0, y ty: CGFloat = 0, z tz: CGFloat = 0) { 101 | self = translatedBy(x: tx, y: ty, z: tz) 102 | } 103 | 104 | @inlinable mutating func concat(_ t2: CATransform3D) { 105 | self = concatenating(t2) 106 | } 107 | 108 | // MARK: operator 109 | 110 | @inlinable static func *(lhs: CATransform3D, rhs: CATransform3D) -> CATransform3D { 111 | return lhs.concatenating(rhs) 112 | } 113 | 114 | @inlinable static func *=(lhs: inout CATransform3D, rhs: CATransform3D) { 115 | lhs = lhs * rhs 116 | } 117 | 118 | @inlinable static func /(lhs: CATransform3D, rhs: CATransform3D) -> CATransform3D { 119 | return lhs.concatenating(rhs.inverse) 120 | } 121 | 122 | @inlinable static func /=(lhs: inout CATransform3D, rhs: CATransform3D) { 123 | lhs = lhs / rhs 124 | } 125 | } 126 | 127 | extension CATransform3D: Equatable { 128 | 129 | @inlinable public static func == (lhs: CATransform3D, rhs: CATransform3D) -> Bool { 130 | return CATransform3DEqualToTransform(lhs, rhs) 131 | } 132 | } 133 | 134 | #endif // canImport(QuartzCore) 135 | -------------------------------------------------------------------------------- /Sources/CoreGraphicsExt/Geometry/CGAffineTransform.swift: -------------------------------------------------------------------------------- 1 | #if canImport(CoreGraphics) 2 | 3 | import CoreGraphics 4 | 5 | public extension CGAffineTransform { 6 | 7 | // MARK: creat 8 | 9 | @inlinable init() { 10 | self = .identity 11 | } 12 | 13 | @inlinable static func translate(x: CGFloat = 0, y: CGFloat = 0) -> CGAffineTransform { 14 | return CGAffineTransform(translationX: x, y: y) 15 | } 16 | 17 | @inlinable static func scale(x: CGFloat = 1, y: CGFloat = 1) -> CGAffineTransform { 18 | return CGAffineTransform(scaleX: x, y: y) 19 | } 20 | 21 | @inlinable static func rotate(_ angle: CGFloat) -> CGAffineTransform { 22 | return CGAffineTransform(rotationAngle: angle) 23 | } 24 | 25 | @inlinable static func flip(height: CGFloat) -> CGAffineTransform { 26 | return CGAffineTransform(translationX: 0, y: height).scaledBy(x: 1, y: -1) 27 | } 28 | 29 | @inlinable static func swap() -> CGAffineTransform { 30 | return CGAffineTransform(scaleX: -1, y: 1).rotated(by: .pi / 2) 31 | } 32 | 33 | // MARK: mutate 34 | 35 | @inlinable func transformed(by t2: CGAffineTransform) -> CGAffineTransform { 36 | return t2.concatenating(self) 37 | } 38 | 39 | @inlinable mutating func invert() { 40 | self = inverted() 41 | } 42 | 43 | @inlinable mutating func transform(by t2: CGAffineTransform) { 44 | self = transformed(by: t2) 45 | } 46 | 47 | @inlinable mutating func translateBy(x: CGFloat = 0, y: CGFloat = 0) { 48 | self = translatedBy(x: x, y: y) 49 | } 50 | 51 | @inlinable mutating func scaleBy(x: CGFloat = 1, y: CGFloat = 1) { 52 | self = scaledBy(x: x, y: y) 53 | } 54 | 55 | @inlinable mutating func rotate(by angle: CGFloat) { 56 | self = rotated(by: angle) 57 | } 58 | 59 | // MARK: operator 60 | 61 | @inlinable static func *(lhs: CGAffineTransform, rhs: CGAffineTransform) -> CGAffineTransform { 62 | return lhs.concatenating(rhs) 63 | } 64 | 65 | @inlinable static func *=(lhs: inout CGAffineTransform, rhs: CGAffineTransform) { 66 | lhs = lhs * rhs 67 | } 68 | 69 | @inlinable static func /(lhs: CGAffineTransform, rhs: CGAffineTransform) -> CGAffineTransform { 70 | return lhs.concatenating(rhs.inverted()) 71 | } 72 | 73 | @inlinable static func /=(lhs: inout CGAffineTransform, rhs: CGAffineTransform) { 74 | lhs = lhs / rhs 75 | } 76 | } 77 | 78 | public protocol CGAffineTransformApplying { 79 | func applying(_ t: CGAffineTransform) -> Self 80 | } 81 | 82 | extension CGPoint: CGAffineTransformApplying {} 83 | extension CGSize: CGAffineTransformApplying {} 84 | extension CGRect: CGAffineTransformApplying {} 85 | 86 | public extension CGAffineTransformApplying { 87 | 88 | mutating func apply(t: CGAffineTransform) { 89 | self = applying(t) 90 | } 91 | } 92 | 93 | #endif // canImport(CoreGraphics) 94 | -------------------------------------------------------------------------------- /Sources/CoreGraphicsExt/Geometry/CGFloat.swift: -------------------------------------------------------------------------------- 1 | #if canImport(CoreGraphics) 2 | 3 | import CoreGraphics 4 | 5 | public extension CGFloat { 6 | 7 | /// degrees to radians 8 | @inlinable var toRadians: CGFloat { 9 | return self * .pi / 180 10 | } 11 | 12 | /// radians to degrees 13 | @inlinable var toDegrees: CGFloat { 14 | return self * 180 / .pi 15 | } 16 | } 17 | 18 | #endif // canImport(CoreGraphics) 19 | -------------------------------------------------------------------------------- /Sources/CoreGraphicsExt/Geometry/CGPoint.swift: -------------------------------------------------------------------------------- 1 | #if canImport(CoreGraphics) 2 | 3 | import CoreGraphics 4 | 5 | public extension CGPoint { 6 | 7 | @inlinable func distance(to point: CGPoint) -> CGFloat { 8 | return hypot(point.x - x, point.y - y) 9 | } 10 | 11 | @inlinable static func +(lhs: CGPoint, rhs: CGPoint) -> CGPoint { 12 | return CGPoint(x: lhs.x + rhs.x, y: lhs.y + rhs.y) 13 | } 14 | 15 | @inlinable static func +=(lhs: inout CGPoint, rhs: CGPoint) { 16 | lhs = lhs + rhs 17 | } 18 | 19 | @inlinable static func -(lhs: CGPoint, rhs: CGPoint) -> CGPoint { 20 | return CGPoint(x: lhs.x - rhs.x, y: lhs.y - rhs.y) 21 | } 22 | 23 | @inlinable static func -=(lhs: inout CGPoint, rhs: CGPoint) { 24 | lhs = lhs - rhs 25 | } 26 | 27 | @inlinable static func *(point: CGPoint, scalar: CGFloat) -> CGPoint { 28 | return CGPoint(x: point.x * scalar, y: point.y * scalar) 29 | } 30 | 31 | @inlinable static func *=(point: inout CGPoint, scalar: CGFloat) { 32 | point = point * scalar 33 | } 34 | 35 | @inlinable static func /(point: CGPoint, scalar: CGFloat) -> CGPoint { 36 | return CGPoint(x: point.x / scalar, y: point.y / scalar) 37 | } 38 | 39 | @inlinable static func /=(point: inout CGPoint, scalar: CGFloat) { 40 | point = point / scalar 41 | } 42 | } 43 | 44 | public extension CGPoint { 45 | 46 | @inlinable init(_ vector: CGVector) { 47 | self.init(x: vector.dx, y: vector.dy) 48 | } 49 | 50 | @inlinable static func +(lhs: CGPoint, rhs: CGVector) -> CGPoint { 51 | return CGPoint(x: lhs.x + rhs.dx, y: lhs.y + rhs.dy) 52 | } 53 | 54 | @inlinable static func +=(lhs: inout CGPoint, rhs: CGVector) { 55 | lhs = lhs + rhs 56 | } 57 | 58 | @inlinable static func -(lhs: CGPoint, rhs: CGVector) -> CGPoint { 59 | return CGPoint(x: lhs.x - rhs.dx, y: lhs.y - rhs.dy) 60 | } 61 | 62 | @inlinable static func -=(lhs: inout CGPoint, rhs: CGVector) { 63 | lhs = lhs - rhs 64 | } 65 | } 66 | 67 | #endif // canImport(CoreGraphics) 68 | -------------------------------------------------------------------------------- /Sources/CoreGraphicsExt/Geometry/CGRect.swift: -------------------------------------------------------------------------------- 1 | #if canImport(CoreGraphics) 2 | 3 | import CoreGraphics 4 | 5 | public extension CGRect { 6 | 7 | @inlinable init(minX: CGFloat, minY: CGFloat, maxX: CGFloat, maxY: CGFloat) { 8 | self.init(x: minX, 9 | y: minY, 10 | width: maxX - minX, 11 | height: maxY - minY) 12 | } 13 | 14 | @inlinable init(center: CGPoint, size: CGSize) { 15 | self.init(x: center.x - size.width / 2, 16 | y: center.y - size.height / 2, 17 | width: size.width, 18 | height: size.height) 19 | } 20 | 21 | @inlinable var area: CGFloat { 22 | return width * height 23 | } 24 | 25 | @inlinable var center: CGPoint { 26 | get { 27 | return CGPoint(x: midX, y: midY) 28 | } 29 | set { 30 | origin.x = newValue.x - size.width / 2 31 | origin.y = newValue.y - size.height / 2 32 | } 33 | } 34 | 35 | @inlinable func center(on edge: CGRectEdge) -> CGPoint { 36 | switch edge { 37 | case .maxXEdge: return CGPoint(x: maxX, y: midY) 38 | case .maxYEdge: return CGPoint(x: midX, y: maxY) 39 | case .minXEdge: return CGPoint(x: minX, y: midY) 40 | case .minYEdge: return CGPoint(x: midX, y: minY) 41 | } 42 | } 43 | 44 | @inlinable func offsetBy(dx: CGFloat = 0, dy: CGFloat = 0) -> CGRect { 45 | return CGRect(x: minX + dx, y: minY + dy, width: width, height: height) 46 | } 47 | 48 | @inlinable mutating func formOffsetBy(dx: CGFloat = 0, dy: CGFloat = 0) { 49 | self = offsetBy(dx: dx, dy: dy) 50 | } 51 | 52 | @inlinable func insetBy(x: CGFloat = 0, y: CGFloat = 0) -> CGRect { 53 | return insetBy(minX: x, minY: y, maxX: x, maxY: y) 54 | } 55 | 56 | @inlinable mutating func formInsetBy(x: CGFloat = 0, y: CGFloat = 0) { 57 | self = insetBy(x: x, y: y) 58 | } 59 | 60 | @inlinable func insetBy(minX: CGFloat = 0, minY: CGFloat = 0, maxX: CGFloat = 0, maxY: CGFloat = 0) -> CGRect { 61 | return CGRect(x: self.minX + minX, 62 | y: self.minY + minY, 63 | width: width - minX - maxX, 64 | height: height - minY - maxY) 65 | } 66 | 67 | @inlinable mutating func formInsetBy(minX: CGFloat = 0, minY: CGFloat = 0, maxX: CGFloat = 0, maxY: CGFloat = 0) { 68 | self = insetBy(minX: minX, minY: minY, maxX: maxX, maxY: maxY) 69 | } 70 | 71 | @inlinable func extendBy(x: CGFloat = 0, y: CGFloat = 0) -> CGRect { 72 | return insetBy(x: -x, y: -y) 73 | } 74 | 75 | @inlinable mutating func formExtendBy(x: CGFloat = 0, y: CGFloat = 0) { 76 | self = extendBy(x: x, y: y) 77 | } 78 | 79 | @inlinable func extendBy(minX: CGFloat = 0, minY: CGFloat = 0, maxX: CGFloat = 0, maxY: CGFloat = 0) -> CGRect { 80 | return insetBy(minX: -minX, minY: -minY, maxX: -maxX, maxY: -maxY) 81 | } 82 | 83 | @inlinable mutating func formExtendBy(minX: CGFloat = 0, minY: CGFloat = 0, maxX: CGFloat = 0, maxY: CGFloat = 0) { 84 | self = extendBy(minX: minX, minY: minY, maxX: maxX, maxY: maxY) 85 | } 86 | } 87 | 88 | #endif // canImport(CoreGraphics) 89 | -------------------------------------------------------------------------------- /Sources/CoreGraphicsExt/Geometry/CGSize.swift: -------------------------------------------------------------------------------- 1 | #if canImport(CoreGraphics) 2 | 3 | import CoreGraphics 4 | 5 | public extension CGSize { 6 | 7 | @inlinable var area: CGFloat { 8 | return width * height 9 | } 10 | 11 | @inlinable var aspectRatio: CGFloat { 12 | guard height != 0 else { return 0 } 13 | return width / height 14 | } 15 | 16 | @inlinable func aspectFit(to size: CGSize) -> CGSize { 17 | let xScale = size.width / width 18 | let yScale = size.height / height 19 | return self * min(xScale, yScale) 20 | } 21 | 22 | @inlinable func aspectFill(to size: CGSize) -> CGSize { 23 | let xScale = size.width / width 24 | let yScale = size.height / height 25 | return self * max(xScale, yScale) 26 | } 27 | 28 | @inlinable static func +(lhs: CGSize, rhs: CGSize) -> CGSize { 29 | return CGSize(width: lhs.width + rhs.width, height: lhs.height + rhs.height) 30 | } 31 | 32 | @inlinable static func +=(lhs: inout CGSize, rhs: CGSize) { 33 | lhs = lhs + rhs 34 | } 35 | 36 | @inlinable static func -(lhs: CGSize, rhs: CGSize) -> CGSize { 37 | return CGSize(width: lhs.width - rhs.width, height: lhs.height - rhs.height) 38 | } 39 | 40 | @inlinable static func -=(lhs: inout CGSize, rhs: CGSize) { 41 | lhs = lhs - rhs 42 | } 43 | 44 | @inlinable static func *(size: CGSize, scalar: CGFloat) -> CGSize { 45 | return CGSize(width: size.width * scalar, height: size.height * scalar) 46 | } 47 | 48 | @inlinable static func *=(size: inout CGSize, scalar: CGFloat) { 49 | size = size * scalar 50 | } 51 | 52 | @inlinable static func /(size: CGSize, scalar: CGFloat) -> CGSize { 53 | return CGSize(width: size.width / scalar, height: size.height / scalar) 54 | } 55 | 56 | @inlinable static func /=(size: inout CGSize, scalar: CGFloat) { 57 | size = size / scalar 58 | } 59 | } 60 | 61 | #endif // canImport(CoreGraphics) 62 | -------------------------------------------------------------------------------- /Sources/CoreGraphicsExt/Geometry/CGVector.swift: -------------------------------------------------------------------------------- 1 | #if canImport(CoreGraphics) 2 | 3 | import CoreGraphics 4 | 5 | public extension CGVector { 6 | 7 | @inlinable init(_ point: CGPoint) { 8 | self.init(dx: point.x, dy: point.y) 9 | } 10 | 11 | @inlinable init(from: CGPoint, to: CGPoint) { 12 | self.init(dx: to.x - from.x, dy: to.y - from.y) 13 | } 14 | 15 | @inlinable init(angle: CGFloat, length: CGFloat = 1) { 16 | self.init(dx: cos(angle) * length, dy: sin(angle) * length) 17 | } 18 | 19 | @inlinable var length: CGFloat { 20 | get { 21 | return hypot(dx, dy) 22 | } 23 | set { 24 | guard self != .zero else { return } 25 | let scale = newValue / length 26 | dx *= scale 27 | dy *= scale 28 | } 29 | } 30 | 31 | /// in radians 32 | @inlinable var angle: CGFloat { 33 | return atan2(dy, dx) 34 | } 35 | 36 | @inlinable static func +(lhs: CGVector, rhs: CGVector) -> CGVector { 37 | return CGVector(dx: lhs.dx + rhs.dx, dy: lhs.dy + rhs.dy) 38 | } 39 | 40 | @inlinable static func +=(lhs: inout CGVector, rhs: CGVector) { 41 | lhs = lhs + rhs 42 | } 43 | 44 | @inlinable static func -(lhs: CGVector, rhs: CGVector) -> CGVector { 45 | return CGVector(dx: lhs.dx - rhs.dx, dy: lhs.dy - rhs.dy) 46 | } 47 | 48 | @inlinable static func -=(lhs: inout CGVector, rhs: CGVector) { 49 | lhs = lhs - rhs 50 | } 51 | 52 | @inlinable static func *(vector: CGVector, scalar: CGFloat) -> CGVector { 53 | return CGVector(dx: vector.dx * scalar, dy: vector.dy * scalar) 54 | } 55 | 56 | @inlinable static func *=(vector: inout CGVector, scalar: CGFloat) { 57 | vector = vector * scalar 58 | } 59 | 60 | @inlinable static func /(vector: CGVector, scalar: CGFloat) -> CGVector { 61 | return CGVector(dx: vector.dx / scalar, dy: vector.dy / scalar) 62 | } 63 | 64 | @inlinable static func /=(vector: inout CGVector, scalar: CGFloat) { 65 | vector = vector / scalar 66 | } 67 | } 68 | 69 | #endif // canImport(CoreGraphics) 70 | -------------------------------------------------------------------------------- /Sources/CoreGraphicsExt/WindowServices/CGWindow.swift: -------------------------------------------------------------------------------- 1 | #if os(macOS) 2 | 3 | import SwiftCF 4 | import CoreGraphics 5 | 6 | // namespace of CGWindow related functions 7 | public enum CGWindow { 8 | 9 | public typealias ID = CGWindowID 10 | public typealias ListOption = CGWindowListOption 11 | } 12 | 13 | public extension CGWindow { 14 | 15 | /// Return an array of window dictionaries for windows within the user 16 | /// session. 17 | /// This function returns NULL if the caller is not running within a Quartz 18 | /// GUI session or the window server is disabled. You should release the 19 | /// array when you are finished using it. 20 | static func windowInfoList(relativeTo window: ID = kCGNullWindowID, option: ListOption = []) -> [[InfoKey: Any]] { 21 | return CGWindowListCopyWindowInfo(option, window)?.asSwift() ?? [] 22 | } 23 | } 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /Sources/CoreGraphicsExt/WindowServices/CGWindowInfoKeys.swift: -------------------------------------------------------------------------------- 1 | #if os(macOS) 2 | 3 | import SwiftCF 4 | import CoreGraphics 5 | 6 | extension CGWindow { 7 | 8 | public struct InfoKey: CFStringKey { 9 | 10 | public let rawValue: CFString 11 | 12 | public init(_ key: CFString) { 13 | rawValue = key 14 | } 15 | } 16 | } 17 | 18 | public extension CGWindow.InfoKey { 19 | // required keys 20 | static let number = kCGWindowNumber as CGWindow.InfoKey 21 | static let storeType = kCGWindowStoreType as CGWindow.InfoKey 22 | static let layer = kCGWindowLayer as CGWindow.InfoKey 23 | static let bounds = kCGWindowBounds as CGWindow.InfoKey 24 | static let sharingState = kCGWindowSharingState as CGWindow.InfoKey 25 | static let alpha = kCGWindowAlpha as CGWindow.InfoKey 26 | static let ownerPID = kCGWindowOwnerPID as CGWindow.InfoKey 27 | static let memoryUsage = kCGWindowMemoryUsage as CGWindow.InfoKey 28 | // optional keys 29 | static let ownerName = kCGWindowOwnerName as CGWindow.InfoKey 30 | static let name = kCGWindowName as CGWindow.InfoKey 31 | static let isOnscreen = kCGWindowIsOnscreen as CGWindow.InfoKey 32 | static let backingLocationVideoMemory = kCGWindowBackingLocationVideoMemory as CGWindow.InfoKey 33 | } 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /Sources/CoreTextExt/CTTollFreeBridge.swift: -------------------------------------------------------------------------------- 1 | #if canImport(CoreText) 2 | 3 | import SwiftCF 4 | import CoreText 5 | 6 | // TODO: CTFont is not documented as toll free bridgable. Tests needed. 7 | 8 | #if canImport(AppKit) 9 | 10 | import AppKit 11 | 12 | extension CTFont: _CFTollFreeBridgeable { 13 | public typealias BridgedNSType = NSFont 14 | } 15 | 16 | #elseif canImport(UIKit) 17 | 18 | import UIKit 19 | 20 | extension CTFont: _CFTollFreeBridgeable { 21 | public typealias BridgedNSType = UIFont 22 | } 23 | 24 | #endif // canImport(AppKit) 25 | 26 | #endif // canImport(CoreText) 27 | -------------------------------------------------------------------------------- /Sources/CoreTextExt/CTType.swift: -------------------------------------------------------------------------------- 1 | #if canImport(CoreText) 2 | 3 | import SwiftCF 4 | import CoreText 5 | 6 | extension CTFont: CFType { 7 | public static var typeID = CTFontCollectionGetTypeID() 8 | } 9 | 10 | extension CTFontCollection: CFType { 11 | public static var typeID = CTFontCollectionGetTypeID() 12 | } 13 | 14 | extension CTFontDescriptor: CFType { 15 | public static var typeID = CTFontDescriptorGetTypeID() 16 | } 17 | 18 | extension CTFrame: CFType { 19 | public static var typeID = CTFrameGetTypeID() 20 | } 21 | 22 | extension CTFramesetter: CFType { 23 | public static var typeID = CTFramesetterGetTypeID() 24 | } 25 | 26 | extension CTGlyphInfo: CFType { 27 | public static var typeID = CTGlyphInfoGetTypeID() 28 | } 29 | 30 | extension CTLine: CFType { 31 | public static var typeID = CTLineGetTypeID() 32 | } 33 | 34 | extension CTParagraphStyle: CFType { 35 | public static var typeID = CTParagraphStyleGetTypeID() 36 | } 37 | 38 | extension CTRubyAnnotation: CFType { 39 | public static var typeID = CTRubyAnnotationGetTypeID() 40 | } 41 | 42 | extension CTRun: CFType { 43 | public static var typeID = CTRunGetTypeID() 44 | } 45 | 46 | extension CTTextTab: CFType { 47 | public static var typeID = CTTextTabGetTypeID() 48 | } 49 | 50 | extension CTTypesetter: CFType { 51 | public static var typeID = CTTypesetterGetTypeID() 52 | } 53 | 54 | // MARK: - Mutable Type 55 | 56 | //extension CTMutableFontCollection: CFType {} 57 | 58 | #endif // canImport(CoreText) 59 | -------------------------------------------------------------------------------- /Sources/CoreTextExt/CTTypeExt/CTFontCollection.swift: -------------------------------------------------------------------------------- 1 | #if canImport(CoreText) 2 | 3 | import SwiftCF 4 | import Foundation 5 | import CoreText 6 | 7 | public extension CTFontCollection { 8 | 9 | /// Returns a new font collection matching all available fonts. 10 | /// 11 | /// - Parameter options: The options dictionary. See constant option keys. 12 | /// - Returns: This function creates a new collection containing all fonts 13 | /// available to the current application. 14 | @inlinable static func availableFonts(options: [OptionKey: Any] = [:]) -> CTFontCollection { 15 | return CTFontCollectionCreateFromAvailableFonts(.from(options)) 16 | } 17 | 18 | /// Returns a new collection based on the array of font descriptors. 19 | /// 20 | /// - Parameters: 21 | /// - queryDescriptors: An array of font descriptors to use for matching. 22 | /// May be NULL, in which case the matching descriptors will be NULL. 23 | /// - options: The options dictionary. See constant option keys. 24 | /// - Returns: This function creates a new collection based on the provided 25 | /// font descriptors. The contents of this collection is defined by matching 26 | /// the provided descriptors against all available font descriptors. 27 | @inlinable static func create(queryDescriptors: [CTFontDescriptor] = [], options: [OptionKey: Any] = [:]) -> CTFontCollection { 28 | return CTFontCollectionCreateWithFontDescriptors(.from(queryDescriptors), .from(options)) 29 | } 30 | 31 | /// Returns a copy of the original collection augmented with the given new 32 | /// font descriptors. 33 | /// 34 | /// The new font descriptors are merged with the existing descriptors to 35 | /// create a single set. 36 | /// 37 | /// - Parameters: 38 | /// - queryDescriptors: An array of font descriptors to augment those of 39 | /// the original collection. 40 | /// - options: The options dictionary. For possible values, see Constants. 41 | @inlinable func copy(queryDescriptors: [CTFontDescriptor] = [], options: [OptionKey: Any] = [:]) -> CTFontCollection { 42 | return CTFontCollectionCreateCopyWithFontDescriptors(self, .from(queryDescriptors), .from(options)) 43 | } 44 | 45 | #if os(macOS) 46 | 47 | /// Returns a mutable copy of the original collection. 48 | @inlinable func mutableCopy() -> CTMutableFontCollection { 49 | return CTFontCollectionCreateMutableCopy(self) 50 | } 51 | 52 | /// Returns the array of descriptors to match. 53 | /// 54 | /// - Returns: This function returns a retained reference to the array of 55 | /// descriptors to be used to query (match) the system font database. The 56 | /// return value is undefined if CTFontCollectionCreateFromAvailableFonts 57 | /// was used to create the collection. 58 | @inlinable func queryDescriptors() -> [CTFontDescriptor] { 59 | return CTFontCollectionCopyQueryDescriptors(self)?.asSwift() ?? [] 60 | } 61 | 62 | /// Returns the array of descriptors to exclude from the match. 63 | /// 64 | /// - Returns: This function returns a retained reference to the array of 65 | /// descriptors to be used to query (match) the system font database. 66 | @inlinable func exclusionDescriptors() -> [CTFontDescriptor] { 67 | return CTFontCollectionCopyExclusionDescriptors(self)?.asSwift() ?? [] 68 | } 69 | 70 | #endif // os(macOS) 71 | 72 | /// Returns an array of font descriptors matching the collection. 73 | /// 74 | /// - Returns: An array of CTFontDescriptors matching the collection 75 | /// definition or NULL if there are none. 76 | @inlinable func matchingFontDescriptors() -> [CTFontDescriptor] { 77 | return CTFontCollectionCreateMatchingFontDescriptors(self)?.asSwift() ?? [] 78 | } 79 | 80 | /// Returns an array of font descriptors matching the collection. 81 | /// 82 | /// - Parameter options: The options dictionary. See constant option keys. 83 | /// May be NULL, in which case this call returns the same results as 84 | /// CTFontCollectionCreateMatchingFontDescriptors, using the options passed 85 | /// in when the collection was created. 86 | /// - Returns: An array of CTFontDescriptors matching the collection 87 | /// definition or NULL if there are none. 88 | @available(macOS 10.7, iOS 12.0, tvOS 12.0, watchOS 5.0, *) 89 | @inlinable func matchingFontDescriptors(options: [OptionKey: Any] = [:]) -> [CTFontDescriptor]? { 90 | return CTFontCollectionCreateMatchingFontDescriptorsWithOptions(self, .from(options))?.asSwift() ?? [] 91 | } 92 | 93 | #if os(macOS) 94 | 95 | /// Option bits for use with CTFontCollection.fontAttribute(s). 96 | typealias CopyOptions = CTFontCollectionCopyOptions 97 | 98 | /// Returns an array of font descriptor attribute values. 99 | /// 100 | /// - Parameters: 101 | /// - key: The attribute to retrieve for each descriptor in the 102 | /// collection. 103 | /// - options: Options to alter the return value. 104 | /// - Returns: An array containing one value for each descriptor. With 105 | /// kCTFontCollectionCopyDefaultOptions, the values will be in the same 106 | /// order as the results from CTFontCollectionCreateMatchingFontDescriptors 107 | /// and NULL values will be transformed to kCFNull. When the 108 | /// kCTFontCollectionCopyUnique is set, duplicate values will be removed. 109 | /// When kCTFontCollectionCopyStandardSort is set, the values will be sorted 110 | /// in standard UI order. 111 | @inlinable func fontAttribute(key: CTFont.AttributeKey, options: CopyOptions) -> [Any?] { 112 | return CTFontCollectionCopyFontAttribute(self, key.rawValue, options).map(cfUnwrap(_:)) 113 | } 114 | 115 | /// Returns an array of dictionaries containing font descriptor attribute 116 | /// values. 117 | /// 118 | /// - Parameters: 119 | /// - keys: The attributes to retrieve for each descriptor in the 120 | /// collection. 121 | /// - options: Options to alter the return value. 122 | /// - Returns: An array containing one CFDictionary value for each 123 | /// descriptor mapping the requested attribute names. With 124 | /// kCTFontCollectionCopyDefaultOptions, the values will be in the same 125 | /// order as the results from CTFontCollectionCreateMatchingFontDescriptors. 126 | /// When the kCTFontCollectionCopyUnique is set, duplicate values will be 127 | /// removed. When kCTFontCollectionCopyStandardSort is set, the values will 128 | /// be sorted in standard UI order. 129 | @inlinable func fontAttributes(keys: Set, options: CopyOptions) -> [[CTFont.AttributeKey: Any?]] { 130 | return CTFontCollectionCopyFontAttributes(self, .from(keys), options) 131 | .map { ($0 as! [CTFont.AttributeKey: CFTypeRef]).mapValues(cfUnwrap(_:)) } 132 | } 133 | 134 | #endif // os(macOS) 135 | } 136 | 137 | #if os(macOS) 138 | 139 | extension CTMutableFontCollection { 140 | 141 | /// Replaces the array of descriptors to match. 142 | /// - Parameter descriptors: An array of CTFontDescriptorRef. May be NULL to 143 | /// represent an empty collection, in which case the matching descriptors 144 | /// will also be NULL. 145 | @inlinable func setQueryDescriptors(_ descriptors: [CTFontDescriptor]) { 146 | return CTFontCollectionSetQueryDescriptors(self, .from(descriptors)) 147 | } 148 | 149 | /// Replaces the array of descriptors to exclude from the match. 150 | /// - Parameter descriptors: An array of CTFontDescriptorRef. May be NULL. 151 | @inlinable func setExclusionDescriptors(_ descriptors: [CTFontDescriptor]) { 152 | return CTFontCollectionSetExclusionDescriptors(self, .from(descriptors)) 153 | } 154 | } 155 | 156 | #endif // os(macOS) 157 | 158 | #endif // canImport(CoreText) 159 | -------------------------------------------------------------------------------- /Sources/CoreTextExt/CTTypeExt/CTFontCollectionOptionKey.swift: -------------------------------------------------------------------------------- 1 | #if canImport(CoreText) 2 | 3 | import SwiftCF 4 | import CoreText 5 | 6 | extension CTFontCollection { 7 | 8 | public struct OptionKey: CFStringKey { 9 | 10 | public let rawValue: CFString 11 | 12 | public init(_ key: CFString) { 13 | rawValue = key 14 | } 15 | } 16 | } 17 | 18 | extension CTFontCollection.OptionKey { 19 | 20 | /// Option key to specify filtering of duplicates. 21 | /// 22 | /// Specify this option key in the options dictionary with a non- zero value 23 | /// to enable automatic filtering of duplicate font descriptors. 24 | static let removeDuplicates = kCTFontCollectionRemoveDuplicatesOption as CTFontCollection.OptionKey 25 | } 26 | 27 | #endif // canImport(CoreText) 28 | -------------------------------------------------------------------------------- /Sources/CoreTextExt/CTTypeExt/CTFontNameKey.swift: -------------------------------------------------------------------------------- 1 | #if canImport(CoreText) 2 | 3 | import SwiftCF 4 | import CoreText 5 | 6 | extension CTFont { 7 | 8 | /// Name specifier constants provide access to the different names 9 | /// associated with a font. 10 | public struct NameKey: CFStringKey { 11 | 12 | public let rawValue: CFString 13 | 14 | public init(_ key: CFString) { 15 | rawValue = key 16 | } 17 | } 18 | } 19 | 20 | public extension CTFont.NameKey { 21 | /// The name specifier for the copyright name. 22 | static let copyright = kCTFontCopyrightNameKey as CTFont.NameKey 23 | /// The name specifier for the family name. 24 | static let family = kCTFontFamilyNameKey as CTFont.NameKey 25 | /// The name specifier for the subfamily name. 26 | static let subFamily = kCTFontSubFamilyNameKey as CTFont.NameKey 27 | /// The name specifier for the style name. 28 | static let style = kCTFontStyleNameKey as CTFont.NameKey 29 | /// The name specifier for the unique name. 30 | /// 31 | /// Note that this name is often not unique and should not be assumed to be 32 | /// truly unique. 33 | static let unique = kCTFontUniqueNameKey as CTFont.NameKey 34 | /// The name specifier for the full name. 35 | static let full = kCTFontFullNameKey as CTFont.NameKey 36 | /// The name specifier for the version name. 37 | static let version = kCTFontVersionNameKey as CTFont.NameKey 38 | /// The name specifier for the PostScript name. 39 | static let postScript = kCTFontPostScriptNameKey as CTFont.NameKey 40 | /// The name specifier for the trademark name. 41 | static let trademark = kCTFontTrademarkNameKey as CTFont.NameKey 42 | /// The name specifier for the manufacturer name. 43 | static let manufacturer = kCTFontManufacturerNameKey as CTFont.NameKey 44 | /// The name specifier for the designer name. 45 | static let designer = kCTFontDesignerNameKey as CTFont.NameKey 46 | /// The name specifier for the description name. 47 | static let description = kCTFontDescriptionNameKey as CTFont.NameKey 48 | /// The name specifier for the vendor url name. 49 | static let vendorURL = kCTFontVendorURLNameKey as CTFont.NameKey 50 | /// The name specifier for the designer url name. 51 | static let designerURL = kCTFontDesignerURLNameKey as CTFont.NameKey 52 | /// The name specifier for the license name. 53 | static let license = kCTFontLicenseNameKey as CTFont.NameKey 54 | /// The name specifier for the license url name. 55 | static let licenseURL = kCTFontLicenseURLNameKey as CTFont.NameKey 56 | /// The name specifier for the sample text name string. 57 | static let sampleText = kCTFontSampleTextNameKey as CTFont.NameKey 58 | /// The name specifier for the PostScript CID name. 59 | static let postScriptCID = kCTFontPostScriptCIDNameKey as CTFont.NameKey 60 | } 61 | 62 | #endif // canImport(CoreText) 63 | -------------------------------------------------------------------------------- /Sources/CoreTextExt/CTTypeExt/CTFontPriority.swift: -------------------------------------------------------------------------------- 1 | #if canImport(CoreText) 2 | 3 | import CoreText 4 | 5 | extension CTFont { 6 | 7 | public struct Priority: RawRepresentable { 8 | 9 | public let rawValue: UInt32 10 | 11 | public init(rawValue: UInt32) { 12 | self.rawValue = rawValue 13 | } 14 | } 15 | } 16 | 17 | public extension CTFont.Priority { 18 | /// Priority of system fonts (located in /System/Library/Fonts). 19 | static let system = CTFont.Priority(rawValue: UInt32(truncatingIfNeeded: kCTFontPrioritySystem)) 20 | /// Priority of network fonts (located in /Network/Library/Fonts). 21 | static let network = CTFont.Priority(rawValue: UInt32(truncatingIfNeeded: kCTFontPriorityNetwork)) 22 | /// Priority of computer local fonts (located in /Library/Fonts). 23 | static let computer = CTFont.Priority(rawValue: UInt32(truncatingIfNeeded: kCTFontPriorityComputer)) 24 | /// Priority of local fonts (located in user's Library/Fonts). 25 | static let user = CTFont.Priority(rawValue: UInt32(truncatingIfNeeded: kCTFontPriorityUser)) 26 | /// Priority of fonts registered dynamically, not located in a standard 27 | /// location (either kCTFontManagerScopeUser, or kCTFontManagerScopeSession). 28 | static let dynamic = CTFont.Priority(rawValue: UInt32(truncatingIfNeeded: kCTFontPriorityDynamic)) 29 | /// Priority of fonts registered for the process (kCTFontManagerScopeProcess). 30 | static let process = CTFont.Priority(rawValue: UInt32(truncatingIfNeeded: kCTFontPriorityProcess)) 31 | } 32 | 33 | #endif // canImport(CoreText) 34 | -------------------------------------------------------------------------------- /Sources/CoreTextExt/CTTypeExt/CTFontTraitKey.swift: -------------------------------------------------------------------------------- 1 | #if canImport(CoreText) 2 | 3 | import SwiftCF 4 | import CoreText 5 | 6 | extension CTFont { 7 | 8 | public struct TraitKey: CFStringKey { 9 | 10 | public let rawValue: CFString 11 | 12 | public init(_ key: CFString) { 13 | rawValue = key 14 | } 15 | } 16 | } 17 | 18 | public extension CTFont.TraitKey { 19 | 20 | /// The symbolic traits value from the font traits dictionary. 21 | /// 22 | /// The value is returned as a CFNumber object. 23 | static let symbolic = kCTFontSymbolicTrait as CTFont.TraitKey 24 | 25 | /// The normalized weight trait from the font traits dictionary. 26 | /// 27 | /// The value returned is a CFNumber representing a float value between -1.0 28 | /// and 1.0 for normalized weight. The value of 0.0 corresponds to the 29 | /// regular or medium font weight. 30 | static let weight = kCTFontWeightTrait as CTFont.TraitKey 31 | 32 | /// The normalized proportion (width condense or expand) trait from the font 33 | /// traits dictionary. 34 | /// 35 | /// This value corresponds to the relative interglyph spacing for a given 36 | /// font. The value returned is a CFNumber object representing a float 37 | /// between -1.0 and 1.0. The value of 0.0 corresponds to regular glyph 38 | /// spacing, and negative values represent condensed glyph spacing. 39 | static let width = kCTFontWidthTrait as CTFont.TraitKey 40 | 41 | /// The normalized slant angle from the font traits dictionary. 42 | /// 43 | /// The value returned is a CFNumber object representing a float value 44 | /// between -1.0 and 1.0 for normalized slant angle. The value of 0.0 45 | /// corresponds to 0 degrees clockwise rotation from the vertical and 1.0 46 | /// corresponds to 30 degrees clockwise rotation. 47 | static let slant = kCTFontSlantTrait as CTFont.TraitKey 48 | } 49 | 50 | #endif // canImport(CoreText) 51 | -------------------------------------------------------------------------------- /Sources/CoreTextExt/CTTypeExt/CTFrame.swift: -------------------------------------------------------------------------------- 1 | #if canImport(CoreText) 2 | 3 | import SwiftCF 4 | import CoreText 5 | 6 | public extension CTFrame { 7 | 8 | /// The type for constants that specify a fill rule used by a frame. 9 | typealias PathFillRule = CTFramePathFillRule 10 | 11 | /// These constants specify frame progression types. 12 | typealias Progression = CTFrameProgression 13 | 14 | /// Returns the range of characters originally requested to fill the frame. 15 | @inlinable var stringRange: CFRange { 16 | return CTFrameGetStringRange(self) 17 | } 18 | 19 | /// Returns the range of characters that actually fit in the frame. 20 | /// 21 | /// This function can be used to cascade frames, because it returns the 22 | /// range of characters that can be seen in the frame. The next frame would 23 | /// start where this frame ends. 24 | @inlinable var visibleStringRange: CFRange { 25 | return CTFrameGetVisibleStringRange(self) 26 | } 27 | 28 | /// Returns the path used to create the frame. 29 | @inlinable var path: CGPath { 30 | return CTFrameGetPath(self) 31 | } 32 | 33 | /// Returns the frame attributes used to create the frame. 34 | /// 35 | /// You can create a frame with an attributes dictionary to control various 36 | /// aspects of the framing process. These attributes are different from the 37 | /// ones used to create an attributed string. 38 | @inlinable var frameAttributes: [AttributeKey: Any] { 39 | return CTFrameGetFrameAttributes(self)?.asSwift() ?? [:] 40 | } 41 | 42 | /// Returns an array of lines stored in the frame. 43 | @inlinable var lines: [CTLine] { 44 | return CTFrameGetLines(self).asSwift() 45 | } 46 | 47 | /// Copies a range of line origins for a frame. 48 | /// 49 | /// This function copies a range of CGPoint structures into the origins 50 | /// buffer. The maximum number of line origins this function will copy into 51 | /// the origins buffer is the count of the array of lines (the length of the 52 | /// range parameter). 53 | /// 54 | /// - Parameter range: The range of line origins you wish to copy. If the 55 | /// length of the range is 0, then the copy operation continues from the 56 | /// start index of the range to the last line origin. 57 | /// - Returns: The buffer to which the origins will be copied. When using 58 | /// the origins to calculate measurements for a frame's contents, remember 59 | /// that line origins do not always correspond to line metrics; paragraph 60 | /// style settings can affect line origins, for one. The overall typographic 61 | /// bounds of a frame may generally be calculated as the difference between 62 | /// the top of the frame and the descent of the last line. This will 63 | /// obviously exclude any spacing following the last line, but such spacing 64 | /// has no effect on framesetting in the first place. 65 | @inlinable func lineOrigins(range: CFRange) -> [CGPoint] { 66 | var arr = [CGPoint](repeating: .zero, count: range.length) 67 | CTFrameGetLineOrigins(self, range, &arr) 68 | return arr 69 | } 70 | 71 | /// Draws an entire frame into a context. 72 | /// 73 | /// If both the frame and the context are valid, the frame is drawn in the 74 | /// context. This call can leave the context in any state and does not flush 75 | /// it after the draw operation. 76 | /// 77 | /// - Parameter context: The context in which to draw the frame. 78 | @inlinable func draw(in context: CGContext) { 79 | CTFrameDraw(self, context) 80 | } 81 | 82 | // MARK: - 83 | 84 | @inlinable var lineAndOrigins: Zip2Sequence<[CTLine], [CGPoint]> { 85 | return zip(lines, lineOrigins(range: lines.indices.asCF)) 86 | } 87 | } 88 | 89 | #endif // canImport(CoreText) 90 | -------------------------------------------------------------------------------- /Sources/CoreTextExt/CTTypeExt/CTFrameAttributeKey.swift: -------------------------------------------------------------------------------- 1 | #if canImport(CoreText) 2 | 3 | import SwiftCF 4 | import CoreText 5 | 6 | extension CTFrame { 7 | 8 | public struct AttributeKey: CFStringKey { 9 | 10 | public let rawValue: CFString 11 | 12 | public init(_ key: CFString) { 13 | rawValue = key 14 | } 15 | } 16 | } 17 | 18 | public extension CTFrame.AttributeKey { 19 | 20 | /// A CFNumber object containing a CTFrame.Progression constant. The default 21 | /// is CTFrame.Progression.topToBottom. 22 | static let progression = kCTFrameProgressionAttributeName as CTFrame.AttributeKey 23 | 24 | /// The value must be a CFNumber object containing a CTFrame.PathFillRule 25 | /// constant. The default value is CTFrame.PathFillRule.evenOdd. 26 | static let pathFillRule = kCTFramePathFillRuleAttributeName as CTFrame.AttributeKey 27 | 28 | /// The value must be a CFNumber object containing a value specifying the 29 | /// frame width. The default width value is zero. 30 | static let pathWidth = kCTFramePathWidthAttributeName as CTFrame.AttributeKey 31 | 32 | /// The value must be a CFArrayRef containing CFDictionaryRefs. Each 33 | /// dictionary should have a CTFrame.AttributeKey.pathClippingPath key-value 34 | /// pair, and can have a CTFrame.AttributeKey.pathFillRule key-value pair 35 | /// and CTFrame.AttributeKey.pathFillRule key-value pair as optional 36 | /// parameters. 37 | static let clippingPaths = kCTFrameClippingPathsAttributeName as CTFrame.AttributeKey 38 | 39 | /// The value must be a CGPathRef specifying a clipping pat. See 40 | /// CTFrame.AttributeKey.clippingPaths. 41 | static let pathClippingPath = kCTFramePathClippingPathAttributeName as CTFrame.AttributeKey 42 | } 43 | 44 | #endif // canImport(CoreText) 45 | -------------------------------------------------------------------------------- /Sources/CoreTextExt/CTTypeExt/CTFramesetter.swift: -------------------------------------------------------------------------------- 1 | #if canImport(CoreText) 2 | 3 | import CoreText 4 | 5 | public extension CTFramesetter { 6 | 7 | /// Creates a framesetter directly from a typesetter. 8 | /// 9 | /// Each framesetter uses a typesetter internally to perform line breaking 10 | /// and other contextual analysis based on the characters in a string. This 11 | /// function allows the framesetter to use a typesetter that was constructed 12 | /// using specific options. 13 | /// 14 | /// - Parameter typesetter: The typesetter to be used by the newly created 15 | /// framesetter. 16 | /// - Returns: This function will return a reference to a CTFramesetter 17 | /// object. 18 | @available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 5.0, *) 19 | @inlinable static func create(typesetter: CTTypesetter) -> CTFramesetter { 20 | return CTFramesetterCreateWithTypesetter(typesetter) 21 | } 22 | 23 | /// Creates an immutable framesetter object from an attributed string. 24 | /// 25 | /// The resultant framesetter object can be used to create and fill text 26 | /// frames with the CTFramesetter.frame(stringRange:path:frameAttributes:) 27 | /// call. 28 | /// 29 | /// - Parameter attributedString: The attributed string with which to 30 | /// construct the framesetter object. 31 | /// - Returns: This function will return a reference to a CTFramesetter 32 | /// object. 33 | @inlinable static func create(attributedString: CFAttributedString) -> CTFramesetter { 34 | return CTFramesetterCreateWithAttributedString(attributedString) 35 | } 36 | 37 | /// Creates an immutable frame using a framesetter. 38 | /// 39 | /// This call creates a frame full of glyphs in the shape of the path 40 | /// provided by the path parameter. The framesetter continues to fill the 41 | /// frame until it either runs out of text or it finds that text no longer 42 | /// fits. 43 | /// 44 | /// - Parameters: 45 | /// - stringRange: The range, of the attributed string that was used to 46 | /// create the framesetter, that is to be typeset in lines fitted into the 47 | /// frame. If the length portion of the range is set to 0, then the 48 | /// framesetter continues to add lines until it runs out of text or space. 49 | /// - path: A CGPath object that specifies the shape of the frame. The 50 | /// path may be non-rectangular in versions of macOS 10.7 or later and 51 | /// versions of iOS 4.2 or later. 52 | /// - frameAttributes: Additional attributes that control the frame 53 | /// filling process can be specified here, or NULL if there are no such 54 | /// attributes. 55 | /// - Returns: This function will return a reference to a new CTFrame object. 56 | @inlinable func frame(stringRange: CFRange = .zero, path: CGPath, frameAttributes: [CTFrame.AttributeKey: Any] = [:]) -> CTFrame { 57 | return CTFramesetterCreateFrame(self, stringRange, path, .from(frameAttributes)) 58 | } 59 | 60 | /// Determines the frame size needed for a string range. 61 | /// 62 | /// This function can be used to determine how much space is needed to 63 | /// display a string, optionally by constraining the space along either 64 | /// dimension. 65 | /// 66 | /// - Parameters: 67 | /// - constraints: The width and height to which the frame size is 68 | /// constrained. A value of CGFLOAT_MAX for either dimension indicates 69 | /// that it should be treated as unconstrained. 70 | /// - stringRange: The string range to which the frame size applies. The 71 | /// string range is a range over the string used to create the framesetter. 72 | /// If the length portion of the range is set to 0, then the framesetter 73 | /// continues to add lines until it runs out of text or space. 74 | /// - frameAttributes: Additional attributes that control the frame 75 | /// filling process, or NULL if there are no such attributes. 76 | /// - Returns: 77 | /// - size: The actual dimensions for the given string range and 78 | /// constraints. 79 | /// - fitRange: On return, contains the range of the string that actually 80 | /// fit in the constrained size. 81 | @inlinable func suggestFrameSize(constraints: CGSize = CGSize(width: CGFloat.infinity, height: .infinity), stringRange: CFRange = .zero, frameAttributes: [CTFrame.AttributeKey: Any] = [:]) -> (size: CGSize, fitRange: CFRange) { 82 | var fitRange = CFRange() 83 | let size = CTFramesetterSuggestFrameSizeWithConstraints(self, stringRange, .from(frameAttributes), constraints, &fitRange) 84 | return (size, fitRange) 85 | } 86 | 87 | /// Returns the typesetter object being used by the framesetter. 88 | /// 89 | /// Each framesetter uses a typesetter internally to perform line breaking 90 | /// and other contextual analysis based on the characters in a string; this 91 | /// function returns the typesetter being used by a particular framesetter 92 | /// in case the caller would like to perform other operations on that 93 | /// typesetter. 94 | @inlinable var typesetter: CTTypesetter { 95 | return CTFramesetterGetTypesetter(self) 96 | } 97 | } 98 | 99 | #endif // canImport(CoreText) 100 | -------------------------------------------------------------------------------- /Sources/CoreTextExt/CTTypeExt/CTParagraphStyle.swift: -------------------------------------------------------------------------------- 1 | #if canImport(CoreText) 2 | 3 | import CoreText 4 | 5 | public extension CTParagraphStyle { 6 | 7 | typealias Setting = CTParagraphStyleSetting 8 | typealias Specifier = CTParagraphStyleSpecifier 9 | 10 | /// Creates an immutable paragraph style. 11 | /// 12 | /// Using this function is the easiest and most efficient way to create a 13 | /// paragraph style. Paragraph styles should be kept immutable for totally 14 | /// lock-free operation. If an invalid paragraph style setting specifier is 15 | /// passed into the settings parameter, nothing bad will happen, but you 16 | /// will be unable to query for this value. The reason is to allow backward 17 | /// compatibility with style setting specifiers that may be introduced in 18 | /// future versions. 19 | /// 20 | /// - Parameter settings: The settings with which to preload the paragraph 21 | /// style. If you want to specify the default set of settings, set this 22 | /// parameter to NULL. 23 | /// - Returns: If the paragraph style creation was successful, this function 24 | /// will return a valid reference to an immutable CTParagraphStyle object. 25 | /// Otherwise, this function will return NULL. 26 | @inlinable static func create(settings: [Setting]) -> CTParagraphStyle { 27 | return settings.withUnsafeBufferPointer { buffer in 28 | CTParagraphStyleCreate(buffer.baseAddress, buffer.count) 29 | } 30 | } 31 | 32 | /// Obtains the current value for a single setting specifier. 33 | /// 34 | /// This function returns the current value of the specifier whether or not 35 | /// the user actually set it. If the user did not set the specifier, this 36 | /// function returns the default value. If an invalid paragraph style 37 | /// setting specifier is passed into the spec parameter, nothing bad 38 | /// happens, and the buffer value is simply zeroed out. The reason is to 39 | /// allow backward compatibility with style setting specifiers that may be 40 | /// introduced in future versions. 41 | /// 42 | /// - Parameters: 43 | /// - specifier: The setting specifier for which to get the value. 44 | @inlinable func value(for specifier: Specifier, type: T.Type) -> T? { 45 | let buffer = UnsafeMutablePointer.allocate(capacity: 1) 46 | // FIXME: initialize 47 | defer { buffer.deallocate() } 48 | guard CTParagraphStyleGetValueForSpecifier(self, specifier, MemoryLayout.size, UnsafeMutableRawPointer(buffer)) else { 49 | return nil 50 | } 51 | return buffer.pointee 52 | } 53 | } 54 | 55 | // TODO: Description 56 | #if false 57 | private extension CTParagraphStyle { 58 | 59 | enum Description { 60 | case alignment(CTTextAlignment) 61 | case firstLineHeadIndent(CGFloat) 62 | case headIndent(CGFloat) 63 | case tailIndent(CGFloat) 64 | case tabStops(CTTextTab) 65 | case defaultTabInterval(CGFloat) 66 | case lineBreakMode(CTLineBreakMode) 67 | case lineHeightMultiple(CGFloat) 68 | case maximumLineHeight(CGFloat) 69 | case minimumLineHeight(CGFloat) 70 | case paragraphSpacing(CGFloat) 71 | case paragraphSpacingBefore(CGFloat) 72 | case baseWritingDirection(CTWritingDirection) 73 | case maximumLineSpacing(CGFloat) 74 | case minimumLineSpacing(CGFloat) 75 | case lineSpacingAdjustment(CGFloat) 76 | case lineBoundsOptions(CTLineBoundsOptions) 77 | } 78 | } 79 | #endif 80 | 81 | #endif // canImport(CoreText) 82 | -------------------------------------------------------------------------------- /Sources/CoreTextExt/CTTypeExt/CTRubyAnnotation.swift: -------------------------------------------------------------------------------- 1 | #if canImport(CoreText) 2 | 3 | import CoreText 4 | 5 | public extension CTRubyAnnotation { 6 | 7 | typealias Alignment = CTRubyAlignment 8 | typealias Overhang = CTRubyOverhang 9 | typealias Position = CTRubyPosition 10 | 11 | @usableFromInline internal static let defaultSizeFactor: CGFloat = 0.5 12 | 13 | /// Creates an immutable ruby annotation object. 14 | /// - Parameters: 15 | /// - string: A string without any formatting, its format will be derived 16 | /// from the attrs specified below. 17 | /// - position: The position of the annotation text. 18 | /// - alignment: Specifies how the ruby text and the base text should be 19 | /// aligned relative to each other. 20 | /// - overhang: Specifies how the ruby text can overhang adjacent 21 | /// characters. 22 | /// - attributes: A attribute dictionary to combine with the string 23 | /// specified above. If you don't specify kCTFontAttributeName, the font 24 | /// used by the Ruby annotation will be deduced from the base text, with 25 | /// a size factor specified by a CFNumberRef value keyed by 26 | /// kCTRubyAnnotationSizeFactorAttributeName. 27 | /// - Returns: This function will return a reference to a CTRubyAnnotation 28 | /// object. 29 | @inlinable static func create(_ string: CFString, position: Position = .before, alignment: Alignment = .auto, overhang: Overhang = .auto, attributes: [CFAttributedString.Key: Any] = [:]) -> CTRubyAnnotation { 30 | if #available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) { 31 | return CTRubyAnnotationCreateWithAttributes(alignment, overhang, position, string, .from(attributes)) 32 | } else { 33 | let sizeFactor = attributes[.ctRubySizeFactor].flatMap { 34 | CFNumber.cast($0)?.value() as CGFloat? 35 | } ?? CTRubyAnnotation.defaultSizeFactor 36 | let count = Int(Position.count.rawValue) 37 | var textArr: [Unmanaged?] = Array(repeating: nil, count: count) 38 | let pos = Int(position.rawValue) 39 | textArr[pos] = Unmanaged.passUnretained(string) 40 | return CTRubyAnnotationCreate(alignment, overhang, sizeFactor, &textArr) 41 | } 42 | } 43 | 44 | /// Creates an immutable ruby annotation object. 45 | /// 46 | /// Using this function is the easiest and most efficient way to create a 47 | /// ruby annotation object. 48 | /// 49 | /// - Parameters: 50 | /// - strings: An array of CFStringRef, indexed by CTRubyPosition. Supply 51 | /// NULL for any unused positions. 52 | /// - alignment: Specifies how the ruby text and the base text should be 53 | /// aligned relative to each other. 54 | /// - overhang: Specifies how the ruby text can overhang adjacent 55 | /// characters. 56 | /// - sizeFactor: Specifies the size of the annotation text as a percent 57 | /// of the size of the base text. 58 | /// - Returns: This function will return a reference to a CTRubyAnnotation 59 | /// object. 60 | @inlinable static func create(_ strings: [Position: CFString], alignment: Alignment = .auto, overhang: Overhang = .auto, sizeFactor: CGFloat = 0.5) -> CTRubyAnnotation { 61 | let count = Int(Position.count.rawValue) 62 | var textArr: [Unmanaged?] = Array(repeating: nil, count: count) 63 | for (position, string) in strings { 64 | let pos = Int(position.rawValue) 65 | textArr[pos] = Unmanaged.passUnretained(string) 66 | } 67 | return CTRubyAnnotationCreate(alignment, overhang, sizeFactor, &textArr) 68 | } 69 | 70 | /// Creates an immutable copy of a ruby annotation object. 71 | @inlinable func copy() -> CTRubyAnnotation { 72 | return CTRubyAnnotationCreateCopy(self) 73 | } 74 | 75 | /// Get the alignment value of a ruby annotation object. 76 | @inlinable var alignment: Alignment { 77 | return CTRubyAnnotationGetAlignment(self) 78 | } 79 | 80 | /// Get the overhang value of a ruby annotation object. 81 | @inlinable var overhang: Overhang { 82 | return CTRubyAnnotationGetOverhang(self) 83 | } 84 | 85 | /// Get the size factor of a ruby annotation object. 86 | @inlinable var sizeFactor: CGFloat { 87 | return CTRubyAnnotationGetSizeFactor(self) 88 | } 89 | 90 | /// Get the ruby text for a particular position in a ruby annotation. 91 | /// - Parameter position: The position for which you want to get the ruby 92 | /// text. 93 | /// - Returns: If the "rubyAnnotation" reference and the position are valid, 94 | /// then this function will return a CFStringRef for the text. Otherwise it 95 | /// will return NULL. 96 | @inlinable func text(for position: Position) -> CFString? { 97 | return CTRubyAnnotationGetTextForPosition(self, position) 98 | } 99 | } 100 | 101 | #endif // canImport(CoreText) 102 | -------------------------------------------------------------------------------- /Sources/IOKitExt/HID/CallBack.swift: -------------------------------------------------------------------------------- 1 | #if canImport(IOKit) 2 | 3 | import SwiftCF 4 | import IOKit.hid 5 | 6 | public protocol HIDCallbackToken: AnyObject {} 7 | 8 | /// Convenient wrapper of IOHIDCallback in which you can capture other variables. 9 | public typealias HIDCallback = (_ error: KernelError?, _ sender: Sender?) -> Void 10 | 11 | class HIDCallbackContext: HIDCallbackToken { 12 | 13 | let callback: HIDCallback 14 | var onDeinit: (() -> Void)? = nil 15 | 16 | init(_ callback: @escaping HIDCallback) { 17 | self.callback = callback 18 | } 19 | 20 | deinit { 21 | onDeinit?() 22 | } 23 | 24 | func callAsFunction(result: IOReturn, sender: UnsafeMutableRawPointer?) { 25 | let sender = sender?.assumingMemoryBound(to: Sender.self).pointee 26 | let error = KernelError(rawValue: result) 27 | callback(error, sender) 28 | } 29 | } 30 | 31 | /// Convenient wrapper of IOHIDValueCallback in which you can capture other variables. 32 | public typealias HIDValueCallback = (_ value: IOHIDValue, _ error: KernelError?, _ sender: Sender?) -> Void 33 | 34 | class HIDValueCallbackContext: HIDCallbackToken { 35 | 36 | let callback: HIDValueCallback 37 | var onDeinit: (() -> Void)? = nil 38 | 39 | init(_ callback: @escaping HIDValueCallback) { 40 | self.callback = callback 41 | } 42 | 43 | deinit { 44 | onDeinit?() 45 | } 46 | 47 | func callAsFunction(result: IOReturn, sender: UnsafeMutableRawPointer?, value: IOHIDValue) { 48 | let sender = sender?.assumingMemoryBound(to: Sender.self).pointee 49 | let error = KernelError(rawValue: result) 50 | callback(value, error, sender) 51 | } 52 | } 53 | 54 | /// Convenient wrapper of IOHIDValueMultipleCallback in which you can capture other variables. 55 | public typealias HIDValueMultipleCallback = (_ values: [IOHIDElement: IOHIDValue], _ error: KernelError?, _ sender: Sender?) -> Void 56 | 57 | class HIDValueMultipleCallbackContext: HIDCallbackToken { 58 | 59 | let callback: HIDValueMultipleCallback 60 | var onDeinit: (() -> Void)? = nil 61 | 62 | init(_ callback: @escaping HIDValueMultipleCallback) { 63 | self.callback = callback 64 | } 65 | 66 | deinit { 67 | onDeinit?() 68 | } 69 | 70 | func callAsFunction(result: IOReturn, sender: UnsafeMutableRawPointer?, values: CFDictionary) { 71 | let sender = sender?.assumingMemoryBound(to: Sender.self).pointee 72 | let error = KernelError(rawValue: result) 73 | callback(values.asSwift(), error, sender) 74 | } 75 | } 76 | 77 | /// Convenient wrapper of IOHIDReportCallback in which you can capture other variables. 78 | public typealias HIDReportCallback = (_ type: IOHIDReportType, _ reportID: UInt32, _ report: UnsafeBufferPointer, _ error: KernelError?, _ sender: Sender?) -> Void 79 | 80 | class HIDReportCallbackContext: HIDCallbackToken { 81 | 82 | let callback: HIDReportCallback 83 | var onDeinit: (() -> Void)? = nil 84 | 85 | let reportBuffer: UnsafeMutableBufferPointer 86 | 87 | init(reportSize: Int, _ callback: @escaping HIDReportCallback) { 88 | self.reportBuffer = reportSize > 0 ? .allocate(capacity: reportSize) : .init(start: nil, count: 0) 89 | self.callback = callback 90 | } 91 | 92 | deinit { 93 | reportBuffer.deallocate() 94 | onDeinit?() 95 | } 96 | 97 | func callAsFunction(result: IOReturn, sender: UnsafeMutableRawPointer?, type: IOHIDReportType, reportID: UInt32, report: UnsafeMutablePointer, reportLength: CFIndex) { 98 | let sender = sender?.assumingMemoryBound(to: Sender.self).pointee 99 | let error = KernelError(rawValue: result) 100 | let report = UnsafeBufferPointer(start: report, count: reportLength) 101 | callback(type, reportID, report, error, sender) 102 | } 103 | } 104 | 105 | /// Convenient wrapper of IOHIDReportWithTimeStampCallback in which you can capture other variables. 106 | public typealias HIDReportWithTimeStampCallback = (_ type: IOHIDReportType, _ reportID: UInt32, _ report: UnsafeBufferPointer, _ timeStamp: UInt64, _ error: KernelError?, _ sender: Sender?) -> Void 107 | 108 | class HIDReportWithTimeStampCallbackContext: HIDCallbackToken { 109 | 110 | let callback: HIDReportWithTimeStampCallback 111 | var onDeinit: (() -> Void)? = nil 112 | 113 | let reportBuffer: UnsafeMutableBufferPointer 114 | 115 | init(reportSize: Int, _ callback: @escaping HIDReportWithTimeStampCallback) { 116 | self.reportBuffer = reportSize > 0 ? .allocate(capacity: reportSize) : .init(start: nil, count: 0) 117 | self.callback = callback 118 | } 119 | 120 | deinit { 121 | reportBuffer.deallocate() 122 | onDeinit?() 123 | } 124 | 125 | func callAsFunction(result: IOReturn, sender: UnsafeMutableRawPointer?, type: IOHIDReportType, reportID: UInt32, report: UnsafeMutablePointer, reportLength: CFIndex, timeStamp: UInt64) { 126 | let sender = sender?.assumingMemoryBound(to: Sender.self).pointee 127 | let error = KernelError(rawValue: result) 128 | let report = UnsafeBufferPointer(start: report, count: reportLength) 129 | callback(type, reportID, report, timeStamp, error, sender) 130 | } 131 | } 132 | 133 | /// Convenient wrapper of IOHIDDeviceCallback in which you can capture other variables. 134 | public typealias HIDDeviceCallback = (_ device: IOHIDDevice, _ error: KernelError?, _ sender: Sender?) -> Void 135 | 136 | class HIDDeviceCallbackContext: HIDCallbackToken { 137 | 138 | let callback: HIDDeviceCallback 139 | var onDeinit: (() -> Void)? = nil 140 | 141 | init(_ callback: @escaping HIDDeviceCallback) { 142 | self.callback = callback 143 | } 144 | 145 | deinit { 146 | onDeinit?() 147 | } 148 | 149 | func callAsFunction(result: IOReturn, sender: UnsafeMutableRawPointer?, device: IOHIDDevice) { 150 | let sender = sender?.assumingMemoryBound(to: Sender.self).pointee 151 | let error = KernelError(rawValue: result) 152 | callback(device, error, sender) 153 | } 154 | } 155 | 156 | #endif // canImport(IOKit) 157 | -------------------------------------------------------------------------------- /Sources/IOKitExt/HID/IOHIDExt/HIDElementKey.swift: -------------------------------------------------------------------------------- 1 | #if canImport(IOKit) 2 | 3 | import SwiftCF 4 | import IOKit.hid 5 | 6 | public struct HIDElementKey: CFStringKey { 7 | 8 | public let rawValue: CFString 9 | 10 | public init(_ key: CFString) { 11 | rawValue = key 12 | } 13 | } 14 | 15 | private func key(_ key: String) -> HIDElementKey { 16 | return key as CFString as HIDElementKey 17 | } 18 | 19 | public extension HIDElementKey { 20 | /// Keys that represents an element property. 21 | /// 22 | /// Property for a HID Device or element dictionary. Elements can be 23 | /// heirarchical, so they can contain other elements. 24 | static let element = key(kIOHIDElementKey) 25 | 26 | static let cookie = key(kIOHIDElementCookieKey) 27 | static let type = key(kIOHIDElementTypeKey) 28 | static let collectionType = key(kIOHIDElementCollectionTypeKey) 29 | static let usage = key(kIOHIDElementUsageKey) 30 | static let usagePage = key(kIOHIDElementUsagePageKey) 31 | static let min = key(kIOHIDElementMinKey) 32 | static let max = key(kIOHIDElementMaxKey) 33 | static let scaledMin = key(kIOHIDElementScaledMinKey) 34 | static let scaledMax = key(kIOHIDElementScaledMaxKey) 35 | static let size = key(kIOHIDElementSizeKey) 36 | static let reportSize = key(kIOHIDElementReportSizeKey) 37 | static let reportCount = key(kIOHIDElementReportCountKey) 38 | static let reportID = key(kIOHIDElementReportIDKey) 39 | static let isArray = key(kIOHIDElementIsArrayKey) 40 | static let isRelative = key(kIOHIDElementIsRelativeKey) 41 | static let isWrapping = key(kIOHIDElementIsWrappingKey) 42 | static let isNonLinear = key(kIOHIDElementIsNonLinearKey) 43 | static let hasPreferredState = key(kIOHIDElementHasPreferredStateKey) 44 | static let hasNullState = key(kIOHIDElementHasNullStateKey) 45 | static let flags = key(kIOHIDElementFlagsKey) 46 | static let unit = key(kIOHIDElementUnitKey) 47 | static let unitExponent = key(kIOHIDElementUnitExponentKey) 48 | static let name = key(kIOHIDElementNameKey) 49 | static let valueLocation = key(kIOHIDElementValueLocationKey) 50 | static let duplicateIndex = key(kIOHIDElementDuplicateIndexKey) 51 | static let parentCollection = key(kIOHIDElementParentCollectionKey) 52 | static let variableSize = key(kIOHIDElementVariableSizeKey) 53 | static let vendorSpecific = key(kIOHIDElementVendorSpecificKey) 54 | 55 | /// The minimum bounds for a calibrated value. 56 | static let calibrationMin = key(kIOHIDElementCalibrationMinKey) 57 | 58 | /// The maximum bounds for a calibrated value. 59 | static let calibrationMax = key(kIOHIDElementCalibrationMaxKey) 60 | 61 | /// The mininum tolerance to be used when calibrating a logical element 62 | /// value. 63 | /// The saturation property is used to allow for slight differences in the 64 | /// minimum and maximum value returned by an element. 65 | static let calibrationSaturationMin = key(kIOHIDElementCalibrationSaturationMinKey) 66 | 67 | /// The maximum tolerance to be used when calibrating a logical element 68 | /// value. 69 | /// The saturation property is used to allow for slight differences in the 70 | /// minimum and maximum value returned by an element. 71 | static let calibrationSaturationMax = key(kIOHIDElementCalibrationSaturationMaxKey) 72 | 73 | /// The minimum bounds near the midpoint of a logical value in which the 74 | /// value is ignored. 75 | /// The dead zone property is used to allow for slight differences in the 76 | /// idle value returned by an element. 77 | static let calibrationDeadZoneMin = key(kIOHIDElementCalibrationDeadZoneMinKey) 78 | 79 | /// The maximum bounds near the midpoint of a logical value in which the 80 | /// value is ignored. 81 | /// The dead zone property is used to allow for slight differences in the 82 | /// idle value returned by an element. 83 | static let calibrationDeadZoneMax = key(kIOHIDElementCalibrationDeadZoneMaxKey) 84 | 85 | /// The scale or level of detail returned in a calibrated element value. 86 | /// Values are rounded off such that if granularity=0.1, values after 87 | /// calibration are 0, 0.1, 0.2, 0.3, etc. 88 | static let calibrationGranularity = key(kIOHIDElementCalibrationGranularityKey) 89 | } 90 | 91 | /// Keys used for matching particular elements. 92 | /// 93 | /// These keys should only be used with a matching dictionary when searching for 94 | /// elements via copyMatchingElements. 95 | public extension HIDElementKey { 96 | static let cookieMin = key(kIOHIDElementCookieMinKey) 97 | static let cookieMax = key(kIOHIDElementCookieMaxKey) 98 | static let usageMin = key(kIOHIDElementUsageMinKey) 99 | static let usageMax = key(kIOHIDElementUsageMaxKey) 100 | } 101 | 102 | #endif // canImport(IOKit) 103 | -------------------------------------------------------------------------------- /Sources/IOKitExt/HID/IOHIDExt/HIDEnums.swift: -------------------------------------------------------------------------------- 1 | #if canImport(IOKit) 2 | 3 | import IOKit.hid 4 | 5 | public extension IOHIDReportType { 6 | static let input = kIOHIDReportTypeInput 7 | static let output = kIOHIDReportTypeOutput 8 | static let feature = kIOHIDReportTypeFeature 9 | static let count = kIOHIDReportTypeCount 10 | } 11 | 12 | public extension IOHIDElementCommitDirection { 13 | static let `in` = kIOHIDElementCommitDirectionIn 14 | static let out = kIOHIDElementCommitDirectionOut 15 | } 16 | 17 | public extension IOHIDElementType { 18 | static let input_Misc = kIOHIDElementTypeInput_Misc 19 | static let input_Button = kIOHIDElementTypeInput_Button 20 | static let input_Axis = kIOHIDElementTypeInput_Axis 21 | static let input_ScanCodes = kIOHIDElementTypeInput_ScanCodes 22 | static let input_NULL = kIOHIDElementTypeInput_NULL 23 | static let output = kIOHIDElementTypeOutput 24 | static let feature = kIOHIDElementTypeFeature 25 | static let collection = kIOHIDElementTypeCollection 26 | } 27 | 28 | public extension IOHIDElementCollectionType { 29 | static let physical = kIOHIDElementCollectionTypePhysical 30 | static let application = kIOHIDElementCollectionTypeApplication 31 | static let logical = kIOHIDElementCollectionTypeLogical 32 | static let report = kIOHIDElementCollectionTypeReport 33 | static let namedArray = kIOHIDElementCollectionTypeNamedArray 34 | static let usageSwitch = kIOHIDElementCollectionTypeUsageSwitch 35 | static let usageModifier = kIOHIDElementCollectionTypeUsageModifier 36 | } 37 | 38 | #endif // canImport(IOKit) 39 | -------------------------------------------------------------------------------- /Sources/IOKitExt/HID/IOHIDExt/HIDPage.swift: -------------------------------------------------------------------------------- 1 | #if canImport(IOKit) 2 | 3 | import IOKit.hid 4 | 5 | public struct HIDPage: RawRepresentable { 6 | 7 | public let rawValue: UInt32 8 | 9 | public init(rawValue: UInt32) { 10 | self.rawValue = rawValue 11 | } 12 | } 13 | 14 | extension HIDPage: Equatable, Hashable {} 15 | 16 | extension HIDPage: Comparable { 17 | 18 | public static func < (lhs: HIDPage, rhs: HIDPage) -> Bool { 19 | return lhs.rawValue < rhs.rawValue 20 | } 21 | } 22 | 23 | private func page(_ raw: Int) -> HIDPage { 24 | return HIDPage(rawValue: UInt32(raw)) 25 | } 26 | 27 | public extension HIDPage { 28 | static let undefined = page(kHIDPage_Undefined) 29 | static let genericDesktop = page(kHIDPage_GenericDesktop) 30 | static let simulation = page(kHIDPage_Simulation) 31 | static let vr = page(kHIDPage_VR) 32 | static let sport = page(kHIDPage_Sport) 33 | static let game = page(kHIDPage_Game) 34 | static let genericDeviceControls = page(kHIDPage_GenericDeviceControls) 35 | /// USB Device Class Definition for Human Interface Devices (HID). Note: the 36 | /// usage type for all key codes is Selector (Sel). 37 | static let keyboardOrKeypad = page(kHIDPage_KeyboardOrKeypad) 38 | static let leds = page(kHIDPage_LEDs) 39 | static let button = page(kHIDPage_Button) 40 | static let ordinal = page(kHIDPage_Ordinal) 41 | static let telephony = page(kHIDPage_Telephony) 42 | static let consumer = page(kHIDPage_Consumer) 43 | static let digitizer = page(kHIDPage_Digitizer) 44 | 45 | /* Reserved 0x0E */ 46 | 47 | /// USB Physical Interface Device definitions for force feedback and related 48 | /// devices. 49 | static let pid = page(kHIDPage_PID) 50 | static let unicode = page(kHIDPage_Unicode) 51 | 52 | /* Reserved 0x11 - 0x13 */ 53 | 54 | static let alphanumericDisplay = page(kHIDPage_AlphanumericDisplay) 55 | 56 | /* Reserved 0x15 - 0x1F */ 57 | 58 | static let sensor = page(kHIDPage_Sensor) 59 | 60 | /* Reserved 0x21 - 0x7f */ 61 | 62 | static let monitor = page(kHIDPage_Monitor) 63 | static let monitorEnumerated = page(kHIDPage_MonitorEnumerated) 64 | static let monitorVirtual = page(kHIDPage_MonitorVirtual) 65 | static let monitorReserved = page(kHIDPage_MonitorReserved) 66 | 67 | /* Power 0x84 - 0x87 USB Device Class Definition for Power Devices */ 68 | 69 | /// Power Device Page 70 | static let powerDevice = page(kHIDPage_PowerDevice) 71 | /// Battery System Page 72 | static let batterySystem = page(kHIDPage_BatterySystem) 73 | static let powerReserved = page(kHIDPage_PowerReserved) 74 | static let powerReserved2 = page(kHIDPage_PowerReserved2) 75 | 76 | /* Reserved 0x88 - 0x8B */ 77 | 78 | /// (Point of Sale) USB Device Class Definition for Bar Code Scanner Devices 79 | static let barCodeScanner = page(kHIDPage_BarCodeScanner) 80 | /// (Point of Sale) USB Device Class Definition for Weighing Devices 81 | static let weighingDevice = page(kHIDPage_WeighingDevice) 82 | /// (Point of Sale) USB Device Class Definition for Scale Devices 83 | static let scale = page(kHIDPage_Scale) 84 | static let magneticStripeReader = page(kHIDPage_MagneticStripeReader) 85 | 86 | /* ReservedPointofSalepages 0x8F */ 87 | 88 | /// USB Device Class Definition for Image Class Devices 89 | static let cameraControl = page(kHIDPage_CameraControl) 90 | /// OAAF Definitions for arcade and coinop related Devices 91 | static let arcade = page(kHIDPage_Arcade) 92 | 93 | /* Reserved 0x92 - 0xFEFF */ 94 | 95 | /// VendorDefined 0xFF00 - 0xFFFF 96 | static let vendorDefinedStart = page(kHIDPage_VendorDefinedStart) 97 | } 98 | 99 | #endif // canImport(IOKit) 100 | -------------------------------------------------------------------------------- /Sources/IOKitExt/HID/IOHIDExt/HIDPropertyKey.swift: -------------------------------------------------------------------------------- 1 | #if canImport(IOKit) 2 | 3 | import SwiftCF 4 | import IOKit.hid 5 | 6 | public struct HIDPropertyKey: CFStringKey { 7 | 8 | public let rawValue: CFString 9 | 10 | public init(_ key: CFString) { 11 | rawValue = key 12 | } 13 | } 14 | 15 | private func key(_ key: String) -> HIDPropertyKey { 16 | return key as CFString as HIDPropertyKey 17 | } 18 | 19 | public extension HIDPropertyKey { 20 | /// Number property that describes the transport of the device. 21 | static let transport = key(kIOHIDTransportKey) 22 | /// Number property that describes the vendor ID of the device. 23 | static let vendorID = key(kIOHIDVendorIDKey) 24 | /// Number property that describes the product ID of the device. 25 | static let productID = key(kIOHIDProductIDKey) 26 | /// Number property that describes the version number of the device. 27 | static let versionNumber = key(kIOHIDVersionNumberKey) 28 | /// String property that describes the manufacturer of the device. 29 | static let manufacturer = key(kIOHIDManufacturerKey) 30 | /// String property that describes the product of the device. 31 | static let product = key(kIOHIDProductKey) 32 | /// String property that describes the serial number of the device. 33 | static let serialNumber = key(kIOHIDSerialNumberKey) 34 | /// Number property that describes the country code of the device. 35 | static let countryCode = key(kIOHIDCountryCodeKey) 36 | /// Number property that describes the location ID of the device. 37 | static let locationID = key(kIOHIDLocationIDKey) 38 | /// Array property that describes the top level usages of the device. The 39 | /// array will have dictionaries of usage pages/usages of each top level 40 | /// collection that exists on the device. 41 | static let deviceUsagePairs = key(kIOHIDDeviceUsagePairsKey) 42 | /// Number property used in the device usage pairs array above. Describes a 43 | /// usage of the device. 44 | static let deviceUsage = key(kIOHIDDeviceUsageKey) 45 | /// Number property used in the device usage pairs array above. Describes a 46 | /// usage page of the device. 47 | static let deviceUsagePage = key(kIOHIDDeviceUsagePageKey) 48 | /// Number property that describes the primary usage page of the device. 49 | static let primaryUsage = key(kIOHIDPrimaryUsageKey) 50 | /// Number property that describes the primary usage of the device. 51 | static let primaryUsagePage = key(kIOHIDPrimaryUsagePageKey) 52 | /// Number property that describes the max input report size of the device. 53 | /// This is derived from the report descriptor data provided in the 54 | /// kIOHIDReportDescriptorKey key. 55 | static let maxInputReportSize = key(kIOHIDMaxInputReportSizeKey) 56 | /// Number property that describes the max output report size of the device. 57 | /// This is derived from the report descriptor data provided in the 58 | /// kIOHIDReportDescriptorKey key. 59 | static let maxOutputReportSize = key(kIOHIDMaxOutputReportSizeKey) 60 | /// Number property that describes the max feature report size of the 61 | /// device. This is derived from the report descriptor data provided in the 62 | /// kIOHIDReportDescriptorKey key. 63 | static let maxFeatureReportSize = key(kIOHIDMaxFeatureReportSizeKey) 64 | /// Number property set on the device from a client that describes the 65 | /// interval at which the client wishes to receive reports. It is up to the 66 | /// device to determine how to handle this key, if it chooses to do so. 67 | static let reportInterval = key(kIOHIDReportIntervalKey) 68 | /// Number property set on the device from a client that describes the 69 | /// interval at which the client wishes to receive batched reports. It is up 70 | /// to the device to determine how to handle this key, if it chooses to do 71 | /// so. 72 | static let batchInterval = key(kIOHIDBatchIntervalKey) 73 | /// Number property that describes the request timeout in us for 74 | /// get/setReport calls. It is up to the device to determine how to handle 75 | /// this key, if it chooses to do so. 76 | static let requestTimeout = key(kIOHIDRequestTimeoutKey) 77 | /// Data property that describes the report descriptor of the device. 78 | static let reportDescriptor = key(kIOHIDReportDescriptorKey) 79 | /// Number property that describes if the device is built in. 80 | static let builtIn = key(kIOHIDBuiltInKey) 81 | /// String property that describes a unique identifier of the device. 82 | static let physicalDeviceUniqueID = key(kIOHIDPhysicalDeviceUniqueIDKey) 83 | } 84 | 85 | public extension HIDPropertyKey { 86 | static let vendorIDSource = key(kIOHIDVendorIDSourceKey) 87 | static let standardType = key(kIOHIDStandardTypeKey) 88 | static let sampleInterval = key(kIOHIDSampleIntervalKey) 89 | static let reset = key(kIOHIDResetKey) 90 | static let keyboardLanguage = key(kIOHIDKeyboardLanguageKey) 91 | static let altHandlerId = key(kIOHIDAltHandlerIdKey) 92 | static let displayIntegrated = key(kIOHIDDisplayIntegratedKey) 93 | static let productIDMask = key(kIOHIDProductIDMaskKey) 94 | static let productIDArray = key(kIOHIDProductIDArrayKey) 95 | static let powerOnDelayNS = key(kIOHIDPowerOnDelayNSKey) 96 | static let category = key(kIOHIDCategoryKey) 97 | static let maxResponseLatency = key(kIOHIDMaxResponseLatencyKey) 98 | static let uniqueID = key(kIOHIDUniqueIDKey) 99 | static let modelNumber = key(kIOHIDModelNumberKey) 100 | } 101 | 102 | #endif // canImport(IOKit) 103 | -------------------------------------------------------------------------------- /Sources/IOKitExt/HID/IOHIDExt/IOHIDTransaction.swift: -------------------------------------------------------------------------------- 1 | #if canImport(IOKit) 2 | 3 | import SwiftCF 4 | import IOKit.hid 5 | 6 | public extension IOHIDTransaction { 7 | 8 | /// Direction for an IOHIDDeviceTransactionInterface. 9 | typealias Direction = IOHIDTransactionDirectionType 10 | 11 | /// Various options that can be supplied to IOHIDTransaction functions. 12 | typealias Options = IOHIDTransactionOptions 13 | 14 | /// Creates an IOHIDTransaction object for the specified device. 15 | /// 16 | /// IOHIDTransaction objects can be used to either send or receive multiple 17 | /// element values. As such the direction used should represent they type of 18 | /// objects added to the transaction. 19 | /// 20 | /// - Parameters: 21 | /// - allocator: Allocator to be used during creation. 22 | /// - device: IOHIDDevice object 23 | /// - direction: The direction, either in or out, for the transaction. 24 | /// - options: Reserved for future use. 25 | /// - Returns: Returns a new IOHIDTransactionRef. 26 | @inlinable static func create(allocator: CFAllocator = .default, device: IOHIDDevice, direction: Direction, options: Options) -> IOHIDTransaction? { 27 | return IOHIDTransactionCreate(allocator, device, direction, options.rawValue) 28 | } 29 | 30 | /// Obtain the device associated with the transaction. 31 | @inlinable var device: IOHIDDevice { 32 | return IOHIDTransactionGetDevice(self) 33 | } 34 | 35 | /// Obtain the device associated with the transaction. 36 | @inlinable var direction: Direction { 37 | get { 38 | return IOHIDTransactionGetDirection(self) 39 | } 40 | set { 41 | IOHIDTransactionSetDirection(self, newValue) 42 | } 43 | } 44 | 45 | /// Adds an element to the transaction 46 | /// 47 | /// To minimize device traffic it is important to add elements that share a 48 | /// common report type and report id. 49 | /// 50 | /// - Parameter element: Element to be added to the transaction. 51 | @inlinable func add(element: IOHIDElement) { 52 | IOHIDTransactionAddElement(self, element) 53 | } 54 | 55 | /// Removes an element to the transaction 56 | /// 57 | /// - Parameter element: Element to be removed to the transaction. 58 | @inlinable func remove(element: IOHIDElement) { 59 | IOHIDTransactionRemoveElement(self, element) 60 | } 61 | 62 | /// Queries the transaction to determine if elemement has been added. 63 | /// 64 | /// - Parameter element: Element to be queried. 65 | /// - Returns: Returns true or false depending if element is present. 66 | @inlinable func contains(element: IOHIDElement) -> Bool { 67 | return IOHIDTransactionContainsElement(self, element) 68 | } 69 | 70 | /// Schedules transaction with run loop. 71 | /// 72 | /// Formally associates transaction with client's run loop. Scheduling this 73 | /// transaction with the run loop is necessary before making use of any 74 | /// asynchronous APIs. 75 | /// 76 | /// - Parameters: 77 | /// - runLoop: RunLoop to be used when scheduling any asynchronous 78 | /// activity. 79 | /// - mode: Run loop mode to be used when scheduling any asynchronous 80 | /// activity. 81 | @inlinable func schedule(with runLoop: CFRunLoop, mode: CFRunLoopMode) { 82 | IOHIDTransactionScheduleWithRunLoop(self, runLoop, mode.rawValue) 83 | } 84 | 85 | /// Unschedules transaction with run loop. 86 | /// 87 | /// Formally disassociates transaction with client's run loop. 88 | /// 89 | /// - Parameters: 90 | /// - runLoop: RunLoop to be used when scheduling any asynchronous 91 | /// activity. 92 | /// - mode: Run loop mode to be used when scheduling any asynchronous 93 | /// activity. 94 | @inlinable func unschedule(from runLoop: CFRunLoop, mode: CFRunLoopMode) { 95 | IOHIDTransactionUnscheduleFromRunLoop(self, runLoop, mode.rawValue) 96 | } 97 | 98 | /// Sets the value for a transaction element. 99 | /// 100 | /// The value set is pended until the transaction is committed and is only 101 | /// used if the transaction direction is .output. Use the 102 | /// .defaultOutputValue option to set the default element value. 103 | /// 104 | /// - Parameters: 105 | /// - value: Value to be set for the given element. 106 | /// - element: Element to be modified after a commit. 107 | /// - options: See IOHIDTransactionOption. 108 | @inlinable func setValue(_ value: IOHIDValue, for element: IOHIDElement, options: Options) { 109 | IOHIDTransactionSetValue(self, element, value, options.rawValue) 110 | } 111 | 112 | /// Obtains the value for a transaction element. 113 | /// 114 | /// If the transaction direction is .input the value represents what was 115 | /// obtained from the device from the transaction. Otherwise, if the 116 | /// transaction direction is .output the value represents the pending value 117 | /// to be sent to the device. Use the .defaultOutputValue option to get the 118 | /// default element value. 119 | /// 120 | /// - Parameters: 121 | /// - element: Element to be queried. 122 | /// - options: See IOHIDTransactionOption. 123 | /// - Returns: Returns IOHIDValueRef for the given element. 124 | @inlinable func value(for element: IOHIDElement, options: Options) -> IOHIDValue? { 125 | return IOHIDTransactionGetValue(self, element, options.rawValue) 126 | } 127 | 128 | /// Synchronously commits element transaction to the device. 129 | /// 130 | /// In regards to .output direction, default element values will be used if 131 | /// element values are not set. If neither are set, that element will be 132 | /// omitted from the commit. After a transaction is committed, transaction 133 | /// element values will be cleared and default values preserved. 134 | /// 135 | /// - Throws: Throws IOError if failed. 136 | @inlinable func commit() throws { 137 | try IOHIDTransactionCommit(self).throwIfIOError() 138 | } 139 | 140 | /// Commits element transaction to the device. 141 | /// 142 | /// In regards to .output direction, default element values will be used if 143 | /// element values are not set. If neither are set, that element will be 144 | /// omitted from the commit. After a transaction is committed, transaction 145 | /// element values will be cleared and default values preserved. Note: It is 146 | /// possible for elements from different reports to be present in a given 147 | /// transaction causing a commit to transcend multiple reports. Keep this in 148 | /// mind when setting a appropriate timeout. 149 | /// 150 | /// - Parameters: 151 | /// - timeout: Timeout for issuing the transaction. 152 | /// - callback: Callback of type HIDCallback to be used when transaction 153 | /// has been completed. If null, this method will behave synchronously. 154 | /// - Throws: Throws IOError if failed. 155 | func commit(timeout: CFTimeInterval, callback: HIDCallback?) throws { 156 | var pContext: UnsafeMutableRawPointer? 157 | var pCallback: IOHIDCallback? 158 | if let callback = callback { 159 | let ctx = HIDCallbackContext(callback) 160 | pContext = Unmanaged.passRetained(ctx).toOpaque() 161 | pCallback = { ctx, result, sender in 162 | Unmanaged> 163 | .fromOpaque(ctx!) 164 | .takeRetainedValue() 165 | .callAsFunction(result: result, sender: sender) 166 | } 167 | } 168 | try IOHIDTransactionCommitWithCallback(self, timeout, pCallback, pContext).throwIfIOError() 169 | } 170 | 171 | /// Clears element transaction values. 172 | /// 173 | /// In regards to .output direction, default element values will be 174 | /// preserved. 175 | @inlinable func clear() { 176 | IOHIDTransactionClear(self) 177 | } 178 | } 179 | 180 | public extension IOHIDTransaction.Options { 181 | 182 | /// Options to be used in conjuntion with an IOHIDDeviceTransactionInterface. 183 | /// 184 | /// kIOHIDTransactionOptionDefaultOutputValue Option to set the default 185 | /// element value to be used with an IOHIDDeviceTransactionInterface of 186 | /// direction kIOHIDTransactionDirectionTypeOutput. 187 | static let defaultOutputValue = IOHIDTransaction.Options(rawValue: kIOHIDTransactionOptionDefaultOutputValue) 188 | } 189 | 190 | #endif // canImport(IOKit) 191 | -------------------------------------------------------------------------------- /Sources/IOKitExt/HID/IOHIDExt/IOHIDValue.swift: -------------------------------------------------------------------------------- 1 | #if canImport(IOKit) 2 | 3 | import IOKit.hid 4 | 5 | public extension IOHIDValue { 6 | 7 | /// Creates a new element value using an integer value. 8 | /// 9 | /// IOHIDValueGetTimeStamp should represent OS AbsoluteTime, not 10 | /// CFAbsoluteTime. To obtain the OS AbsoluteTime, please reference the APIs 11 | /// declared in 12 | /// 13 | /// - Parameters: 14 | /// - allocator: The CFAllocator which should be used to allocate memory 15 | /// for the value. This parameter may be NULL in which case the current 16 | /// default CFAllocator is used. If this reference is not a valid 17 | /// CFAllocator, the behavior is undefined. 18 | /// - element: IOHIDElementRef associated with this value. 19 | /// - timeStamp: OS absolute time timestamp for this value. 20 | /// - value: Integer value to be copied to this object. 21 | /// - Returns: Returns a reference to a new IOHIDValueRef. 22 | @inlinable static func create(allocator: CFAllocator = .default, element: IOHIDElement, timeStamp: UInt64 = mach_absolute_time(), value: CFIndex) -> IOHIDValue? { 23 | return IOHIDValueCreateWithIntegerValue(allocator, element, timeStamp, value) 24 | } 25 | 26 | /// Creates a new element value using byte data. 27 | /// 28 | /// IOHIDValueGetTimeStamp should represent OS AbsoluteTime, not 29 | /// CFAbsoluteTime. To obtain the OS AbsoluteTime, please reference the APIs 30 | /// declared in 31 | /// 32 | /// - Parameters: 33 | /// - allocator: The CFAllocator which should be used to allocate memory 34 | /// for the value. This parameter may be NULL in which case the current 35 | /// default CFAllocator is used. If this reference is not a valid 36 | /// CFAllocator, the behavior is undefined. 37 | /// - element: IOHIDElementRef associated with this value. 38 | /// - timeStamp: OS absolute time timestamp for this value. 39 | /// - bytes: Pointer to a buffer of uint8_t to be copied to this object. 40 | /// - Returns: Returns a reference to a new IOHIDValueRef. 41 | @inlinable static func create(allocator: CFAllocator = .default, element: IOHIDElement, timeStamp: UInt64 = mach_absolute_time(), bytes: UnsafeBufferPointer) -> IOHIDValue? { 42 | return IOHIDValueCreateWithBytes(allocator, element, timeStamp, bytes.baseAddress!, bytes.count) 43 | } 44 | 45 | /// Returns the element value associated with this IOHIDValueRef. 46 | @inlinable var element: IOHIDElement { 47 | return IOHIDValueGetElement(self) 48 | } 49 | 50 | /// Returns the timestamp value contained in this IOHIDValueRef. 51 | /// 52 | /// The timestamp value returned represents OS AbsoluteTime, not 53 | /// CFAbsoluteTime. 54 | @inlinable var timeStamp: UInt64 { 55 | return IOHIDValueGetTimeStamp(self) 56 | } 57 | 58 | /// Returns the size, in bytes, of the value contained in this IOHIDValueRef. 59 | @inlinable var length: CFIndex { 60 | return IOHIDValueGetLength(self) 61 | } 62 | 63 | /// Returns a byte pointer to the value contained in this IOHIDValueRef. 64 | @inlinable var bytePtr: UnsafePointer { 65 | return IOHIDValueGetBytePtr(self) 66 | } 67 | 68 | /// Returns a byte pointer to the value contained in this IOHIDValueRef. 69 | var bytes: UnsafeBufferPointer { 70 | return UnsafeBufferPointer(start: bytePtr, count: length) 71 | } 72 | 73 | /// Returns an integer representaion of the value contained in this 74 | /// IOHIDValueRef. 75 | /// 76 | /// The value is based on the logical element value contained in the report 77 | /// returned by the device. 78 | @inlinable var integerValue: CFIndex { 79 | return IOHIDValueGetIntegerValue(self) 80 | } 81 | 82 | /// Returns an scaled representaion of the value contained in this 83 | /// IOHIDValueRef based on the scale type. 84 | /// 85 | /// The scaled value is based on the range described by the scale type's min 86 | /// and max, such that: scaledValue = ((value - min) * (scaledMax - 87 | /// scaledMin) / (max - min)) + scaledMin Note: There are currently two 88 | /// types of scaling that can be applied: kIOHIDValueScaleTypePhysical: 89 | /// Scales element value using the physical bounds of the device such that 90 | /// scaledMin = physicalMin and scaledMax = physicalMax. 91 | /// kIOHIDValueScaleTypeCalibrated: Scales element value such that scaledMin 92 | /// = -1 and scaledMax = 1. This value will also take into account the 93 | /// calibration properties associated with this element. 94 | /// 95 | /// - Parameter type: The type of scaling to be performed. 96 | /// - Returns: Returns an scaled floating point representation of the value. 97 | @inlinable func scaledValue(type: ScaleType) -> double_t { 98 | return IOHIDValueGetScaledValue(self, type.rawValue) 99 | } 100 | } 101 | 102 | extension IOHIDValue { 103 | 104 | /// Describes different types of scaling that can be performed on element 105 | /// values. 106 | public struct ScaleType: RawRepresentable { 107 | 108 | public let rawValue: IOHIDValueScaleType 109 | 110 | public init(rawValue: IOHIDValueScaleType) { 111 | self.rawValue = rawValue 112 | } 113 | } 114 | } 115 | 116 | public extension IOHIDValue.ScaleType { 117 | /// Type for value that is scaled with respect to the calibration properties. 118 | static let calibrated = IOHIDValue.ScaleType(rawValue: IOHIDValueScaleType(kIOHIDValueScaleTypeCalibrated)) 119 | /// Type for value that is scaled with respect to the physical min and 120 | /// physical max of the element. 121 | static let physical = IOHIDValue.ScaleType(rawValue: IOHIDValueScaleType(kIOHIDValueScaleTypePhysical)) 122 | /// Type for value that is scaled with respect to the element's unit 123 | /// exponent. 124 | static let exponent = IOHIDValue.ScaleType(rawValue: IOHIDValueScaleType(kIOHIDValueScaleTypeExponent)) 125 | } 126 | 127 | #endif // canImport(IOKit) 128 | -------------------------------------------------------------------------------- /Sources/IOKitExt/HID/IOHIDType.swift: -------------------------------------------------------------------------------- 1 | #if canImport(IOKit) 2 | 3 | import SwiftCF 4 | import IOKit.hid 5 | 6 | extension IOHIDQueue: CFType { 7 | public static let typeID = IOHIDQueueGetTypeID() 8 | } 9 | 10 | extension IOHIDDevice: CFType { 11 | public static let typeID = IOHIDDeviceGetTypeID() 12 | } 13 | 14 | extension IOHIDElement: CFType { 15 | public static let typeID = IOHIDElementGetTypeID() 16 | } 17 | 18 | extension IOHIDManager: CFType { 19 | public static let typeID = IOHIDManagerGetTypeID() 20 | } 21 | 22 | extension IOHIDValue: CFType { 23 | public static let typeID = IOHIDValueGetTypeID() 24 | } 25 | 26 | extension IOHIDTransaction: CFType { 27 | public static let typeID = IOHIDTransactionGetTypeID() 28 | } 29 | 30 | #endif // canImport(IOKit) 31 | -------------------------------------------------------------------------------- /Sources/IOKitExt/IOError.swift: -------------------------------------------------------------------------------- 1 | #if canImport(IOKit) 2 | 3 | import SwiftCF 4 | import IOKit 5 | 6 | extension IOReturn { 7 | 8 | @usableFromInline func throwIfIOError() throws { 9 | if let err = KernelError(rawValue: self) { 10 | throw err 11 | } 12 | } 13 | } 14 | 15 | /// A namespace for IOReturn errors. 16 | public enum IOError {} 17 | 18 | public extension IOError { 19 | /// General error 20 | static let error = KernelError(rawValue: kIOReturnError)! 21 | /// Can't allocate memory 22 | static let noMemory = KernelError(rawValue: kIOReturnNoMemory)! 23 | /// Resource shortage 24 | static let noResources = KernelError(rawValue: kIOReturnNoResources)! 25 | /// Error during IPC 26 | static let ipcError = KernelError(rawValue: kIOReturnIPCError)! 27 | /// No such device 28 | static let noDevice = KernelError(rawValue: kIOReturnNoDevice)! 29 | /// Privilege violation 30 | static let notPrivileged = KernelError(rawValue: kIOReturnNotPrivileged)! 31 | /// Invalid argument 32 | static let badArgument = KernelError(rawValue: kIOReturnBadArgument)! 33 | /// Device read locked 34 | static let lockedRead = KernelError(rawValue: kIOReturnLockedRead)! 35 | /// Device write locked 36 | static let lockedWrite = KernelError(rawValue: kIOReturnLockedWrite)! 37 | /// Exclusive access and device already open 38 | static let exclusiveAccess = KernelError(rawValue: kIOReturnExclusiveAccess)! 39 | /// Sent/received messages had different msg_id 40 | static let badMessageID = KernelError(rawValue: kIOReturnBadMessageID)! 41 | /// Unsupported function 42 | static let unsupported = KernelError(rawValue: kIOReturnUnsupported)! 43 | /// Misc. VM failure 44 | static let vmError = KernelError(rawValue: kIOReturnVMError)! 45 | /// Internal error 46 | static let internalError = KernelError(rawValue: kIOReturnInternalError)! 47 | /// General I/O error 48 | static let ioError = KernelError(rawValue: kIOReturnIOError)! 49 | /// Can't acquire lock 50 | static let cannotLock = KernelError(rawValue: kIOReturnCannotLock)! 51 | /// Device not open 52 | static let notOpen = KernelError(rawValue: kIOReturnNotOpen)! 53 | /// Read not supported 54 | static let notReadable = KernelError(rawValue: kIOReturnNotReadable)! 55 | /// Write not supported 56 | static let notWritable = KernelError(rawValue: kIOReturnNotWritable)! 57 | /// Alignment error 58 | static let notAligned = KernelError(rawValue: kIOReturnNotAligned)! 59 | /// Media error 60 | static let badMedia = KernelError(rawValue: kIOReturnBadMedia)! 61 | /// Device(s) still open 62 | static let stillOpen = KernelError(rawValue: kIOReturnStillOpen)! 63 | /// RLD failure 64 | static let rldError = KernelError(rawValue: kIOReturnRLDError)! 65 | /// DMA failure 66 | static let dmaError = KernelError(rawValue: kIOReturnDMAError)! 67 | /// Device busy 68 | static let busy = KernelError(rawValue: kIOReturnBusy)! 69 | /// I/O timeout 70 | static let timeout = KernelError(rawValue: kIOReturnTimeout)! 71 | /// Device offline 72 | static let offline = KernelError(rawValue: kIOReturnOffline)! 73 | /// Not ready 74 | static let notReady = KernelError(rawValue: kIOReturnNotReady)! 75 | /// Device not attached 76 | static let notAttached = KernelError(rawValue: kIOReturnNotAttached)! 77 | /// No DMA channels left 78 | static let noChannels = KernelError(rawValue: kIOReturnNoChannels)! 79 | /// No space for data 80 | static let noSpace = KernelError(rawValue: kIOReturnNoSpace)! 81 | /// Port already exists 82 | static let portExists = KernelError(rawValue: kIOReturnPortExists)! 83 | /// Can't wire down physical memory 84 | static let cannotWire = KernelError(rawValue: kIOReturnCannotWire)! 85 | /// No interrupt attached 86 | static let noInterrupt = KernelError(rawValue: kIOReturnNoInterrupt)! 87 | /// No DMA frames enqueued 88 | static let noFrames = KernelError(rawValue: kIOReturnNoFrames)! 89 | /// Oversized msg received on interrupt port 90 | static let messageTooLarge = KernelError(rawValue: kIOReturnMessageTooLarge)! 91 | /// Not permitted 92 | static let notPermitted = KernelError(rawValue: kIOReturnNotPermitted)! 93 | /// No power to device 94 | static let noPower = KernelError(rawValue: kIOReturnNoPower)! 95 | /// Media not present 96 | static let noMedia = KernelError(rawValue: kIOReturnNoMedia)! 97 | /// media not formatted 98 | static let unformattedMedia = KernelError(rawValue: kIOReturnUnformattedMedia)! 99 | /// No such mode 100 | static let unsupportedMode = KernelError(rawValue: kIOReturnUnsupportedMode)! 101 | /// Data underrun 102 | static let underrun = KernelError(rawValue: kIOReturnUnderrun)! 103 | /// Data overrun 104 | static let overrun = KernelError(rawValue: kIOReturnOverrun)! 105 | /// The device is not working properly 106 | static let deviceError = KernelError(rawValue: kIOReturnDeviceError)! 107 | /// A completion routine is required 108 | static let noCompletion = KernelError(rawValue: kIOReturnNoCompletion)! 109 | /// Operation aborted 110 | static let aborted = KernelError(rawValue: kIOReturnAborted)! 111 | /// Bus bandwidth would be exceeded 112 | static let noBandwidth = KernelError(rawValue: kIOReturnNoBandwidth)! 113 | /// Device not responding 114 | static let notResponding = KernelError(rawValue: kIOReturnNotResponding)! 115 | /// Isochronous I/O request for distant past 116 | static let isoTooOld = KernelError(rawValue: kIOReturnIsoTooOld)! 117 | /// Isochronous I/O request for distant future 118 | static let isoTooNew = KernelError(rawValue: kIOReturnIsoTooNew)! 119 | /// Data was not found 120 | static let notFound = KernelError(rawValue: kIOReturnNotFound)! 121 | /// Should never be seen 122 | static let invalid = KernelError(rawValue: kIOReturnInvalid)! 123 | } 124 | 125 | public extension KernelError { 126 | /// A namespace for IOReturn errors. 127 | static let io = IOError.self 128 | } 129 | 130 | #endif // canImport(IOKit) 131 | -------------------------------------------------------------------------------- /Sources/ImageIOExt/ImageIOExt/CGImageAuxiliaryData.swift: -------------------------------------------------------------------------------- 1 | #if canImport(ImageIO) 2 | 3 | import SwiftCF 4 | import ImageIO 5 | 6 | extension CGImage { 7 | 8 | public struct AuxiliaryDataType: CFStringKey { 9 | 10 | public let rawValue: CFString 11 | 12 | public init(_ key: CFString) { 13 | rawValue = key 14 | } 15 | } 16 | } 17 | 18 | public extension CGImage.AuxiliaryDataType { 19 | @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) 20 | static let depth = kCGImageAuxiliaryDataTypeDepth as CGImage.AuxiliaryDataType 21 | @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) 22 | static let disparity = kCGImageAuxiliaryDataTypeDisparity as CGImage.AuxiliaryDataType 23 | @available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 5.0, *) 24 | static let portraitEffectsMatte = kCGImageAuxiliaryDataTypePortraitEffectsMatte as CGImage.AuxiliaryDataType 25 | @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) 26 | static let semanticSegmentationSkinMatte = kCGImageAuxiliaryDataTypeSemanticSegmentationSkinMatte as CGImage.AuxiliaryDataType 27 | @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) 28 | static let semanticSegmentationHairMatte = kCGImageAuxiliaryDataTypeSemanticSegmentationHairMatte as CGImage.AuxiliaryDataType 29 | @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) 30 | static let semanticSegmentationTeethMatte = kCGImageAuxiliaryDataTypeSemanticSegmentationTeethMatte as CGImage.AuxiliaryDataType 31 | } 32 | 33 | extension CGImage { 34 | 35 | public struct AuxiliaryDataInfoKey: CFStringKey { 36 | 37 | public let rawValue: CFString 38 | 39 | public init(_ key: CFString) { 40 | rawValue = key 41 | } 42 | } 43 | } 44 | 45 | @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) 46 | public extension CGImage.AuxiliaryDataInfoKey { 47 | static let data = kCGImageAuxiliaryDataInfoData as CGImage.AuxiliaryDataInfoKey 48 | static let dataDescription = kCGImageAuxiliaryDataInfoDataDescription as CGImage.AuxiliaryDataInfoKey 49 | static let metadata = kCGImageAuxiliaryDataInfoMetadata as CGImage.AuxiliaryDataInfoKey 50 | } 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /Sources/ImageIOExt/ImageIOExt/CGImageDestination.swift: -------------------------------------------------------------------------------- 1 | #if canImport(ImageIO) 2 | 3 | import SwiftCF 4 | import ImageIO 5 | 6 | public extension CGImageDestination { 7 | 8 | /// Creates an image destination that writes to the specified data consumer. 9 | /// 10 | /// - Parameters: 11 | /// - consumer: The data consumer to write to. For information on data 12 | /// consumers see CGDataConsumer and Quartz 2D Programming Guide. 13 | /// - type: The uniform type identifier (UTI) of the resulting image file. 14 | /// See Uniform Type Identifiers Overview for a list of system-declared 15 | /// and third-party UTIs. 16 | /// - count: The number of images (not including thumbnail images) that 17 | /// the image file will contain. 18 | /// - Returns: An image destination. You are responsible for releasing this 19 | /// object using CFRelease. 20 | @inlinable static func create(consumer: CGDataConsumer, type: CFString, count: Int) -> CGImageDestination? { 21 | return CGImageDestinationCreateWithDataConsumer(consumer, type, count, nil) 22 | } 23 | 24 | /// Creates an image destination that writes to a Core Foundation mutable 25 | /// data object. 26 | /// 27 | /// - Parameters: 28 | /// - data: The data object to write to. For more information on data 29 | /// objects, see CFData and Data Objects. 30 | /// - type: The uniform type identifier (UTI) of the resulting image file. 31 | /// See Uniform Type Identifiers Overview for a list of system-declared 32 | /// and third-party UTIs. 33 | /// - count: The number of images (not including thumbnail images) that 34 | /// the image file will contain. 35 | /// - Returns: An image destination. You are responsible for releasing this 36 | /// object using CFRelease. 37 | @inlinable static func create(data: CFMutableData, type: CFString, count: Int) -> CGImageDestination? { 38 | return CGImageDestinationCreateWithData(data, type, count, nil) 39 | } 40 | 41 | /// Creates an image destination that writes to a location specified by a 42 | /// URL. 43 | /// 44 | /// - Parameters: 45 | /// - url: The URL to write to. If the URL already exists, the data at 46 | /// this location is overwritten. 47 | /// - type: The UTI (uniform type identifier) of the resulting image file. 48 | /// See Uniform Type Identifiers Overview for a list of system-declared 49 | /// and third-party UTIs. 50 | /// - count: The number of images (not including thumbnail images) that 51 | /// the image file will contain. 52 | /// - Returns: An image destination. You are responsible for releasing this 53 | /// object using CFRelease. 54 | @inlinable static func create(url: CFURL, type: CFString, count: Int) -> CGImageDestination? { 55 | return CGImageDestinationCreateWithURL(url, type, count, nil) 56 | } 57 | 58 | /// Applies one or more properties to all images in an image destination. 59 | /// 60 | /// - Parameter properties: A dictionary that contains the properties to 61 | /// apply. You can set any of the properties described in Destination 62 | /// Properties or the image properties described in CGImageProperties. 63 | @inlinable func setProperties(_ properties: [CGImage.PropertyName: Any]) { 64 | CGImageDestinationSetProperties(self, .from(properties)) 65 | } 66 | 67 | /// Adds an image to an image destination. 68 | /// 69 | /// The function logs an error if you add more images than what you 70 | /// specified when you created the image destination. 71 | /// 72 | /// - Parameters: 73 | /// - image: The image to add. 74 | /// - properties: An optional dictionary that specifies the properties of 75 | /// the added image. The dictionary can contain any of the properties 76 | /// described in Destination Properties or the image properties described 77 | /// in CGImageProperties. 78 | @inlinable func addImage(_ image: CGImage, properties: [CGImage.PropertyName: Any] = [:]) { 79 | CGImageDestinationAddImage(self, image, .from(properties)) 80 | } 81 | 82 | /// Adds an image from an image source to an image destination. 83 | /// 84 | /// - Parameters: 85 | /// - source: An image source. 86 | /// - index: An index that specifies the location of the image in the 87 | /// image source. The index is zero-based. 88 | /// - properties: A dictionary that specifies properties to overwrite or 89 | /// add to the source image properties. If a key in properties has the 90 | /// value kCFNull, the corresponding property in the image destination is 91 | /// removed. The dictionary can contain any of the properties described in 92 | /// Destination Properties or the image properties described in 93 | /// CGImageProperties. 94 | @inlinable func addImage(from source: CGImageSource, at index: Int, properties: [CGImage.PropertyName: Any] = [:]) { 95 | CGImageDestinationAddImageFromSource(self, source, index, .from(properties)) 96 | } 97 | 98 | /// Set the next image in the image destination `idst' to be `image' with 99 | /// metadata properties specified in `metadata'. An error is logged if more 100 | /// images are added than specified in the original count of the image 101 | /// destination. 102 | @inlinable func addImage(_ image: CGImage, metadata: CGImageMetadata) { 103 | CGImageDestinationAddImageAndMetadata(self, image, metadata, nil) 104 | } 105 | 106 | /// Writes image data and properties to the data, URL, or data consumer 107 | /// associated with the image destination. 108 | /// 109 | /// You must call this function or the output of the image destination will 110 | /// not be valid. After calling this function, no additional data can be 111 | /// added to the image destination. 112 | /// 113 | /// - Returns: Returns true if the image is successfully written; false 114 | /// otherwise. 115 | @inlinable func finalize() -> Bool { 116 | return CGImageDestinationFinalize(self) 117 | } 118 | 119 | /// Losslessly copies the contents of the image source, 'isrc', to the 120 | /// destination, 'idst'. The image data will not be modified. The image's 121 | /// metadata can be modified by adding the keys and values defined above to 122 | /// 'options'. No other images should be added to the image destination. 123 | /// CGImageDestinationFinalize() should not be called afterward - the result 124 | /// is saved to the destination when this function returns. 125 | /// 126 | /// The image type of the destination must match the image source. Returns 127 | /// true if the operation was successful. If an error occurs, false will be 128 | /// returned and 'err' will be set to a CFErrorRef. Not all image formats 129 | /// are supported for this operation. 130 | @inlinable func copy(from source: CGImageSource, options: [CopyOptions: Any]) throws { 131 | var error: Unmanaged? 132 | let success = withUnsafeMutablePointer(to: &error) { ptr in 133 | CGImageDestinationCopyImageSource(self, source, .from(options), ptr) 134 | } 135 | if !success { 136 | throw error!.takeRetainedValue() 137 | } 138 | } 139 | 140 | /// Depth data support for JPEG, HEIF, and DNG images. 141 | /// 142 | /// The auxiliaryDataInfoDictionary should contain: 143 | /// - the depth data (CFDataRef) - (kCGImageAuxiliaryDataInfoData), 144 | /// - the depth data description (CFDictionary) - (kCGImageAuxiliaryDataInfoDataDescription) 145 | /// - metadata (CGImageMetadataRef) - (kCGImageAuxiliaryDataInfoMetadata) 146 | /// To add depth data to an image, call 147 | /// CGImageDestinationAddAuxiliaryDataInfo() after adding the CGImage to the 148 | /// CGImageDestinationRef. 149 | @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) 150 | @inlinable func addAuxiliaryDataInfo(_ auxiliaryDataInfo: [CGImage.AuxiliaryDataInfoKey: Any], type: CGImage.AuxiliaryDataType) { 151 | CGImageDestinationAddAuxiliaryDataInfo(self, type.rawValue, .from(auxiliaryDataInfo)) 152 | } 153 | } 154 | 155 | extension CGImageDestination { 156 | 157 | public struct CopyOptions: CFStringKey { 158 | 159 | public let rawValue: CFString 160 | 161 | public init(_ key: CFString) { 162 | rawValue = key 163 | } 164 | } 165 | } 166 | 167 | public extension CGImageDestination.CopyOptions { 168 | static let metadata = kCGImageDestinationMetadata as CGImageDestination.CopyOptions 169 | static let mergeMetadata = kCGImageDestinationMergeMetadata as CGImageDestination.CopyOptions 170 | static let shouldExcludeXMP = kCGImageMetadataShouldExcludeXMP as CGImageDestination.CopyOptions 171 | static let shouldExcludeGPS = kCGImageMetadataShouldExcludeGPS as CGImageDestination.CopyOptions 172 | static let dateTime = kCGImageDestinationDateTime as CGImageDestination.CopyOptions 173 | static let orientation = kCGImageDestinationOrientation as CGImageDestination.CopyOptions 174 | } 175 | 176 | /// Properties for a single image in an image destination. 177 | public extension CGImage.PropertyName { 178 | static let destinationLossyCompressionQuality = kCGImageDestinationLossyCompressionQuality as CGImage.PropertyName 179 | static let destinationBackgroundColor = kCGImageDestinationBackgroundColor as CGImage.PropertyName 180 | } 181 | 182 | #endif 183 | -------------------------------------------------------------------------------- /Sources/ImageIOExt/ImageIOExt/CGImageMetadataFormatSpecificDictionary.swift: -------------------------------------------------------------------------------- 1 | #if canImport(ImageIO) 2 | 3 | import SwiftCF 4 | import ImageIO 5 | 6 | extension CGImageMetadata { 7 | 8 | public struct FormatSpecificDictionary: CFStringKey { 9 | 10 | public let rawValue: CFString 11 | 12 | public init(_ key: CFString) { 13 | rawValue = key 14 | } 15 | } 16 | } 17 | 18 | public extension CGImageMetadata.FormatSpecificDictionary { 19 | static let tiff = kCGImagePropertyTIFFDictionary as CGImageMetadata.FormatSpecificDictionary 20 | static let gif = kCGImagePropertyGIFDictionary as CGImageMetadata.FormatSpecificDictionary 21 | static let jfif = kCGImagePropertyJFIFDictionary as CGImageMetadata.FormatSpecificDictionary 22 | @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) 23 | static let heics = kCGImagePropertyHEICSDictionary as CGImageMetadata.FormatSpecificDictionary 24 | static let exif = kCGImagePropertyExifDictionary as CGImageMetadata.FormatSpecificDictionary 25 | static let png = kCGImagePropertyPNGDictionary as CGImageMetadata.FormatSpecificDictionary 26 | static let iptc = kCGImagePropertyIPTCDictionary as CGImageMetadata.FormatSpecificDictionary 27 | static let gps = kCGImagePropertyGPSDictionary as CGImageMetadata.FormatSpecificDictionary 28 | static let raw = kCGImagePropertyRawDictionary as CGImageMetadata.FormatSpecificDictionary 29 | static let ciff = kCGImagePropertyCIFFDictionary as CGImageMetadata.FormatSpecificDictionary 30 | static let makerCanon = kCGImagePropertyMakerCanonDictionary as CGImageMetadata.FormatSpecificDictionary 31 | static let makerNikon = kCGImagePropertyMakerNikonDictionary as CGImageMetadata.FormatSpecificDictionary 32 | static let makerMinolta = kCGImagePropertyMakerMinoltaDictionary as CGImageMetadata.FormatSpecificDictionary 33 | static let makerFuji = kCGImagePropertyMakerFujiDictionary as CGImageMetadata.FormatSpecificDictionary 34 | static let makerOlympus = kCGImagePropertyMakerOlympusDictionary as CGImageMetadata.FormatSpecificDictionary 35 | static let makerPentax = kCGImagePropertyMakerPentaxDictionary as CGImageMetadata.FormatSpecificDictionary 36 | static let _8BIM = kCGImageProperty8BIMDictionary as CGImageMetadata.FormatSpecificDictionary 37 | static let dng = kCGImagePropertyDNGDictionary as CGImageMetadata.FormatSpecificDictionary 38 | static let exifAux = kCGImagePropertyExifAuxDictionary as CGImageMetadata.FormatSpecificDictionary 39 | @available(iOS 11.3, tvOS 11.3, watchOS 4.3, *) 40 | static let openEXR = kCGImagePropertyOpenEXRDictionary as CGImageMetadata.FormatSpecificDictionary 41 | static let makerApple = kCGImagePropertyMakerAppleDictionary as CGImageMetadata.FormatSpecificDictionary 42 | @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) 43 | static let fileContents = kCGImagePropertyFileContentsDictionary as CGImageMetadata.FormatSpecificDictionary 44 | } 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /Sources/ImageIOExt/ImageIOExt/CGImageMetadataTag.swift: -------------------------------------------------------------------------------- 1 | #if canImport(ImageIO) 2 | 3 | import SwiftCF 4 | import ImageIO 5 | 6 | public extension CGImageMetadataTag { 7 | 8 | @inlinable static func create(namespace: Namespace, _ prefix: Prefix? = nil, name: CFString, type: CGImageMetadataType, value: CFTypeRef) -> CGImageMetadataTag? { 9 | return CGImageMetadataTagCreate(namespace.rawValue, prefix?.rawValue, name, type, value) 10 | } 11 | 12 | @inlinable func namespace() -> Namespace? { 13 | return CGImageMetadataTagCopyNamespace(self).map(Namespace.init) 14 | } 15 | 16 | @inlinable func prefix() -> Prefix? { 17 | return CGImageMetadataTagCopyPrefix(self).map(Prefix.init) 18 | } 19 | 20 | @inlinable func name() -> CFString? { 21 | return CGImageMetadataTagCopyName(self) 22 | } 23 | 24 | @inlinable func value() -> CFTypeRef? { 25 | return CGImageMetadataTagCopyValue(self) 26 | } 27 | 28 | @inlinable var type: CGImageMetadataType { 29 | return CGImageMetadataTagGetType(self) 30 | } 31 | 32 | @inlinable func qualifiers() -> [CGImageMetadataTag] { 33 | return CGImageMetadataTagCopyQualifiers(self)?.asSwift() ?? [] 34 | } 35 | } 36 | 37 | extension CGImageMetadataTag { 38 | 39 | public struct Namespace: CFStringKey { 40 | 41 | public let rawValue: CFString 42 | 43 | public init(_ key: CFString) { 44 | rawValue = key 45 | } 46 | } 47 | } 48 | 49 | public extension CGImageMetadataTag.Namespace { 50 | static let exif = kCGImageMetadataNamespaceExif as CGImageMetadataTag.Namespace 51 | static let exifAux = kCGImageMetadataNamespaceExifAux as CGImageMetadataTag.Namespace 52 | static let exifEX = kCGImageMetadataNamespaceExifEX as CGImageMetadataTag.Namespace 53 | static let dublinCore = kCGImageMetadataNamespaceDublinCore as CGImageMetadataTag.Namespace 54 | static let iptcCore = kCGImageMetadataNamespaceIPTCCore as CGImageMetadataTag.Namespace 55 | @available(macOS 10.13.4, iOS 11.3, tvOS 11.3, watchOS 4.3, *) 56 | static let iptcExtension = kCGImageMetadataNamespaceIPTCExtension as CGImageMetadataTag.Namespace 57 | static let photoshop = kCGImageMetadataNamespacePhotoshop as CGImageMetadataTag.Namespace 58 | static let tiff = kCGImageMetadataNamespaceTIFF as CGImageMetadataTag.Namespace 59 | static let xmpBasic = kCGImageMetadataNamespaceXMPBasic as CGImageMetadataTag.Namespace 60 | static let xmpRights = kCGImageMetadataNamespaceXMPRights as CGImageMetadataTag.Namespace 61 | } 62 | 63 | extension CGImageMetadataTag { 64 | 65 | public struct Prefix: CFStringKey { 66 | 67 | public let rawValue: CFString 68 | 69 | public init(_ key: CFString) { 70 | rawValue = key 71 | } 72 | } 73 | } 74 | 75 | public extension CGImageMetadataTag.Prefix { 76 | static let exif = kCGImageMetadataPrefixExif as CGImageMetadataTag.Prefix 77 | static let exifAux = kCGImageMetadataPrefixExifAux as CGImageMetadataTag.Prefix 78 | static let exifEX = kCGImageMetadataPrefixExifEX as CGImageMetadataTag.Prefix 79 | static let dublinCore = kCGImageMetadataPrefixDublinCore as CGImageMetadataTag.Prefix 80 | static let iptcCore = kCGImageMetadataPrefixIPTCCore as CGImageMetadataTag.Prefix 81 | @available(macOS 10.13.4, iOS 11.3, tvOS 11.3, watchOS 4.3, *) 82 | static let iptcExtension = kCGImageMetadataPrefixIPTCExtension as CGImageMetadataTag.Prefix 83 | static let photoshop = kCGImageMetadataPrefixPhotoshop as CGImageMetadataTag.Prefix 84 | static let tiff = kCGImageMetadataPrefixTIFF as CGImageMetadataTag.Prefix 85 | static let xmpBasic = kCGImageMetadataPrefixXMPBasic as CGImageMetadataTag.Prefix 86 | static let xmpRights = kCGImageMetadataPrefixXMPRights as CGImageMetadataTag.Prefix 87 | } 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /Sources/ImageIOExt/ImageIOExt/CGImageSourceOption.swift: -------------------------------------------------------------------------------- 1 | #if canImport(ImageIO) 2 | 3 | import SwiftCF 4 | import ImageIO 5 | 6 | extension CGImageSource { 7 | 8 | public struct Option: CFStringKey { 9 | 10 | public let rawValue: CFString 11 | 12 | public init(_ key: CFString) { 13 | rawValue = key 14 | } 15 | } 16 | } 17 | 18 | public extension CGImageSource.Option { 19 | static let typeIdentifierHint = kCGImageSourceTypeIdentifierHint as CGImageSource.Option 20 | static let shouldCache = kCGImageSourceShouldCache as CGImageSource.Option 21 | static let shouldCacheImmediately = kCGImageSourceShouldCacheImmediately as CGImageSource.Option 22 | static let shouldAllowFloat = kCGImageSourceShouldAllowFloat as CGImageSource.Option 23 | static let createThumbnailFromImageIfAbsent = kCGImageSourceCreateThumbnailFromImageIfAbsent as CGImageSource.Option 24 | static let createThumbnailFromImageAlways = kCGImageSourceCreateThumbnailFromImageAlways as CGImageSource.Option 25 | static let thumbnailMaxPixelSize = kCGImageSourceThumbnailMaxPixelSize as CGImageSource.Option 26 | static let createThumbnailWithTransform = kCGImageSourceCreateThumbnailWithTransform as CGImageSource.Option 27 | @available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *) 28 | static let subsampleFactor = kCGImageSourceSubsampleFactor as CGImageSource.Option 29 | } 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /Sources/ImageIOExt/ImageIOType.swift: -------------------------------------------------------------------------------- 1 | #if canImport(ImageIO) 2 | 3 | import SwiftCF 4 | import ImageIO 5 | 6 | extension CGImageDestination: CFType { 7 | public static let typeID = CGImageDestinationGetTypeID() 8 | } 9 | 10 | extension CGImageSource: CFType { 11 | public static let typeID = CGImageSourceGetTypeID() 12 | } 13 | 14 | extension CGImageMetadata: CFType { 15 | public static let typeID = CGImageMetadataGetTypeID() 16 | } 17 | 18 | extension CGImageMetadataTag: CFType { 19 | public static let typeID = CGImageMetadataTagGetTypeID() 20 | } 21 | 22 | #endif // canImport(ImageIO) 23 | -------------------------------------------------------------------------------- /Sources/SwiftCF/CFObject.swift: -------------------------------------------------------------------------------- 1 | import CoreFoundation 2 | 3 | public extension CFType /* _CFObject */ { 4 | 5 | @inlinable static var cfDescription: CFString { 6 | return CFCopyTypeIDDescription(typeID) 7 | } 8 | 9 | @inlinable var cfDescription: CFString { 10 | return CFCopyDescription(self) 11 | } 12 | 13 | @inlinable func cfEqual(to v: CFTypeRef) -> Bool { 14 | return CFEqual(self, v) 15 | } 16 | 17 | @inlinable var cfHash: CFHashCode { 18 | return CFHash(self) 19 | } 20 | 21 | #if canImport(Darwin) 22 | @inlinable var cfRetainCount: CFIndex { 23 | return CFGetRetainCount(self) 24 | } 25 | #endif 26 | 27 | @inlinable var cfAllocator: CFAllocator { 28 | return CFGetAllocator(self) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/SwiftCF/CFStringKey.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import CoreFoundation 3 | 4 | public protocol CFStringKey: RawRepresentable, ReferenceConvertible, ExpressibleByStringLiteral, _CFConvertible where RawValue == CFString, ReferenceType == NSString, _CFType == CFString { 5 | init(_ key: CFString) 6 | } 7 | 8 | public extension CFStringKey { 9 | 10 | init(rawValue: CFString) { 11 | self.init(rawValue) 12 | } 13 | 14 | init(stringLiteral value: String) { 15 | self.init(.from(value)) 16 | } 17 | 18 | var description: String { 19 | return String._bridgeFromCF(rawValue) 20 | } 21 | 22 | var debugDescription: String { 23 | return description 24 | } 25 | 26 | static func == (lhs: Self, rhs: Self) -> Bool { 27 | return lhs.rawValue.cfEqual(to: rhs.rawValue) 28 | } 29 | 30 | func hash(into hasher: inout Hasher) { 31 | hasher.combine(rawValue.cfHash) 32 | } 33 | 34 | func _bridgeToObjectiveC() -> NSString { 35 | return rawValue._bridgeToNS() 36 | } 37 | 38 | static func _forceBridgeFromObjectiveC(_ source: NSString, result: inout Self?) { 39 | result = Self(CFString._bridgeFromNS(source)) 40 | } 41 | 42 | static func _conditionallyBridgeFromObjectiveC(_ source: NSString, result: inout Self?) -> Bool { 43 | _forceBridgeFromObjectiveC(source, result: &result) 44 | return true 45 | } 46 | 47 | static func _unconditionallyBridgeFromObjectiveC(_ source: NSString?) -> Self { 48 | var result: Self? 49 | _forceBridgeFromObjectiveC(source!, result: &result) 50 | return result! 51 | } 52 | 53 | func _bridgeToCF() -> CFString { 54 | return rawValue 55 | } 56 | 57 | static func _bridgeFromCF(_ source: CFString) -> Self { 58 | return self.init(source) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Sources/SwiftCF/CFType.swift: -------------------------------------------------------------------------------- 1 | import CoreFoundation 2 | 3 | public protocol CFType: AnyObject { 4 | static var typeID: CFTypeID { get } 5 | } 6 | 7 | // MARK: - Conformance 8 | 9 | extension CFAllocator: CFType { 10 | public static let typeID = CFAllocatorGetTypeID() 11 | } 12 | 13 | extension CFArray: CFType { 14 | public static let typeID = CFArrayGetTypeID() 15 | } 16 | 17 | extension CFAttributedString: CFType { 18 | public static let typeID = CFAttributedStringGetTypeID() 19 | } 20 | 21 | extension CFBag: CFType { 22 | public static let typeID = CFBagGetTypeID() 23 | } 24 | 25 | extension CFBinaryHeap: CFType { 26 | public static let typeID = CFBinaryHeapGetTypeID() 27 | } 28 | 29 | extension CFBitVector: CFType { 30 | public static let typeID = CFBitVectorGetTypeID() 31 | } 32 | 33 | extension CFBoolean: CFType { 34 | public static let typeID = CFBooleanGetTypeID() 35 | } 36 | 37 | extension CFBundle: CFType { 38 | public static let typeID = CFBundleGetTypeID() 39 | } 40 | 41 | extension CFCalendar: CFType { 42 | public static let typeID = CFCalendarGetTypeID() 43 | } 44 | 45 | extension CFCharacterSet: CFType { 46 | public static let typeID = CFCharacterSetGetTypeID() 47 | } 48 | 49 | extension CFData: CFType { 50 | public static let typeID = CFDataGetTypeID() 51 | } 52 | 53 | extension CFDate: CFType { 54 | public static let typeID = CFDateGetTypeID() 55 | } 56 | 57 | extension CFDateFormatter: CFType { 58 | public static let typeID = CFDateFormatterGetTypeID() 59 | } 60 | 61 | extension CFDictionary: CFType { 62 | public static let typeID = CFDictionaryGetTypeID() 63 | } 64 | 65 | extension CFError: CFType { 66 | public static let typeID = CFErrorGetTypeID() 67 | } 68 | 69 | extension CFLocale: CFType { 70 | public static let typeID = CFLocaleGetTypeID() 71 | } 72 | 73 | // FIXME: undefined reference error on Linux 74 | #if canImport(Darwin) 75 | extension CFMessagePort: CFType { 76 | public static let typeID = CFMessagePortGetTypeID() 77 | } 78 | 79 | extension CFNotificationCenter: CFType { 80 | public static let typeID = CFNotificationCenterGetTypeID() 81 | } 82 | #endif 83 | 84 | extension CFNull: CFType { 85 | public static let typeID = CFNullGetTypeID() 86 | } 87 | 88 | extension CFNumber: CFType { 89 | public static let typeID = CFNumberGetTypeID() 90 | } 91 | 92 | extension CFNumberFormatter: CFType { 93 | public static let typeID = CFNumberFormatterGetTypeID() 94 | } 95 | 96 | extension CFPlugIn: CFType { 97 | public static let typeID = CFPlugInGetTypeID() 98 | } 99 | 100 | extension CFPlugInInstance: CFType { 101 | public static let typeID = CFPlugInInstanceGetTypeID() 102 | } 103 | 104 | extension CFReadStream: CFType { 105 | public static let typeID = CFReadStreamGetTypeID() 106 | } 107 | 108 | extension CFRunLoop: CFType { 109 | public static let typeID = CFRunLoopGetTypeID() 110 | } 111 | 112 | extension CFRunLoopObserver: CFType { 113 | public static let typeID = CFRunLoopObserverGetTypeID() 114 | } 115 | 116 | extension CFRunLoopSource: CFType { 117 | public static let typeID = CFRunLoopSourceGetTypeID() 118 | } 119 | 120 | extension CFRunLoopTimer: CFType { 121 | public static let typeID = CFRunLoopTimerGetTypeID() 122 | } 123 | 124 | extension CFSet: CFType { 125 | public static let typeID = CFSetGetTypeID() 126 | } 127 | 128 | extension CFSocket: CFType { 129 | public static let typeID = CFSocketGetTypeID() 130 | } 131 | 132 | extension CFString: CFType { 133 | public static let typeID = CFStringGetTypeID() 134 | } 135 | 136 | extension CFTimeZone: CFType { 137 | public static let typeID = CFTimeZoneGetTypeID() 138 | } 139 | 140 | extension CFTree: CFType { 141 | public static let typeID = CFTreeGetTypeID() 142 | } 143 | 144 | extension CFURL: CFType { 145 | public static let typeID = CFURLGetTypeID() 146 | } 147 | 148 | extension CFUUID: CFType { 149 | public static let typeID = CFUUIDGetTypeID() 150 | } 151 | 152 | extension CFWriteStream: CFType { 153 | public static let typeID = CFWriteStreamGetTypeID() 154 | } 155 | 156 | #if os(macOS) 157 | 158 | extension CFUserNotification: CFType { 159 | public static let typeID = CFUserNotificationGetTypeID() 160 | } 161 | 162 | #endif 163 | 164 | #if canImport(Darwin) 165 | 166 | extension CFFileDescriptor: CFType { 167 | public static let typeID = CFFileDescriptorGetTypeID() 168 | } 169 | 170 | extension CFMachPort: CFType { 171 | public static let typeID = CFMachPortGetTypeID() 172 | } 173 | 174 | extension CFStringTokenizer: CFType { 175 | public static let typeID = CFStringTokenizerGetTypeID() 176 | } 177 | 178 | #endif 179 | 180 | // MARK: - Mutable Type 181 | 182 | //extension CFMutableArray: CFMutableType {} 183 | //extension CFMutableAttributedString: CFMutableType {} 184 | //extension CFMutableBag: CFMutableType {} 185 | //extension CFMutableBitVector: CFMutableType {} 186 | //extension CFMutableCharacterSet: CFMutableType {} 187 | //extension CFMutableData: CFMutableType {} 188 | //extension CFMutableDictionary: CFMutableType {} 189 | //extension CFMutableSet: CFMutableType {} 190 | //extension CFMutableString: CFMutableType {} 191 | 192 | // MARK: Deprecated TypeID 193 | 194 | //extension CFPropertyList: CFType { 195 | // public static let typeID = CFPropertyListGetTypeID() 196 | //} 197 | 198 | //extension CFXMLNode: CFType { 199 | // public static let typeID = CFXMLNodeGetTypeID() 200 | //} 201 | 202 | //extension CFXMLParser: CFType { 203 | // public static let typeID = CFXMLParserGetTypeID() 204 | //} 205 | 206 | //extension CFXMLTree: CFType { 207 | // public static let typeID = CFXMLTreeGetTypeID() 208 | //} 209 | -------------------------------------------------------------------------------- /Sources/SwiftCF/CFTypeExt/CFAllocator.swift: -------------------------------------------------------------------------------- 1 | import CoreFoundation 2 | 3 | public extension CFAllocator { 4 | 5 | static let systemDefault = kCFAllocatorSystemDefault! 6 | static let malloc = kCFAllocatorMalloc! 7 | static let mallocZone = kCFAllocatorMallocZone! 8 | static let null = kCFAllocatorNull! 9 | static let useContext = kCFAllocatorUseContext! 10 | 11 | @inlinable static var `default`: CFAllocator { 12 | get { 13 | return CFAllocatorGetDefault().takeUnretainedValue() 14 | } 15 | set { 16 | CFAllocatorSetDefault(newValue) 17 | } 18 | } 19 | 20 | /// Set an allocator as the default in a nested fashion. 21 | @inlinable func withDefaultAllocator(do body: () throws -> Void) rethrows { 22 | let previous = CFAllocator.default 23 | CFAllocator.default = self 24 | defer { 25 | CFAllocator.default = previous 26 | } 27 | try body() 28 | } 29 | 30 | @inlinable var context: CFAllocatorContext { 31 | var ctx = CFAllocatorContext() 32 | CFAllocatorGetContext(self, &ctx) 33 | return ctx 34 | } 35 | 36 | @inlinable func allocate(size: CFIndex) -> UnsafeMutableRawPointer { 37 | return CFAllocatorAllocate(self, size, 0) 38 | } 39 | 40 | @inlinable func reallocate(_ ptr: UnsafeMutableRawPointer, newSize: CFIndex) -> UnsafeMutableRawPointer { 41 | return CFAllocatorReallocate(self, ptr, newSize, 0) 42 | } 43 | 44 | @inlinable func deallocate(_ ptr: UnsafeMutableRawPointer) { 45 | CFAllocatorDeallocate(self, ptr) 46 | } 47 | 48 | @inlinable func preferredSize(for size: CFIndex) -> CFIndex { 49 | return CFAllocatorGetPreferredSizeForSize(self, size, 0) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/SwiftCF/CFTypeExt/CFArray.swift: -------------------------------------------------------------------------------- 1 | import CoreFoundation 2 | 3 | public extension CFArray { 4 | 5 | static let empty: CFArray = CFArray.create(values: nil, count: 0) 6 | 7 | @inlinable static func create(allocator: CFAllocator = .default, values: UnsafeMutablePointer?, count: CFIndex, pCallBacks: UnsafePointer? = pCFTypeArrayCallBacks) -> CFArray { 8 | return CFArrayCreate(allocator, values, count, pCallBacks) 9 | } 10 | 11 | @inlinable func copy(allocator: CFAllocator = .default) -> CFArray { 12 | return CFArrayCreateCopy(allocator, self) 13 | } 14 | 15 | @inlinable func mutableCopy(allocator: CFAllocator = .default, capacity: CFIndex = 0) -> CFMutableArray { 16 | return CFArrayCreateMutableCopy(allocator, capacity, self) 17 | } 18 | 19 | @inlinable var count: CFIndex { 20 | return CFArrayGetCount(self) 21 | } 22 | 23 | @inlinable var fullRange: CFRange { 24 | return CFRange(location: 0, length: count) 25 | } 26 | 27 | @inlinable func count(of value: CFTypeRef) -> CFIndex { 28 | return count(of: value, in: fullRange) 29 | } 30 | 31 | @inlinable func count(of value: CFTypeRef, in range: CFRange) -> CFIndex { 32 | return CFArrayGetCountOfValue(self, range, .fromCF(value)) 33 | } 34 | 35 | @inlinable func contains(_ value: CFTypeRef, in range: CFRange? = nil) -> Bool { 36 | return CFArrayContainsValue(self, range ?? fullRange, .fromCF(value)) 37 | } 38 | 39 | @inlinable func value(at index: CFIndex) -> CFTypeRef { 40 | return rawValue(at: index).asCF() 41 | } 42 | 43 | @inlinable func rawValue(at index: CFIndex) -> UnsafeRawPointer { 44 | return CFArrayGetValueAtIndex(self, index)! 45 | } 46 | 47 | @inlinable func values(in range: CFRange) -> [CFTypeRef] { 48 | guard range.length > 0 else { 49 | return [] 50 | } 51 | return Array(unsafeUninitializedCapacity: range.length) { p, count in 52 | CFArrayGetValues(self, range, UnsafeMutablePointer(OpaquePointer(p.baseAddress))) 53 | count = range.length 54 | } 55 | } 56 | } 57 | 58 | extension CFArray: RandomAccessCollection { 59 | 60 | @inlinable public var startIndex: Int { 61 | return 0 62 | } 63 | 64 | @inlinable public var endIndex: Int { 65 | return count 66 | } 67 | 68 | @inlinable public subscript(position: Int) -> CFTypeRef { 69 | return value(at: position) 70 | } 71 | } 72 | 73 | // MARK: - CFMutableArray 74 | 75 | public extension CFMutableArray { 76 | 77 | @inlinable static func create(allocator: CFAllocator = .default, capacity: CFIndex = 0, pCallBacks: UnsafePointer? = pCFTypeArrayCallBacks) -> CFMutableArray { 78 | return CFArrayCreateMutable(allocator, capacity, pCallBacks) 79 | } 80 | 81 | @inlinable func append(_ value: CFTypeRef) { 82 | append(rawValue: .fromCF(value)) 83 | } 84 | 85 | @inlinable func append(rawValue: UnsafeRawPointer) { 86 | CFArrayAppendValue(self, rawValue) 87 | } 88 | 89 | @inlinable func append(contentsOf array: CFArray, range: CFRange? = nil) { 90 | CFArrayAppendArray(self, array, range ?? array.fullRange) 91 | } 92 | 93 | @inlinable func insert(_ value: CFTypeRef, at index: CFIndex) { 94 | insert(rawValue: .fromCF(value), at: index) 95 | } 96 | 97 | @inlinable func insert(rawValue: UnsafeRawPointer, at index: CFIndex) { 98 | CFArrayInsertValueAtIndex(self, index, rawValue) 99 | } 100 | 101 | @inlinable func set(_ value: CFTypeRef, at index: CFIndex) { 102 | set(rawValue: .fromCF(value), at: index) 103 | } 104 | 105 | @inlinable func set(rawValue: UnsafeRawPointer, at index: CFIndex) { 106 | CFArraySetValueAtIndex(self, index, rawValue) 107 | } 108 | 109 | @inlinable func remove(at index: CFIndex) { 110 | CFArrayRemoveValueAtIndex(self, index) 111 | } 112 | 113 | @inlinable func removeAll() { 114 | CFArrayRemoveAllValues(self) 115 | } 116 | 117 | @inlinable func replace(range: CFRange, values: [CFTypeRef]) { 118 | values.withUnsafeBufferPointer { p in 119 | CFArrayReplaceValues(self, range, UnsafeMutablePointer(OpaquePointer(p.baseAddress)), values.count) 120 | } 121 | } 122 | 123 | @inlinable func swapAt(_ i: CFIndex, _ j: CFIndex) { 124 | CFArrayExchangeValuesAtIndices(self, i, j) 125 | } 126 | } 127 | 128 | -------------------------------------------------------------------------------- /Sources/SwiftCF/CFTypeExt/CFAttributedString.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import CoreFoundation 3 | 4 | public extension CFAttributedString { 5 | 6 | @inlinable static func create(allocator: CFAllocator = .default, string: CFString, attributes: [Key: Any] = [:]) -> CFAttributedString { 7 | return CFAttributedStringCreate(allocator, string, .from(attributes)) 8 | } 9 | 10 | @inlinable func copy(allocator: CFAllocator = .default) -> CFAttributedString { 11 | return CFAttributedStringCreateCopy(allocator, self) 12 | } 13 | 14 | @inlinable func mutableCopy(allocator: CFAllocator = .default, capacity: CFIndex = 0) -> CFMutableAttributedString { 15 | return CFAttributedStringCreateMutableCopy(allocator, capacity, self) 16 | } 17 | 18 | @inlinable var string: CFString { 19 | return CFAttributedStringGetString(self) 20 | } 21 | 22 | @inlinable var count: CFIndex { 23 | return CFAttributedStringGetLength(self) 24 | } 25 | 26 | @inlinable var fullRange: CFRange { 27 | return CFRange(location: 0, length: count) 28 | } 29 | 30 | @inlinable func attributes(at loc: CFIndex) -> (attributes: [Key: Any], effectiveRange: CFRange) { 31 | var effectiveRange = CFRange() 32 | let attr: [Key: Any] = CFAttributedStringGetAttributes(self, loc, &effectiveRange)?.asSwift() ?? [:] 33 | return (attr, effectiveRange) 34 | } 35 | 36 | @inlinable func attribute(at loc: CFIndex, name: Key) -> (attribute: CFTypeRef, effectiveRange: CFRange) { 37 | var effectiveRange = CFRange() 38 | let attr = CFAttributedStringGetAttribute(self, loc, name.rawValue, &effectiveRange)! 39 | return (attr, effectiveRange) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Sources/SwiftCF/CFTypeExt/CFAttributedStringKey.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import CoreFoundation 3 | 4 | #if !canImport(Darwin) && swift(<5.1) 5 | public extension NSAttributedString { 6 | typealias Key = NSAttributedStringKey 7 | } 8 | #endif 9 | 10 | extension CFAttributedString { 11 | 12 | public struct Key: CFStringKey { 13 | 14 | public let rawValue: CFString 15 | 16 | public init(_ key: CFString) { 17 | rawValue = key 18 | } 19 | } 20 | } 21 | 22 | public extension CFAttributedString.Key { 23 | 24 | static func ns(_ key: NSAttributedString.Key) -> CFAttributedString.Key { 25 | return .init(.from(key.rawValue)) 26 | } 27 | } 28 | 29 | public extension NSAttributedString.Key { 30 | 31 | static func cf(_ key: CFAttributedString.Key) -> NSAttributedString.Key { 32 | return .init(key.rawValue.asSwift()) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/SwiftCF/CFTypeExt/CFBoolean.swift: -------------------------------------------------------------------------------- 1 | import CoreFoundation 2 | 3 | public extension CFBoolean { 4 | static let `true` = kCFBooleanTrue! 5 | static let `false` = kCFBooleanFalse! 6 | } 7 | -------------------------------------------------------------------------------- /Sources/SwiftCF/CFTypeExt/CFData.swift: -------------------------------------------------------------------------------- 1 | import CoreFoundation 2 | 3 | public extension CFData { 4 | 5 | @inlinable static func create(allocator: CFAllocator = .default, bytes: UnsafePointer?, length: CFIndex) -> CFData { 6 | return CFDataCreate(allocator, bytes, length) 7 | } 8 | 9 | @inlinable static func createNoCopy(allocator: CFAllocator = .default, bytes: UnsafePointer?, length: CFIndex, bytesDeallocator: CFAllocator) -> CFData { 10 | return CFDataCreateWithBytesNoCopy(allocator, bytes, length, bytesDeallocator) 11 | } 12 | 13 | @inlinable func copy(allocator: CFAllocator = .default) -> CFData { 14 | return CFDataCreateCopy(allocator, self) 15 | } 16 | 17 | @inlinable func mutableCopy(allocator: CFAllocator = .default, capacity: CFIndex = 0) -> CFMutableData { 18 | return CFDataCreateMutableCopy(allocator, capacity, self) 19 | } 20 | 21 | @inlinable var length: CFIndex { 22 | return CFDataGetLength(self) 23 | } 24 | 25 | @inlinable var fullRange: CFRange { 26 | return CFRange(location: 0, length: length) 27 | } 28 | 29 | @inlinable var bytePtr: UnsafeBufferPointer { 30 | let p = CFDataGetBytePtr(self) 31 | return UnsafeBufferPointer(start: p, count: length) 32 | } 33 | 34 | @inlinable subscript(range: CFRange) -> [UInt8] { 35 | guard range.length > 0 else { 36 | return [] 37 | } 38 | return Array(unsafeUninitializedCapacity: range.length) { p, count in 39 | CFDataGetBytes(self, range, p.baseAddress) 40 | count = range.length 41 | } 42 | } 43 | } 44 | 45 | extension CFData: RandomAccessCollection { 46 | 47 | @inlinable public var startIndex: Int { 48 | return 0 49 | } 50 | 51 | @inlinable public var endIndex: Int { 52 | return length 53 | } 54 | 55 | @inlinable public subscript(position: Int) -> UInt8 { 56 | return bytePtr[position] 57 | } 58 | } 59 | 60 | // MARK: - CFMutableData 61 | 62 | public extension CFMutableData { 63 | 64 | @inlinable static func create(allocator: CFAllocator = .default, capacity: CFIndex = 0) -> CFMutableData { 65 | return CFDataCreateMutable(allocator, capacity) 66 | } 67 | 68 | @inlinable var mutableBytePtr: UnsafeMutableBufferPointer { 69 | let p = CFDataGetMutableBytePtr(self) 70 | return UnsafeMutableBufferPointer(start: p, count: length) 71 | } 72 | 73 | @inlinable func setLength(_ length: CFIndex) { 74 | CFDataSetLength(self, length) 75 | } 76 | 77 | @inlinable func increaseLength(_ extraLength: CFIndex) { 78 | CFDataIncreaseLength(self, extraLength) 79 | } 80 | 81 | @inlinable func append(bytes: UnsafePointer, length: CFIndex) { 82 | CFDataAppendBytes(self, bytes, length) 83 | } 84 | 85 | @inlinable func replaceBytes(range: CFRange, newBytes: UnsafePointer, length: CFIndex) { 86 | CFDataReplaceBytes(self, range, newBytes, length) 87 | } 88 | 89 | @inlinable func deleteBytes(range: CFRange) { 90 | CFDataDeleteBytes(self, range) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Sources/SwiftCF/CFTypeExt/CFDictionary.swift: -------------------------------------------------------------------------------- 1 | import CoreFoundation 2 | 3 | public extension CFDictionary { 4 | 5 | static let empty = CFDictionary.create(keys: nil, values: nil, count: 0) 6 | 7 | @inlinable static func create(allocator: CFAllocator = .default, keys: UnsafeMutablePointer?, values: UnsafeMutablePointer?, count: CFIndex, keyCallBacks: UnsafePointer? = pCFTypeDictionaryKeyCallBacks, valueCallBacks: UnsafePointer? = pCFTypeDictionaryValueCallBacks) -> CFDictionary { 8 | return CFDictionaryCreate(allocator, keys, values, count, keyCallBacks, valueCallBacks) 9 | } 10 | 11 | @inlinable func copy(allocator: CFAllocator = .default) -> CFDictionary { 12 | return CFDictionaryCreateCopy(allocator, self) 13 | } 14 | 15 | @inlinable func mutableCopy(allocator: CFAllocator = .default, capacity: CFIndex = 0) -> CFMutableDictionary { 16 | return CFDictionaryCreateMutableCopy(allocator, capacity, self) 17 | } 18 | 19 | @inlinable var count: CFIndex { 20 | return CFDictionaryGetCount(self) 21 | } 22 | 23 | @inlinable func count(ofKey key: CFTypeRef) -> CFIndex { 24 | return CFDictionaryGetCountOfKey(self, .fromCF(key)) 25 | } 26 | 27 | @inlinable func count(ofValue value: CFTypeRef) -> CFIndex { 28 | return CFDictionaryGetCountOfValue(self, .fromCF(value)) 29 | } 30 | 31 | @inlinable func contains(key: CFTypeRef) -> Bool { 32 | return CFDictionaryContainsValue(self, .fromCF(key)) 33 | } 34 | 35 | @inlinable func contains(value: CFTypeRef) -> Bool { 36 | return CFDictionaryContainsValue(self, .fromCF(value)) 37 | } 38 | 39 | @inlinable func value(key: CFTypeRef) -> CFTypeRef? { 40 | return rawValue(key: key)?.asCF() 41 | } 42 | 43 | @inlinable func rawValue(key: CFTypeRef) -> UnsafeRawPointer? { 44 | return CFDictionaryGetValue(self, .fromCF(key)) 45 | } 46 | } 47 | 48 | public extension CFMutableDictionary { 49 | 50 | @inlinable static func create(allocator: CFAllocator = .default, capacity: CFIndex, keyCallBacks: UnsafePointer? = pCFTypeDictionaryKeyCallBacks, valueCallBacks: UnsafePointer? = pCFTypeDictionaryValueCallBacks) -> CFMutableDictionary { 51 | return CFDictionaryCreateMutable(allocator, capacity, keyCallBacks, valueCallBacks) 52 | } 53 | 54 | @inlinable func addValue(_ value: CFTypeRef, for key: CFTypeRef) { 55 | CFDictionaryAddValue(self, .fromCF(key), .fromCF(value)) 56 | } 57 | 58 | @inlinable func setValue(_ value: CFTypeRef, for key: CFTypeRef) { 59 | CFDictionarySetValue(self, .fromCF(key), .fromCF(value)) 60 | } 61 | 62 | @inlinable func replaceValue(_ value: CFTypeRef, for key: CFTypeRef) { 63 | CFDictionaryReplaceValue(self, .fromCF(key), .fromCF(value)) 64 | } 65 | 66 | @inlinable func removeValue(for key: CFTypeRef) { 67 | CFDictionaryRemoveValue(self, .fromCF(key)) 68 | } 69 | 70 | @inlinable func removeAllValues() { 71 | CFDictionaryRemoveAllValues(self) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Sources/SwiftCF/CFTypeExt/CFError.swift: -------------------------------------------------------------------------------- 1 | import CoreFoundation 2 | 3 | public extension CFError { 4 | 5 | @inlinable static func create(allocator: CFAllocator = .default, domain: Domain, code: CFIndex, userInfo: [UserInfoKey: Any] = [:]) -> CFError { 6 | return CFErrorCreate(allocator, domain.rawValue, code, .from(userInfo)) 7 | } 8 | 9 | @inlinable var domain: Domain { 10 | return Domain(CFErrorGetDomain(self)) 11 | } 12 | 13 | @inlinable var code: CFIndex { 14 | return CFErrorGetCode(self) 15 | } 16 | 17 | @inlinable func userInfo() -> [UserInfoKey: Any] { 18 | return CFErrorCopyUserInfo(self)?.asSwift() ?? [:] 19 | } 20 | 21 | @inlinable func description() -> CFString? { 22 | return CFErrorCopyDescription(self) 23 | } 24 | 25 | @inlinable func failureReason() -> CFString? { 26 | return CFErrorCopyFailureReason(self) 27 | } 28 | 29 | @inlinable func recoverySuggestion() -> CFString? { 30 | return CFErrorCopyRecoverySuggestion(self) 31 | } 32 | } 33 | 34 | // MARK: - Domain 35 | 36 | extension CFError { 37 | 38 | public struct Domain: CFStringKey { 39 | 40 | public let rawValue: CFString 41 | 42 | public init(_ key: CFString) { 43 | rawValue = key 44 | } 45 | } 46 | } 47 | 48 | public extension CFError.Domain { 49 | static let posix = CFError.Domain(kCFErrorDomainPOSIX) 50 | static let osStatus = CFError.Domain(kCFErrorDomainOSStatus) 51 | static let mach = CFError.Domain(kCFErrorDomainMach) 52 | static let cocoa = CFError.Domain(kCFErrorDomainCocoa) 53 | } 54 | 55 | // MARK: - UserInfoKey 56 | 57 | extension CFError { 58 | 59 | public struct UserInfoKey: CFStringKey { 60 | 61 | public let rawValue: CFString 62 | 63 | public init(_ key: CFString) { 64 | rawValue = key 65 | } 66 | } 67 | } 68 | 69 | public extension CFError.UserInfoKey { 70 | static let localizedDescription = CFError.UserInfoKey(kCFErrorLocalizedDescriptionKey) 71 | @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) 72 | static let localizedFailure = CFError.UserInfoKey(kCFErrorLocalizedFailureKey) 73 | static let localizedFailureReason = CFError.UserInfoKey(kCFErrorLocalizedFailureReasonKey) 74 | static let localizedRecoverySuggestion = CFError.UserInfoKey(kCFErrorLocalizedRecoverySuggestionKey) 75 | static let description = CFError.UserInfoKey(kCFErrorDescriptionKey) 76 | static let underlyingError = CFError.UserInfoKey(kCFErrorUnderlyingErrorKey) 77 | static let url = CFError.UserInfoKey(kCFErrorURLKey) 78 | static let filePath = CFError.UserInfoKey(kCFErrorFilePathKey) 79 | } 80 | -------------------------------------------------------------------------------- /Sources/SwiftCF/CFTypeExt/CFNumber.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import CoreFoundation 3 | 4 | public extension CFNumber { 5 | 6 | static let positiveInfinity = kCFNumberPositiveInfinity! 7 | static let negativeInfinity = kCFNumberNegativeInfinity! 8 | static let nan = kCFNumberNaN! 9 | 10 | @inlinable var type: CFNumberType { 11 | return CFNumberGetType(self) 12 | } 13 | 14 | @inlinable var byteSize: CFIndex { 15 | return CFNumberGetByteSize(self) 16 | } 17 | 18 | @inlinable var isFloatType: Bool { 19 | return CFNumberIsFloatType(self) 20 | } 21 | 22 | @inlinable func value() -> T { 23 | return T._from(cfNumber: self).result 24 | } 25 | } 26 | 27 | // MARK: - CFNumberRepresentable 28 | 29 | public protocol CFNumberRepresentable { 30 | static var cfNumberType: CFNumberType { get } 31 | static func _from(cfNumber: CFNumber) -> (result: Self, lossless: Bool) 32 | } 33 | 34 | public extension CFNumberRepresentable where Self: BinaryInteger { 35 | static func _from(cfNumber: CFNumber) -> (result: Self, lossless: Bool) { 36 | var result = Self.zero 37 | let lossless = CFNumberGetValue(cfNumber, Self.cfNumberType, &result) 38 | return (result, lossless) 39 | } 40 | } 41 | 42 | public extension CFNumberRepresentable where Self: FloatingPoint { 43 | static func _from(cfNumber: CFNumber) -> (result: Self, lossless: Bool) { 44 | var result = Self.zero 45 | let lossless = CFNumberGetValue(cfNumber, Self.cfNumberType, &result) 46 | return (result, lossless) 47 | } 48 | } 49 | 50 | // TODO: Linux: CFNumberType on Linux 51 | #if canImport(Darwin) 52 | 53 | extension Int8: CFNumberRepresentable { 54 | public static let cfNumberType = CFNumberType.sInt8Type 55 | } 56 | 57 | extension Int16: CFNumberRepresentable { 58 | public static let cfNumberType = CFNumberType.sInt16Type 59 | } 60 | 61 | extension Int32: CFNumberRepresentable { 62 | public static let cfNumberType = CFNumberType.sInt32Type 63 | } 64 | 65 | extension Int64: CFNumberRepresentable { 66 | public static let cfNumberType = CFNumberType.sInt64Type 67 | } 68 | 69 | extension NSInteger: CFNumberRepresentable { 70 | public static let cfNumberType = CFNumberType.nsIntegerType 71 | } 72 | 73 | extension Float32: CFNumberRepresentable { 74 | public static let cfNumberType = CFNumberType.float32Type 75 | } 76 | 77 | extension Float64: CFNumberRepresentable { 78 | public static let cfNumberType = CFNumberType.float64Type 79 | } 80 | 81 | #if canImport(CoreGraphics) 82 | 83 | import CoreGraphics 84 | 85 | extension CGFloat: CFNumberRepresentable { 86 | public static let cfNumberType = CFNumberType.cgFloatType 87 | } 88 | 89 | #endif // canImport(CoreGraphics) 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /Sources/SwiftCF/CFTypeExt/CFRange.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import CoreFoundation 3 | 4 | public extension CFRange { 5 | 6 | static let zero = CFRange(location: 0, length: 0) 7 | 8 | @inlinable var asNS: NSRange { 9 | return NSRange(location: location, length: length) 10 | } 11 | 12 | @inlinable var asRange: Range { 13 | return location..<(location+length) 14 | } 15 | } 16 | 17 | public extension NSRange { 18 | 19 | @inlinable var asCF: CFRange { 20 | return CFRange(location: location, length: length) 21 | } 22 | } 23 | 24 | public extension RangeExpression where Bound: FixedWidthInteger { 25 | 26 | @inlinable var asCF: CFRange { 27 | let r = relative(to: 0.. Mode { 22 | return CFRunLoopCopyCurrentMode(self) 23 | } 24 | 25 | @inlinable func allModes() -> [Mode] { 26 | return CFRunLoopCopyAllModes(self)._bridgeToNS().map { Mode._from(raw: $0 as! CFString ) } 27 | } 28 | 29 | @inlinable func addCommonMode(_ mode: Mode) { 30 | CFRunLoopAddCommonMode(self, mode) 31 | } 32 | 33 | @inlinable func nextTimerFireDate(mode: Mode) -> CFAbsoluteTime { 34 | return CFRunLoopGetNextTimerFireDate(self, mode) 35 | } 36 | 37 | @inlinable static func run() { 38 | CFRunLoopRun() 39 | } 40 | 41 | @inlinable static func run(in mode: Mode, seconds: CFTimeInterval = 0, returnAfterSourceHandled: Bool = true) -> RunResult { 42 | return CFRunLoopRunInMode(mode, seconds, returnAfterSourceHandled) 43 | } 44 | 45 | @inlinable var isWaiting: Bool { 46 | return CFRunLoopIsWaiting(self) 47 | } 48 | 49 | @inlinable func wakeUp() { 50 | CFRunLoopWakeUp(self) 51 | } 52 | 53 | @inlinable func stop() { 54 | CFRunLoopStop(self) 55 | } 56 | 57 | @inlinable func perform(mode: Mode, block: @escaping () -> Void) { 58 | CFRunLoopPerformBlock(self, mode._raw, block) 59 | } 60 | 61 | @inlinable func perform(modes: [Mode], block: @escaping () -> Void) { 62 | let arr = CFArray._bridgeFromNS(.init(array: modes.map { $0._raw })) 63 | CFRunLoopPerformBlock(self, arr, block) 64 | } 65 | 66 | @inlinable func contains(source: Source, mode: Mode) { 67 | CFRunLoopContainsSource(self, source, mode) 68 | } 69 | 70 | @inlinable func add(source: Source, mode: Mode) { 71 | CFRunLoopAddSource(self, source, mode) 72 | } 73 | 74 | @inlinable func remove(source: Source, mode: Mode) { 75 | CFRunLoopRemoveSource(self, source, mode) 76 | } 77 | 78 | @inlinable func contains(observer: Observer, mode: Mode) { 79 | CFRunLoopContainsObserver(self, observer, mode) 80 | } 81 | 82 | @inlinable func add(observer: Observer, mode: Mode) { 83 | CFRunLoopAddObserver(self, observer, mode) 84 | } 85 | 86 | @inlinable func remove(observer: Observer, mode: Mode) { 87 | CFRunLoopRemoveObserver(self, observer, mode) 88 | } 89 | 90 | @inlinable func contains(timer: Timer, mode: Mode) { 91 | CFRunLoopContainsTimer(self, timer, mode) 92 | } 93 | 94 | @inlinable func add(timer: Timer, mode: Mode) { 95 | CFRunLoopAddTimer(self, timer, mode) 96 | } 97 | 98 | @inlinable func remove(timer: Timer, mode: Mode) { 99 | CFRunLoopRemoveTimer(self, timer, mode) 100 | } 101 | } 102 | 103 | // MARK: - Source 104 | 105 | public extension CFRunLoop.Source { 106 | 107 | typealias Context = CFRunLoopSourceContext 108 | typealias Context1 = CFRunLoopSourceContext1 109 | 110 | @inlinable static func create(allocator: CFAllocator = .default, order: CFIndex, context: Context) -> CFRunLoop.Source { 111 | var ctx = context 112 | return CFRunLoopSourceCreate(allocator, order, &ctx) 113 | } 114 | 115 | @inlinable var order: CFIndex { 116 | return CFRunLoopSourceGetOrder(self) 117 | } 118 | 119 | @inlinable func invalidate() { 120 | return CFRunLoopSourceInvalidate(self) 121 | } 122 | 123 | @inlinable var isValid: Bool { 124 | return CFRunLoopSourceIsValid(self) 125 | } 126 | 127 | @inlinable var context: Context { 128 | var ctx = Context() 129 | CFRunLoopSourceGetContext(self, &ctx) 130 | return ctx 131 | } 132 | 133 | @inlinable func signal() { 134 | return CFRunLoopSourceSignal(self) 135 | } 136 | } 137 | 138 | // MARK: - Observer 139 | 140 | public extension CFRunLoop.Observer { 141 | 142 | typealias Context = CFRunLoopObserverContext 143 | typealias CallBack = CFRunLoopObserverCallBack 144 | 145 | @inlinable static func create(allocator: CFAllocator = .default, activities: CFRunLoop.Activity, repeats: Bool, order: CFIndex, callout: @escaping CallBack, context: Context) -> CFRunLoopObserver { 146 | var ctx = context 147 | return CFRunLoopObserverCreate(allocator, activities._raw, repeats, order, callout, &ctx) 148 | } 149 | 150 | @inlinable static func create(allocator: CFAllocator = .default, activities: CFRunLoop.Activity, repeats: Bool, order: CFIndex, block: @escaping (CFRunLoop.Observer?, CFRunLoop.Activity) -> Void) -> CFRunLoopObserver { 151 | return CFRunLoopObserverCreateWithHandler(allocator, activities._raw, repeats, order, block) 152 | } 153 | 154 | @inlinable var activity: CFRunLoop.Activity { 155 | let raw = CFRunLoopObserverGetActivities(self) 156 | return CFRunLoop.Activity._from(raw: raw) 157 | } 158 | 159 | @inlinable var doesRepeat: Bool { 160 | return CFRunLoopObserverDoesRepeat(self) 161 | } 162 | 163 | @inlinable var order: CFIndex { 164 | return CFRunLoopObserverGetOrder(self) 165 | } 166 | 167 | @inlinable func invalidate() { 168 | return CFRunLoopObserverInvalidate(self) 169 | } 170 | 171 | @inlinable var isValid: Bool { 172 | return CFRunLoopObserverIsValid(self) 173 | } 174 | 175 | @inlinable var context: Context { 176 | var ctx = Context() 177 | CFRunLoopObserverGetContext(self, &ctx) 178 | return ctx 179 | } 180 | } 181 | 182 | // MARK: - Timer 183 | 184 | public extension CFRunLoop.Timer { 185 | 186 | typealias Context = CFRunLoopTimerContext 187 | typealias CallBack = CFRunLoopTimerCallBack 188 | 189 | @inlinable static func create(allocator: CFAllocator = .default, fireDate: CFAbsoluteTime, interval: CFTimeInterval, flags: CFOptionFlags, order: CFIndex, callout: @escaping CallBack, context: Context) -> CFRunLoopTimer { 190 | var ctx = context 191 | return CFRunLoopTimerCreate(allocator, fireDate, interval, flags, order, callout, &ctx) 192 | } 193 | 194 | @inlinable static func create(allocator: CFAllocator = .default, fireDate: CFAbsoluteTime, interval: CFTimeInterval, flags: CFOptionFlags, order: CFIndex, block: @escaping (CFRunLoop.Timer?) -> Void) -> CFRunLoopTimer { 195 | return CFRunLoopTimerCreateWithHandler(allocator, fireDate, interval, flags, order, block) 196 | } 197 | 198 | @inlinable var nextFireDate: CFAbsoluteTime { 199 | get { 200 | return CFRunLoopTimerGetNextFireDate(self) 201 | } 202 | set { 203 | CFRunLoopTimerSetNextFireDate(self, newValue) 204 | } 205 | } 206 | 207 | @inlinable var interval: CFTimeInterval { 208 | return CFRunLoopTimerGetInterval(self) 209 | } 210 | 211 | @inlinable var doesRepeat: Bool { 212 | return CFRunLoopTimerDoesRepeat(self) 213 | } 214 | 215 | @inlinable var order: CFIndex { 216 | return CFRunLoopTimerGetOrder(self) 217 | } 218 | 219 | @inlinable func invalidate() { 220 | return CFRunLoopTimerInvalidate(self) 221 | } 222 | 223 | @inlinable var isValid: Bool { 224 | return CFRunLoopTimerIsValid(self) 225 | } 226 | 227 | @inlinable var context: Context { 228 | var ctx = Context() 229 | CFRunLoopTimerGetContext(self, &ctx) 230 | return ctx 231 | } 232 | 233 | @inlinable var tolerance: CFTimeInterval { 234 | get { 235 | return CFRunLoopTimerGetTolerance(self) 236 | } 237 | set { 238 | CFRunLoopTimerSetTolerance(self, newValue) 239 | } 240 | } 241 | } 242 | 243 | // MARK: - 244 | 245 | extension CFRunLoop.Mode { 246 | 247 | @usableFromInline 248 | var _raw: CFString { 249 | #if canImport(Darwin) 250 | return rawValue 251 | #else 252 | return self 253 | #endif 254 | } 255 | 256 | @usableFromInline 257 | static func _from(raw: CFString) -> CFRunLoop.Mode { 258 | #if canImport(Darwin) 259 | return .init(rawValue: raw) 260 | #else 261 | return raw 262 | #endif 263 | } 264 | } 265 | 266 | extension CFRunLoop.Activity { 267 | 268 | @usableFromInline 269 | var _raw: CFOptionFlags { 270 | #if canImport(Darwin) || swift(>=5.3) 271 | return rawValue 272 | #else 273 | return self 274 | #endif 275 | } 276 | 277 | @usableFromInline 278 | static func _from(raw: CFOptionFlags) -> CFRunLoop.Activity { 279 | #if canImport(Darwin) || swift(>=5.3) 280 | return .init(rawValue: raw) 281 | #else 282 | return raw 283 | #endif 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /Sources/SwiftCF/CFTypeExt/CFString.swift: -------------------------------------------------------------------------------- 1 | import CoreFoundation 2 | 3 | public extension CFString { 4 | 5 | @inlinable static func create(allocator: CFAllocator = .default, cString: UnsafePointer?, encoding: CFStringEncoding)-> CFString { 6 | return CFStringCreateWithCString(allocator, cString, encoding) 7 | } 8 | 9 | @inlinable static func create(allocator: CFAllocator = .default, bytes: UnsafePointer?, length: CFIndex, encoding: CFStringEncoding, isExternalRepresentation: Bool = false)-> CFString { 10 | return CFStringCreateWithBytes(allocator, bytes, length, encoding, isExternalRepresentation) 11 | } 12 | 13 | @inlinable static func createNoCopy(allocator: CFAllocator = .default, cString: UnsafePointer?, encoding: CFStringEncoding, contentsDeallocator: CFAllocator)-> CFString { 14 | return CFStringCreateWithCStringNoCopy(allocator, cString, encoding, contentsDeallocator) 15 | } 16 | 17 | @inlinable static func createNoCopy(allocator: CFAllocator = .default, bytes: UnsafePointer?, length: CFIndex, encoding: CFStringEncoding, isExternalRepresentation: Bool = false, contentsDeallocator: CFAllocator)-> CFString { 18 | return CFStringCreateWithBytesNoCopy(allocator, bytes, length, encoding, isExternalRepresentation, contentsDeallocator) 19 | } 20 | 21 | @inlinable func copy(allocator: CFAllocator = .default) -> CFString { 22 | return CFStringCreateCopy(allocator, self) 23 | } 24 | 25 | @inlinable func mutableCopy(allocator: CFAllocator = .default, capacity: CFIndex = 0) -> CFMutableString { 26 | return CFStringCreateMutableCopy(allocator, capacity, self) 27 | } 28 | 29 | @inlinable var length: CFIndex { 30 | return CFStringGetLength(self) 31 | } 32 | 33 | @inlinable var fullRange: CFRange { 34 | return CFRange(location: 0, length: length) 35 | } 36 | 37 | /// This function either returns the requested pointer immediately, with no memory allocations and no copying, in constant time, or returns NULL. 38 | @inlinable func cStringPtr(encoding: CFStringEncoding) -> UnsafePointer? { 39 | return CFStringGetCStringPtr(self, encoding) 40 | } 41 | 42 | @inlinable func substring(allocator: CFAllocator = .default, range: CFRange) -> CFString { 43 | return CFStringCreateWithSubstring(allocator, self, range) 44 | } 45 | 46 | @inlinable var smallestEncoding: CFStringEncoding { 47 | return CFStringGetSmallestEncoding(self) 48 | } 49 | 50 | @inlinable var fastestEncoding: CFStringEncoding { 51 | return CFStringGetFastestEncoding(self) 52 | } 53 | 54 | @inlinable static var systemEncoding: CFStringEncoding { 55 | return CFStringGetSystemEncoding() 56 | } 57 | } 58 | 59 | #if canImport(Carbon) 60 | 61 | import Carbon 62 | 63 | public extension CFString { 64 | 65 | @inlinable static var applicationEncoding: CFStringEncoding { 66 | return GetApplicationTextEncoding() 67 | } 68 | } 69 | 70 | #endif 71 | 72 | // MARK: - CFMutableString 73 | 74 | public extension CFMutableString { 75 | 76 | /// Perform in-place transliteration on a mutable string. 77 | /// 78 | /// The transformation represented by transform is applied to the given 79 | /// range of string, modifying it in place. Only the specified range is 80 | /// modified, but the transform may look at portions of the string outside 81 | /// that range for context. Reasons that the transform may be unsuccessful 82 | /// include an invalid transform identifier, and attempting to reverse an 83 | /// irreversible transform. 84 | /// 85 | /// - Parameters: 86 | /// - transform: The transformation to apply. 87 | /// - range: The range over which the transformation is applied. `nil` 88 | /// causes the whole string to be transformed. 89 | /// - reverse: A Boolean that, if true, specifies that the inverse 90 | /// transform should be used (if it exists). 91 | /// - Returns: The new range corresponding to the original range. Or nil if 92 | /// unsuccessful. 93 | @inlinable func transform(_ transform: Transform, range: CFRange? = nil, reverse: Bool = false) -> CFRange? { 94 | var range = range ?? fullRange 95 | guard CFStringTransform(self, &range, transform.rawValue, reverse) else { 96 | return nil 97 | } 98 | return range 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Sources/SwiftCF/CFTypeExt/CFStringTokenizer.swift: -------------------------------------------------------------------------------- 1 | #if canImport(Darwin) 2 | 3 | import CoreFoundation 4 | 5 | public extension CFStringTokenizer { 6 | 7 | enum Attribute: CFOptionFlags { 8 | 9 | /// `kCFStringTokenizerAttributeLatinTranscription` 10 | case latinTranscription = 0b1_0000_0000_0000_0000 11 | 12 | /// `kCFStringTokenizerAttributeLanguage` 13 | case language = 0b10_0000_0000_0000_0000 14 | } 15 | 16 | enum Unit: CFOptionFlags { 17 | 18 | /// `kCFStringTokenizerUnitWord` 19 | case word = 0 20 | 21 | /// `kCFStringTokenizerUnitSentence` 22 | case sentence = 1 23 | 24 | /// `kCFStringTokenizerUnitParagraph` 25 | case paragraph = 2 26 | 27 | /// `kCFStringTokenizerUnitLineBreak` 28 | case lineBreak = 3 29 | 30 | /// `kCFStringTokenizerUnitWordBoundary` 31 | case wordBoundary = 4 32 | } 33 | 34 | @inlinable static func create(allocator: CFAllocator = .default, string: CFString, range: CFRange? = nil, unit: Unit = .wordBoundary, locale: CFLocale? = nil) -> CFStringTokenizer { 35 | return CFStringTokenizerCreate(allocator, string, range ?? string.fullRange, unit.rawValue, locale) 36 | } 37 | 38 | @inlinable func setString(_ string: CFString, range: CFRange? = nil) { 39 | CFStringTokenizerSetString(self, string, range ?? string.fullRange) 40 | } 41 | 42 | @inlinable func goToToken(at index: CFIndex) -> CFStringTokenizerTokenType? { 43 | let token = CFStringTokenizerGoToTokenAtIndex(self, index) 44 | if token.isEmpty { return nil } 45 | return token 46 | } 47 | 48 | @inlinable func advanceToNextToken() -> CFStringTokenizerTokenType? { 49 | let token = CFStringTokenizerAdvanceToNextToken(self) 50 | if token.isEmpty { return nil } 51 | return token 52 | } 53 | 54 | @inlinable func currentTokenRange() -> CFRange { 55 | return CFStringTokenizerGetCurrentTokenRange(self) 56 | } 57 | 58 | @inlinable func currentTokenAttribute(_ attribute: Attribute) -> CFString? { 59 | return CFStringTokenizerCopyCurrentTokenAttribute(self, attribute.rawValue).map { 60 | cfCast($0, to: CFString.self)! 61 | } 62 | } 63 | 64 | @inlinable func currentSubTokens(maxRangeLength: CFIndex = 0) -> [CFStringTokenizerTokenType] { 65 | let arr = CFMutableArray.create() 66 | CFStringTokenizerGetCurrentSubTokens(self, nil, maxRangeLength, arr) 67 | return arr as! [CFStringTokenizerTokenType] 68 | } 69 | 70 | @inlinable static func bestLanguage(for string: CFString, range: CFRange? = nil) -> CFString? { 71 | return CFStringTokenizerCopyBestStringLanguage(string, range ?? string.fullRange) 72 | } 73 | } 74 | 75 | extension CFStringTokenizer: IteratorProtocol { 76 | 77 | @inlinable public func next() -> CFStringTokenizerTokenType? { 78 | return advanceToNextToken() 79 | } 80 | } 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /Sources/SwiftCF/CFTypeExt/CFStringTransformer.swift: -------------------------------------------------------------------------------- 1 | import CoreFoundation 2 | 3 | extension CFString { 4 | 5 | public struct Transform: CFStringKey { 6 | 7 | public let rawValue: CFString 8 | 9 | public init(_ key: CFString) { 10 | rawValue = key 11 | } 12 | } 13 | } 14 | 15 | public extension CFString.Transform { 16 | 17 | static func icuTransform(id: CFString) -> CFString.Transform { 18 | return .init(id) 19 | } 20 | 21 | static let stripCombiningMarks = CFString.Transform(kCFStringTransformStripCombiningMarks) 22 | static let toLatin = CFString.Transform(kCFStringTransformToLatin) 23 | static let fullwidthHalfwidth = CFString.Transform(kCFStringTransformFullwidthHalfwidth) 24 | static let latinKatakana = CFString.Transform(kCFStringTransformLatinKatakana) 25 | static let latinHiragana = CFString.Transform(kCFStringTransformLatinHiragana) 26 | static let hiraganaKatakana = CFString.Transform(kCFStringTransformHiraganaKatakana) 27 | static let mandarinLatin = CFString.Transform(kCFStringTransformMandarinLatin) 28 | static let latinHangul = CFString.Transform(kCFStringTransformLatinHangul) 29 | static let latinArabic = CFString.Transform(kCFStringTransformLatinArabic) 30 | static let latinHebrew = CFString.Transform(kCFStringTransformLatinHebrew) 31 | static let latinThai = CFString.Transform(kCFStringTransformLatinThai) 32 | static let latinCyrillic = CFString.Transform(kCFStringTransformLatinCyrillic) 33 | static let latinGreek = CFString.Transform(kCFStringTransformLatinGreek) 34 | static let toXMLHex = CFString.Transform(kCFStringTransformToXMLHex) 35 | static let toUnicodeName = CFString.Transform(kCFStringTransformToUnicodeName) 36 | static let stripDiacritics = CFString.Transform(kCFStringTransformStripDiacritics) 37 | } 38 | -------------------------------------------------------------------------------- /Sources/SwiftCF/Cast/CFBridge.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import CoreFoundation 3 | 4 | extension CFType { 5 | 6 | public static func from(_ v: T) -> Self where T._CFType == Self { 7 | return v._bridgeToCF() 8 | } 9 | } 10 | 11 | extension _CFTollFreeBridgeable { 12 | 13 | public static func from(_ v: BridgedNSType) -> Self { 14 | return ._bridgeFromNS(v) 15 | } 16 | } 17 | 18 | extension CFType { 19 | 20 | public func asSwift() -> T where T._CFType == Self { 21 | return T._bridgeFromCF(self) 22 | } 23 | } 24 | 25 | extension _CFTollFreeBridgeable { 26 | 27 | public func asNS() -> BridgedNSType { 28 | return _bridgeToNS() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/SwiftCF/Cast/CFCast.swift: -------------------------------------------------------------------------------- 1 | import CoreFoundation 2 | 3 | @inlinable public func cfCast(_ v: Source, to type: Target.Type = Target.self) -> Target? { 4 | // TODO: Cast to _CFTollFreeBridgingMutableType 5 | // if let t = T.self as? _CFTollFreeBridgingNSType.Type { 6 | // return type(of: v) as? t.bridgedNSType 7 | // } 8 | // if Target.self is _CFMutableType.Type { 9 | // assertionFailure("Cast '\(v)' to CoreFoundation mutable type '\(type)' is not supported and will always produce nil.") 10 | // return nil 11 | // } 12 | let ref = v as CFTypeRef 13 | if CFGetTypeID(ref) == type.typeID { 14 | return (ref as! Target) 15 | } else { 16 | return nil 17 | } 18 | } 19 | 20 | @inlinable public func cfCast(_ v: T, to type: Result.Type = Result.self) -> Result? { 21 | if let nsValue = v as? Result.BridgedNSType { 22 | return (nsValue as! Result) 23 | } else { 24 | return nil 25 | } 26 | } 27 | 28 | // MARK: - 29 | 30 | public extension CFType { 31 | @inlinable static func cast(_ v: Source) -> Self? { 32 | return cfCast(v, to: Self.self) 33 | } 34 | } 35 | 36 | public extension _CFTollFreeBridgeable { 37 | @inlinable static func cast(_ v: Source) -> Self? { 38 | return cfCast(v, to: Self.self) 39 | } 40 | } 41 | 42 | @inlinable public func cfUnwrap(_ v: CFTypeRef) -> CFTypeRef? { 43 | return kCFNull.cfEqual(to: v) ? nil : v 44 | } 45 | -------------------------------------------------------------------------------- /Sources/SwiftCF/Cast/_CFConvertible.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import CoreFoundation 3 | 4 | /// _CFConvertible without associatedtype 5 | public protocol __CFConvertible { 6 | 7 | func __bridgeToCF() -> CFTypeRef 8 | 9 | static func __bridgeFromCF(_ source: CFTypeRef) -> Self 10 | } 11 | 12 | // TODO: strip it from public API. currently required by `CFStringKey`. 13 | public protocol _CFConvertible: __CFConvertible, _ObjectiveCBridgeable { 14 | 15 | associatedtype _CFType: CFType 16 | 17 | func _bridgeToCF() -> _CFType 18 | 19 | static func _bridgeFromCF(_ source: _CFType) -> Self 20 | } 21 | 22 | public extension _CFConvertible { 23 | 24 | func __bridgeToCF() -> CFTypeRef { 25 | return _bridgeToCF() 26 | } 27 | 28 | static func __bridgeFromCF(_ source: CFTypeRef) -> Self { 29 | guard let s = _CFType.cast(source) else { 30 | preconditionFailure("failed to bridge \(source) to incompatible CoreFoundation type \(_CFType.self)") 31 | } 32 | return _bridgeFromCF(s) 33 | } 34 | } 35 | 36 | public extension _CFConvertible where _CFType: _CFTollFreeBridgeable, _ObjectiveCType == _CFType.BridgedNSType { 37 | 38 | func _bridgeToCF() -> _CFType { 39 | return _CFType._bridgeFromNS(_bridgeToObjectiveC()) 40 | } 41 | 42 | static func _bridgeFromCF(_ source: _CFType) -> Self { 43 | return _unconditionallyBridgeFromObjectiveC(source._bridgeToNS()) 44 | } 45 | } 46 | 47 | // MARK: Container 48 | 49 | extension Array: _CFConvertible { 50 | 51 | // Prevent Swift bridge value types to `_SwiftObject`. 52 | public func _bridgeToCF() -> CFArray { 53 | return CFArray._bridgeFromNS(map(_bridgeToCFIfNeeded)._bridgeToObjectiveC()) 54 | } 55 | 56 | public static func _bridgeFromCF(_ source: CFArray) -> [Element] { 57 | #if canImport(Darwin) 58 | return source as! [Element] 59 | #else 60 | return source._bridgeToNS().map(_bridgeFromCFIfNeeded) 61 | #endif 62 | } 63 | } 64 | 65 | extension Dictionary: _CFConvertible { 66 | 67 | // Prevent Swift bridge value types to _SwiftObject 68 | public func _bridgeToCF() -> CFDictionary { 69 | return CFDictionary._bridgeFromNS(mapValues(_bridgeToCFIfNeeded)._bridgeToObjectiveC()) 70 | } 71 | 72 | public static func _bridgeFromCF(_ source: CFDictionary) -> [Key: Value] { 73 | #if canImport(Darwin) 74 | return source as! [Key: Value] 75 | #else 76 | var result = [Key: Value](minimumCapacity: source.count) 77 | source._bridgeToNS().enumerateKeysAndObjects { key, value, stop in 78 | result[_bridgeFromCFIfNeeded(key)] = _bridgeFromCFIfNeeded(value) 79 | } 80 | return result 81 | #endif 82 | } 83 | } 84 | 85 | extension Set: _CFConvertible { 86 | 87 | public func _bridgeToCF() -> CFSet { 88 | return CFSet._bridgeFromNS(NSSet(array: self.map(_bridgeToCFIfNeeded))) 89 | } 90 | 91 | public static func _bridgeFromCF(_ source: CFSet) -> Set { 92 | #if canImport(Darwin) 93 | return source as! Set 94 | #else 95 | return Set(source._bridgeToNS().map(_bridgeFromCFIfNeeded)) 96 | #endif 97 | } 98 | } 99 | 100 | // MARK: - Value 101 | 102 | extension Bool: _CFConvertible { 103 | public typealias _CFType = CFBoolean 104 | } 105 | 106 | extension Calendar: _CFConvertible { 107 | public typealias _CFType = CFCalendar 108 | } 109 | 110 | extension CharacterSet: _CFConvertible { 111 | public typealias _CFType = CFCharacterSet 112 | } 113 | 114 | extension Data: _CFConvertible { 115 | public typealias _CFType = CFData 116 | } 117 | 118 | extension Date: _CFConvertible { 119 | public typealias _CFType = CFDate 120 | } 121 | 122 | extension Locale: _CFConvertible { 123 | public typealias _CFType = CFLocale 124 | } 125 | 126 | extension String: _CFConvertible { 127 | public typealias _CFType = CFString 128 | } 129 | 130 | #if canImport(Darwin) 131 | extension TimeZone: _CFConvertible { 132 | public typealias _CFType = CFTimeZone 133 | } 134 | #endif 135 | 136 | extension URL: _CFConvertible { 137 | public typealias _CFType = CFURL 138 | } 139 | 140 | // MARK: CFNumber 141 | 142 | extension FixedWidthInteger where Self: _CFConvertible { 143 | public typealias _CFType = CFNumber 144 | } 145 | 146 | extension FloatingPoint where Self: _CFConvertible { 147 | public typealias _CFType = CFNumber 148 | } 149 | 150 | extension Int: _CFConvertible {} 151 | extension Int8: _CFConvertible {} 152 | extension Int16: _CFConvertible {} 153 | extension Int32: _CFConvertible {} 154 | extension Int64: _CFConvertible {} 155 | extension UInt: _CFConvertible {} 156 | extension UInt8: _CFConvertible {} 157 | extension UInt16: _CFConvertible {} 158 | extension UInt32: _CFConvertible {} 159 | extension UInt64: _CFConvertible {} 160 | 161 | extension Float32: _CFConvertible {} 162 | extension Float64: _CFConvertible {} 163 | 164 | // MARK: - Helper 165 | 166 | private func _bridgeToCFIfNeeded(_ v: T) -> Any { 167 | if let bridgeable = v as? __CFConvertible { 168 | return bridgeable.__bridgeToCF() 169 | } else { 170 | return v 171 | } 172 | } 173 | 174 | private func _bridgeFromCFIfNeeded(_ v: Any) -> T { 175 | if let t = T.self as? __CFConvertible.Type { 176 | return t.__bridgeFromCF(v as CFTypeRef) as! T 177 | } else { 178 | return v as! T 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /Sources/SwiftCF/Cast/_CFTollFreeBridgeable.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import CoreFoundation 3 | 4 | // MARK: CFTollFreeBridgeable 5 | 6 | public protocol _CFTollFreeBridgeable: CFType { 7 | associatedtype BridgedNSType where BridgedNSType: NSObject 8 | } 9 | 10 | public extension _CFTollFreeBridgeable { 11 | 12 | @inlinable 13 | static func _bridgeFromNS(_ source: BridgedNSType) -> Self { 14 | return unsafeDowncast(source, to: Self.self) 15 | } 16 | 17 | @inlinable 18 | func _bridgeToNS() -> BridgedNSType { 19 | return unsafeDowncast(self, to: BridgedNSType.self) 20 | } 21 | } 22 | 23 | // MARK: - Conformance 24 | 25 | extension CFArray: _CFTollFreeBridgeable { 26 | public typealias BridgedNSType = NSArray 27 | } 28 | 29 | extension CFAttributedString: _CFTollFreeBridgeable { 30 | public typealias BridgedNSType = NSAttributedString 31 | } 32 | 33 | extension CFBoolean: _CFTollFreeBridgeable { 34 | public typealias BridgedNSType = NSNumber 35 | } 36 | 37 | extension CFBundle: _CFTollFreeBridgeable { 38 | public typealias BridgedNSType = Bundle 39 | } 40 | 41 | extension CFCalendar: _CFTollFreeBridgeable { 42 | public typealias BridgedNSType = NSCalendar 43 | } 44 | 45 | extension CFCharacterSet: _CFTollFreeBridgeable { 46 | public typealias BridgedNSType = NSCharacterSet 47 | } 48 | 49 | extension CFData: _CFTollFreeBridgeable { 50 | public typealias BridgedNSType = NSData 51 | } 52 | 53 | extension CFDate: _CFTollFreeBridgeable { 54 | public typealias BridgedNSType = NSDate 55 | } 56 | 57 | extension CFDateFormatter: _CFTollFreeBridgeable { 58 | public typealias BridgedNSType = DateFormatter 59 | } 60 | 61 | extension CFDictionary: _CFTollFreeBridgeable { 62 | public typealias BridgedNSType = NSDictionary 63 | } 64 | 65 | extension CFError: _CFTollFreeBridgeable { 66 | public typealias BridgedNSType = NSError 67 | } 68 | 69 | extension CFLocale: _CFTollFreeBridgeable { 70 | public typealias BridgedNSType = NSLocale 71 | } 72 | 73 | extension CFMutableArray { 74 | public typealias BridgedNSType = NSMutableArray 75 | } 76 | 77 | extension CFMutableAttributedString { 78 | public typealias BridgedNSType = NSMutableAttributedString 79 | } 80 | 81 | extension CFMutableCharacterSet { 82 | public typealias BridgedNSType = NSMutableCharacterSet 83 | } 84 | 85 | extension CFMutableData { 86 | public typealias BridgedNSType = NSMutableData 87 | } 88 | 89 | extension CFMutableDictionary { 90 | public typealias BridgedNSType = NSMutableDictionary 91 | } 92 | 93 | extension CFMutableSet { 94 | public typealias BridgedNSType = NSMutableSet 95 | } 96 | 97 | extension CFMutableString { 98 | public typealias BridgedNSType = NSMutableString 99 | } 100 | 101 | extension CFNull: _CFTollFreeBridgeable { 102 | public typealias BridgedNSType = NSNull 103 | } 104 | 105 | extension CFNumber: _CFTollFreeBridgeable { 106 | public typealias BridgedNSType = NSNumber 107 | } 108 | 109 | extension CFReadStream: _CFTollFreeBridgeable { 110 | public typealias BridgedNSType = InputStream 111 | } 112 | 113 | extension CFRunLoopTimer: _CFTollFreeBridgeable { 114 | public typealias BridgedNSType = Timer 115 | } 116 | 117 | extension CFSet: _CFTollFreeBridgeable { 118 | public typealias BridgedNSType = NSSet 119 | } 120 | 121 | extension CFString: _CFTollFreeBridgeable { 122 | public typealias BridgedNSType = NSString 123 | } 124 | 125 | extension CFTimeZone: _CFTollFreeBridgeable { 126 | public typealias BridgedNSType = NSTimeZone 127 | } 128 | 129 | extension CFURL: _CFTollFreeBridgeable { 130 | public typealias BridgedNSType = NSURL 131 | } 132 | 133 | // Not Toll Free Bridged 134 | // extension CFUUID: CFType {} 135 | 136 | extension CFWriteStream: _CFTollFreeBridgeable { 137 | public typealias BridgedNSType = OutputStream 138 | } 139 | -------------------------------------------------------------------------------- /Sources/SwiftCF/Mach/KernelError.swift: -------------------------------------------------------------------------------- 1 | #if canImport(Darwin) 2 | 3 | import Darwin.Mach 4 | 5 | extension kern_return_t { 6 | 7 | @usableFromInline func throwIfKernelError() throws { 8 | if let err = KernelError(rawValue: self) { 9 | throw err 10 | } 11 | } 12 | } 13 | 14 | public struct KernelError: Error, RawRepresentable, Equatable, Hashable { 15 | 16 | public static let errorDomain = "ddddxxx.SwiftCF.KernelError" 17 | 18 | public var rawValue: kern_return_t 19 | 20 | public init?(rawValue: kern_return_t) { 21 | guard rawValue != KERN_SUCCESS else { 22 | return nil 23 | } 24 | self.rawValue = rawValue 25 | } 26 | 27 | public var errorDescription: String? { 28 | guard let cstr = mach_error_string(rawValue) else { 29 | return "Unknown kernel error \(rawValue)" 30 | } 31 | return String(cString: cstr) 32 | } 33 | } 34 | 35 | public extension KernelError { 36 | /// Specified address is not currently valid. 37 | static let invalidAddress = KernelError(rawValue: KERN_INVALID_ADDRESS)! 38 | /// Specified memory is valid, but does not permit the required forms of 39 | /// access. 40 | static let protectionFailure = KernelError(rawValue: KERN_PROTECTION_FAILURE)! 41 | /// The address range specified is already in use, or no address range of 42 | /// the size specified could be found. 43 | static let noSpace = KernelError(rawValue: KERN_NO_SPACE)! 44 | /// The function requested was not applicable to this type of argument, or 45 | /// an argument is invalid 46 | static let invalidArgument = KernelError(rawValue: KERN_INVALID_ARGUMENT)! 47 | /// The function could not be performed. A catch-all. 48 | static let failure = KernelError(rawValue: KERN_FAILURE)! 49 | /// A system resource could not be allocated to fulfill this request. This 50 | /// failure may not be permanent. 51 | static let resourceShortage = KernelError(rawValue: KERN_RESOURCE_SHORTAGE)! 52 | /// The task in question does not hold receive rights for the port argument. 53 | static let notReceiver = KernelError(rawValue: KERN_NOT_RECEIVER)! 54 | /// Bogus access restriction. 55 | static let noAccess = KernelError(rawValue: KERN_NO_ACCESS)! 56 | /// During a page fault, the target address refers to a memory object that 57 | /// has been destroyed. This failure is permanent. 58 | static let memoryFailure = KernelError(rawValue: KERN_MEMORY_FAILURE)! 59 | /// During a page fault, the memory object indicated that the data could not 60 | /// be returned. This failure may be temporary; future attempts to access 61 | /// this same data may succeed, as defined by the memory object. 62 | static let memoryError = KernelError(rawValue: KERN_MEMORY_ERROR)! 63 | /// The receive right is already a member of the portset. 64 | static let alreadyInSet = KernelError(rawValue: KERN_ALREADY_IN_SET)! 65 | /// The receive right is not a member of a port set. 66 | static let notInSet = KernelError(rawValue: KERN_NOT_IN_SET)! 67 | /// The name already denotes a right in the task. 68 | static let nameExists = KernelError(rawValue: KERN_NAME_EXISTS)! 69 | /// The operation was aborted. Ipc code will catch this and reflect it as a 70 | /// message error. 71 | static let aborted = KernelError(rawValue: KERN_ABORTED)! 72 | /// The name doesn't denote a right in the task. 73 | static let invalidName = KernelError(rawValue: KERN_INVALID_NAME)! 74 | /// Target task isn't an active task. 75 | static let invalidTask = KernelError(rawValue: KERN_INVALID_TASK)! 76 | /// The name denotes a right, but not an appropriate right. 77 | static let invalidRight = KernelError(rawValue: KERN_INVALID_RIGHT)! 78 | /// A blatant range error. 79 | static let invalidValue = KernelError(rawValue: KERN_INVALID_VALUE)! 80 | /// Operation would overflow limit on user-references. 81 | static let urefsOverflow = KernelError(rawValue: KERN_UREFS_OVERFLOW)! 82 | /// The supplied (port) capability is improper. 83 | static let invalidCapability = KernelError(rawValue: KERN_INVALID_CAPABILITY)! 84 | /// The task already has send or receive rights for the port under another 85 | /// name. 86 | static let rightExists = KernelError(rawValue: KERN_RIGHT_EXISTS)! 87 | /// Target host isn't actually a host. 88 | static let invalidHost = KernelError(rawValue: KERN_INVALID_HOST)! 89 | /// An attempt was made to supply "precious" data for memory that is already 90 | /// present in a memory object. 91 | static let memoryPresent = KernelError(rawValue: KERN_MEMORY_PRESENT)! 92 | /// A page was requested of a memory manager via memoryObjectDataRequest for 93 | /// an object using a MEMORY_OBJECT_COPY_CALL strategy, with the 94 | /// VM_PROT_WANTS_COPY flag being used to specify that the page desired is 95 | /// for a copy of the object, and the memory manager has detected the page 96 | /// was pushed into a copy of the object while the kernel was walking the 97 | /// shadow chain from the copy to the object. This error code is delivered 98 | /// via memoryObjectDataError and is handled by the kernel (it forces the 99 | /// kernel to restart the fault). It will not be seen by users. 100 | static let memoryDataMoved = KernelError(rawValue: KERN_MEMORY_DATA_MOVED)! 101 | /// A strategic copy was attempted of an object upon which a quicker copy is 102 | /// now possible. The caller should retry the copy using vmObjectCopyQuickly. 103 | /// This error code is seen only by the kernel. 104 | static let memoryRestartCopy = KernelError(rawValue: KERN_MEMORY_RESTART_COPY)! 105 | /// An argument applied to assert processor set privilege was not a 106 | /// processor set control port. 107 | static let invalidProcessorSet = KernelError(rawValue: KERN_INVALID_PROCESSOR_SET)! 108 | /// The specified scheduling attributes exceed the thread's limits. 109 | static let policyLimit = KernelError(rawValue: KERN_POLICY_LIMIT)! 110 | /// The specified scheduling policy is not currently enabled for the 111 | /// processor set. 112 | static let invalidPolicy = KernelError(rawValue: KERN_INVALID_POLICY)! 113 | /// The external memory manager failed to initialize the memory object. 114 | static let invalidObject = KernelError(rawValue: KERN_INVALID_OBJECT)! 115 | /// A thread is attempting to wait for an event for which there is already a 116 | /// waiting thread. 117 | static let alreadyWaiting = KernelError(rawValue: KERN_ALREADY_WAITING)! 118 | /// An attempt was made to destroy the default processor set. 119 | static let defaultSet = KernelError(rawValue: KERN_DEFAULT_SET)! 120 | /// An attempt was made to fetch an exception port that is protected, or to 121 | /// abort a thread while processing a protected exception. 122 | static let exceptionProtected = KernelError(rawValue: KERN_EXCEPTION_PROTECTED)! 123 | /// A ledger was required but not supplied. 124 | static let invalidLedger = KernelError(rawValue: KERN_INVALID_LEDGER)! 125 | /// The port was not a memory cache control port. 126 | static let invalidMemoryControl = KernelError(rawValue: KERN_INVALID_MEMORY_CONTROL)! 127 | /// An argument supplied to assert security privilege was not a host 128 | /// security port. 129 | static let invalidSecurity = KernelError(rawValue: KERN_INVALID_SECURITY)! 130 | /// threadDepressAbort was called on a thread which was not currently 131 | /// depressed. 132 | static let notDepressed = KernelError(rawValue: KERN_NOT_DEPRESSED)! 133 | /// Object has been terminated and is no longer available 134 | static let terminated = KernelError(rawValue: KERN_TERMINATED)! 135 | /// Lock set has been destroyed and is no longer available. 136 | static let lockSetDestroyed = KernelError(rawValue: KERN_LOCK_SET_DESTROYED)! 137 | /// The thread holding the lock terminated before releasing the lock 138 | static let lockUnstable = KernelError(rawValue: KERN_LOCK_UNSTABLE)! 139 | /// The lock is already owned by another thread 140 | static let lockOwned = KernelError(rawValue: KERN_LOCK_OWNED)! 141 | /// The lock is already owned by the calling thread 142 | static let lockOwnedSelf = KernelError(rawValue: KERN_LOCK_OWNED_SELF)! 143 | /// Semaphore has been destroyed and is no longer available. 144 | static let semaphoreDestroyed = KernelError(rawValue: KERN_SEMAPHORE_DESTROYED)! 145 | /// Return from RPC indicating the target server was terminated before it 146 | /// successfully replied 147 | static let rpcServerTerminated = KernelError(rawValue: KERN_RPC_SERVER_TERMINATED)! 148 | /// Terminate an orphaned activation. 149 | static let rpcTerminateOrphan = KernelError(rawValue: KERN_RPC_TERMINATE_ORPHAN)! 150 | /// Allow an orphaned activation to continue executing. 151 | static let rpcContinueOrphan = KernelError(rawValue: KERN_RPC_CONTINUE_ORPHAN)! 152 | /// Empty thread activation (No thread linked to it) 153 | static let notSupported = KernelError(rawValue: KERN_NOT_SUPPORTED)! 154 | /// Remote node down or inaccessible. 155 | static let nodeDown = KernelError(rawValue: KERN_NODE_DOWN)! 156 | /// A signalled thread was not actually waiting. 157 | static let notWaiting = KernelError(rawValue: KERN_NOT_WAITING)! 158 | /// Some thread-oriented operation (semaphoreWait) timed out 159 | static let operationTimedOut = KernelError(rawValue: KERN_OPERATION_TIMED_OUT)! 160 | /// During a page fault, indicates that the page was rejected as a result of 161 | /// a signature check. 162 | static let codesignError = KernelError(rawValue: KERN_CODESIGN_ERROR)! 163 | /// The requested property cannot be changed at this time. 164 | static let policyStatic = KernelError(rawValue: KERN_POLICY_STATIC)! 165 | /// The provided buffer is of insufficient size for the requested data. 166 | static let insufficientBufferSize = KernelError(rawValue: KERN_INSUFFICIENT_BUFFER_SIZE)! 167 | #if compiler(>=5.3) 168 | /// Denied by security policy 169 | static let denied = KernelError(rawValue: KERN_DENIED)! 170 | #endif 171 | } 172 | 173 | import Foundation 174 | 175 | extension KernelError: CustomNSError {} 176 | 177 | extension KernelError: LocalizedError {} 178 | 179 | #endif 180 | -------------------------------------------------------------------------------- /Sources/SwiftCF/Utilities.swift: -------------------------------------------------------------------------------- 1 | import CoreFoundation 2 | 3 | extension UnsafeRawPointer { 4 | 5 | @inlinable 6 | func asCF() -> CFTypeRef { 7 | return Unmanaged.fromOpaque(self).takeUnretainedValue() 8 | } 9 | 10 | @inlinable 11 | static func fromCF(_ v: CFTypeRef) -> UnsafeRawPointer { 12 | return UnsafeRawPointer(Unmanaged.passUnretained(v).toOpaque()) 13 | } 14 | } 15 | 16 | public let pCFTypeArrayCallBacks = withUnsafePointer(to: kCFTypeArrayCallBacks) { return $0 } 17 | public let pCFTypeDictionaryKeyCallBacks = withUnsafePointer(to: kCFTypeDictionaryKeyCallBacks) { return $0 } 18 | public let pCFTypeDictionaryValueCallBacks = withUnsafePointer(to: kCFTypeDictionaryValueCallBacks) { return $0 } 19 | 20 | #if swift(<5.1) 21 | 22 | extension Array { 23 | 24 | @inlinable 25 | init(unsafeUninitializedCapacity: Int, initializingWith initializer: (inout UnsafeMutableBufferPointer, inout Int) throws -> Void) rethrows { 26 | try self.init(_unsafeUninitializedCapacity: unsafeUninitializedCapacity, initializingWith: initializer) 27 | } 28 | } 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | fatalError("Run the tests with `swift test --enable-test-discovery`.") 2 | -------------------------------------------------------------------------------- /Tests/SwiftCFTests/SwiftCFTests.swift: -------------------------------------------------------------------------------- 1 | import CoreFoundation 2 | import XCTest 3 | import SwiftCF 4 | 5 | final class SwiftCFTests: XCTestCase { 6 | 7 | func testBridgeContainer() { 8 | let key = CFError.UserInfoKey.description 9 | let cfdict = [key: 42]._bridgeToCF() 10 | let bridged = cfdict.value(key: key.rawValue)! 11 | XCTAssertNotNil(CFNumber.cast(bridged)) 12 | } 13 | 14 | func testBridgeNestedContainer() throws { 15 | let key = CFError.UserInfoKey.description 16 | let cfarray = [[key: 42]]._bridgeToCF() 17 | let cfdict = CFDictionary.cast(cfarray.value(at: 0))! 18 | let bridged = cfdict.value(key: key.rawValue)! 19 | XCTAssertNotNil(CFNumber.cast(bridged)) 20 | } 21 | 22 | func testCFArrayCallBacks() { 23 | let arr = CFMutableArray.create() 24 | weak var weakObj: AnyObject? 25 | do { 26 | let data = CFData.create(bytes: nil, length: 0) 27 | arr.append(data) 28 | weakObj = data 29 | } 30 | XCTAssertNotNil(weakObj) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Tests/SwiftCFTests/TollFreeBridgingTests.swift: -------------------------------------------------------------------------------- 1 | import CoreFoundation 2 | import XCTest 3 | import SwiftCF 4 | 5 | final class TollFreeBridgingTests: XCTestCase { 6 | 7 | func testCast() { 8 | let num = CFNumber._bridgeFromNS(NSNumber(value: 42)) 9 | XCTAssertNotNil(CFNumber.cast(num as Any)) 10 | XCTAssertNil(CFString.cast(num as Any)) 11 | } 12 | 13 | func testCastMutable() { 14 | let str = CFString._bridgeFromNS(NSString(string: "foo")) 15 | let mutable = str.mutableCopy() 16 | XCTAssertNotNil(CFString.cast(str as Any)) 17 | XCTAssertNotNil(CFString.cast(mutable as Any)) 18 | XCTAssertNotNil(CFMutableString.cast(mutable as Any)) 19 | // FIXME: cast mutable bridgeable type 20 | // XCTAssertNil(cfCast(str as Any, to: CFMutableString.self)) 21 | } 22 | } 23 | --------------------------------------------------------------------------------