PMCreateSession not creating print session - swift

Trying to get printable rect for OS X app. Seems to involve creating a session, then a page format, validating the format, etc. Code compiles, but getting a status of -50 from PMCreateSession. Am I declaring printSession improperly? Normally don't have to deal so much with UnsafeMutablePointers.
Thanks!
let printSession: UnsafeMutablePointer<PMPrintSession> = nil
let pmPageFormat: UnsafeMutablePointer<PMPageFormat> = nil
var status = PMCreateSession(printSession)
status = PMCreatePageFormat(pmPageFormat)
status = PMSessionDefaultPageFormat(printSession.memory, pmPageFormat.memory)
let changed: UnsafeMutablePointer<DarwinBoolean> = nil
status = PMSessionValidatePageFormat(printSession.memory, pmPageFormat.memory, changed)
changed.destroy()
var pRect = PMRect()
status = PMGetAdjustedPageRect(pmPageFormat.memory, &pRect)
Swift.print("pRect \(pRect)")
status = PMRelease(pmPageFormat)
status = PMRelease(printSession)

Am I declaring printSession improperly?
One could acquire a PMPrintSession as follows:
// create a C Null pointer of type PMPrintSession
let printSession = unsafeBitCast(0, to: PMPrintSession.self)
// pass by & converts PMPrintSession to UnsafeMutablePointer<PMPrintSession>
PMCreateSession(&printSession)
…
// recast printSession to release memory
PMRelease( PMObject(printSession) )
Alternately, a PMPrintSession can be accessed from the Cocoa NSPrintInfo:
let printInfo = NSPrintInfo.shared()
let printSession = PMPrintSession(printInfo.pmPrintSession())
Normally don't have to deal so much with UnsafeMutablePointers
For information more information on using an UnsafeMutablePointer see StackOverflow "How to use UnsafeMutablePointer in Swift?"
For a complete example of using Core Printing with Swift see 004.42Apple_CorePrintingExample on GitHub.

Related

Unwrapping issue with AXUIElementCopyAttributeValue in Mac OS

I'm trying to copy some information regarding an accessibility window option. Unfortunately, I can't resolve an error that's caused by the AXUIElementCopyAttributeValue method, despite passing in what appears to be all the correct types as parameters.
Code:
for entry in windowList! as Array {
let ownerName: String = entry.object(forKey: kCGWindowName) as? String ?? "N/A"
let ownerPID: Int = entry.object(forKey: kCGWindowOwnerPID) as? Int ?? 0
let pid = Int32(ownerPID)
//3. Get AXUIElement using PID
let windowAccessibilityElem : AXUIElement = AXUIElementCreateApplication(pid)
print(windowAccessibilityElem)
var position : CFTypeRef? = nil
/****
* This line throws the error
****/
let res : AXError = AXUIElementCopyAttributeValue(windowAccessibilityElem, kAXPositionAttribute as CFString, position as! UnsafeMutablePointer<CFTypeRef?>)
print("res is: \(res)")
...
I'm new to Swift, yet I've read and re-read the documentation on optionals and it really isn't apparent what unexpected value is being passed in- I think it has to do with the position variable, but from what I see I should be passing in the reference correctly. Any help would be apprediated.
You have to assign the pointer to the variable with the in-out operator &
var position : CFTypeRef?
let res : AXError = AXUIElementCopyAttributeValue(windowAccessibilityElem,
kAXPositionAttribute as CFString,
&position)
res contains the error on failure.
position contains the position on success.
In the documentation an in-out parameter is indicated by On return, ...

MDQueryGetResultAtIndex and UnsafeRawPointer in Swift 3

I'm having trouble exploring the results of a Spotlight search in Swift 3 using MDQuery. I expect MDQueryGetResultAtIndex to yield an MDItem, and in C/Objective-C, that assumption works, and I can call MDItemCopyAttribute on it to explore the item. Here, for example, I successfully get the pathname of a found item:
MDItemRef item = (MDItemRef)MDQueryGetResultAtIndex(q,i);
CFStringRef path = MDItemCopyAttribute(item,kMDItemPath);
But in Swift 3, MDQueryGetResultAtIndex returns an UnsafeRawPointer! (it's a pointer-to-void in C). To get past that, I've tried, for example:
if let item = MDQueryGetResultAtIndex(q, 0) {
let ptr = item.bindMemory(to: MDItem.self, capacity: 1)
let path = MDItemCopyAttribute(ptr.pointee, kMDItemPath)
}
but that crashes, and logging shows that ptr.pointee is an NSAtom. It is quite evident that my personal UnsafeRawPointer mojo is not working (and to be frank I've always found this confusing).
How would I transform this UnsafeRawPointer into something I can successfully call MDItemCopyAttribute on?
Alternatives
I can get over this hump by putting my Objective-C code into an Objective-C helper object and calling it from Swift; but I'd like to write a pure Swift solution.
Similarly, I could probably rewrite my code to use the higher-level NSMetadataQuery, and I may well do so; but my original Objective-C code using the lower-level MDQueryRef works fine, so now I'm curious how to turn it directly into Swift.
Complete code for those who would like to try this at home:
let s = "kMDItemDisplayName == \"test\"" // you probably have one somewhere
let q = MDQueryCreate(nil, s as CFString, nil, nil)
MDQueryExecute(q, CFOptionFlags(kMDQuerySynchronous.rawValue))
let ct = MDQueryGetResultCount(q)
if ct > 0 {
if let item = MDQueryGetResultAtIndex(q, 0) {
// ...
}
}
The problem in your code
if let item = MDQueryGetResultAtIndex(q, 0) {
let ptr = item.bindMemory(to: MDItem.self, capacity: 1)
let path = MDItemCopyAttribute(ptr.pointee, kMDItemPath)
}
is that the UnsafeRawPointer is interpreted as a pointer to an
MDItem reference and then dereferenced in ptr.pointee, but
the raw pointer is the MDItem reference, so it is dereferenced
once too often.
The "shortest" method to convert the raw pointer to an MDItem reference
is unsafeBitCast:
let item = unsafeBitCast(rawPtr, to: MDItem.self)
which is the direct analogue of an (Objective-)C cast.
You can also use the Unmanaged methods
to convert the raw pointer to an unmanaged reference and from there
to a managed reference (compare How to cast self to UnsafeMutablePointer<Void> type in swift):
let item = Unmanaged<MDItem>.fromOpaque(rawPtr).takeUnretainedValue()
This looks a bit more complicated but perhaps expresses the intention
more clearly. The latter approach works also with (+1) retained
references (using takeRetainedValue()).
Self-contained example:
import CoreServices
let queryString = "kMDItemContentType = com.apple.application-bundle"
if let query = MDQueryCreate(kCFAllocatorDefault, queryString as CFString, nil, nil) {
MDQueryExecute(query, CFOptionFlags(kMDQuerySynchronous.rawValue))
for i in 0..<MDQueryGetResultCount(query) {
if let rawPtr = MDQueryGetResultAtIndex(query, i) {
let item = Unmanaged<MDItem>.fromOpaque(rawPtr).takeUnretainedValue()
if let path = MDItemCopyAttribute(item, kMDItemPath) as? String {
print(path)
}
}
}
}

Can you send objects other than strings in URLQueryItems?

Ok, I am building an iMessage app and to transfer data back and forth I have to use URLQueryItems. I am working with an SKScene and need to transfer Ints, CGPoints, images, etc. Reading Apple's documentation and my own attempts it seems like you can only store strings in URLQueryItems.
As this us the only way to pass data back and forth, is there a (better) way to store other types of data? Currently I have been doing this:
func composeMessage(theScene: GameScene) {
let conversation = activeConversation
let session = conversation?.selectedMessage?.session ?? MSSession()
let layout = MSMessageTemplateLayout()
layout.caption = "Hello world!"
let message = MSMessage(session: session)
message.layout = layout
message.summaryText = "Sent Hello World message"
var components = URLComponents()
let queryItem = URLQueryItem(name: "score",value: theScene.score.description)
components.queryItems = [queryItem] //array of queryitems
message.url = components.url!
print("SENT:",message.url?.query)
conversation?.insert(message, completionHandler: nil)
}
Then on the flip side I have to convert this string back to an Int again. Doing this with CGPoints will be inefficient.. how would one pass something like a CGPoint in a URLQueryItem? Any other way than storing the x and y values as strings?
EDIT: This is how I have been receiving data from the other person and putting into their scene:
override func willBecomeActive(with conversation: MSConversation) {
// Called when the extension is about to move from the inactive to active state.
// This will happen when the extension is about to present UI.
// Use this method to configure the extension and restore previously stored state.
let val = conversation.selectedMessage?.url?.query?.description
print("GOT IT ", val)
if(val != nil)
{
scene.testTxt = val!
}
}
As you discovered, to pass data via URLQueryItem, you do have to convert everything to Strings since the information is supposed to be represented as a URL after all :) For CGPoint information, you can break the x and y values apart and send them as two separate Ints converted to String. Or, you can send it as a single String value in the form of "10,5" where 10 is the x and 5 is the y value but at the other end you would need to split the value on a comma first and then convert the resulting values back to Ints, something like this (at the other end):
let arr = cgPointValue.components(separatedBy:",")
let x = Int(arr[0])
let y = Int(arr[1])
For other types of data, you'd have to follow a similar tactic where you convert the values to String in some fashion. For images, if you have the image in your resources, you should be able to get away with passing just the name or an identifying number. For external images, a URL (or part of one if the images all come from the same server) should work. Otherwise, you might have to look at base64 encoding the image data or something if you use URLQueryItem but if you come to that point, you might want to look at what you are trying to achieve and if perhaps there is a better way to do it since large images could result in a lot of data being sent and I'm not sure if iMessage apps even support that. So you might want to look into limitations in the iMessage app data passing as well.
Hope this helps :)
You can use iMessageDataKit library for storing key-value pairs in your MSMessage objects. It makes setting and getting data really easy and straightforward like:
let message: MSMessage = MSMessage()
message.md.set(value: 7, forKey: "moveCount")
message.md.set(value: "john", forKey: "username")
message.md.set(values: [15.2, 70.1], forKey: "startPoint")
message.md.set(values: [20, 20], forKey: "boxSize")
if let moveCount = message.md.integer(forKey: "moveCount") {
print(moveCount)
}
if let username = message.md.string(forKey: "username") {
print(username)
}
if let startPoint = message.md.values(forKey: "startPoint") {
print("x: \(startPoint[0])")
print("y: \(startPoint[1])")
}
if let boxSize = message.md.values(forKey: "boxSize") {
let size = CGSize(width: CGFloat(boxSize[0] as? Float ?? 0),
height: CGFloat(boxSize[1] as? Float ?? 0))
print("box size: \(size)")
}
(Disclaimer: I'm the author of iMessageDataKit)

Get the name of a connected screen Swift

Does anyone know of a way to get the screen name, or model name/number from a display that is connected to the system? I've been looking around for quite some time to see if there is a way to do this. The only method I've seen anyone post only works with a deprecated API (CGDisplayIOServicePort), (and there's not replacement listed for that API), so that isn't really an option.
Basically, I am wanting to give the user a list of connected screens to display the output of the app, and I feel like giving them a list of names of the displays would be much more elegant and nicer than whatever the ID is that is returned from NSScreen or CGGetActiveDisplayList, etc. It has to possible, when you go to the display preferences in OS X it gives you the names of the displays there. Anyone have any ideas?
macOS 10.15 Catalina introduced a new property localizedName for getting the external display name:
NSScreen.screens.forEach {
print($0.localizedName)
}
You can get the names of connected screens directly from IOReg
func screenNames() -> [String] {
var names = [String]()
var object : io_object_t
var serialPortIterator = io_iterator_t()
let matching = IOServiceMatching("IODisplayConnect")
let kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault,
matching,
&serialPortIterator)
if KERN_SUCCESS == kernResult && serialPortIterator != 0 {
repeat {
object = IOIteratorNext(serialPortIterator)
let info = IODisplayCreateInfoDictionary(object, UInt32(kIODisplayOnlyPreferredName)).takeRetainedValue() as NSDictionary as! [String:AnyObject]
if let productName = info["DisplayProductName"] as? [String:String],
let firstKey = Array(productName.keys).first {
names.append(productName[firstKey]!)
}
} while object != 0
}
IOObjectRelease(serialPortIterator)
return names
}
let names = screenNames()

How do you create a ConstUnsafePointer in Swift

General question: I have an object which I want to convert to a ConstUnsafePointer. Everything I've tried seems to fail...how do I do it? I was able to do this previously in DP2, but cannot work it out in DP3.
Previously, I had code which looked like this:
var bpData = AUSamplerBankPresetData(bankURL: Unmanaged<CFURL>(_private: soundBankURL), bankMSB: UInt8(kAUSampler_DefaultMelodicBankMSB), bankLSB: UInt8(kAUSampler_DefaultBankLSB), presetID: presetID, reserved: 0)
let bpDataPointer: CConstVoidPointer = &bpData
// set the kAUSamplerProperty_LoadPresetFromBank property
result = AudioUnitSetProperty(samplerAudioUnit,
UInt32(kAUSamplerProperty_LoadPresetFromBank),
UInt32(kAudioUnitScope_Global),
0, bpDataPointer, 8)
However, now CConstVoidPointer is no longer around and has been replaced by ConstUnsafePointer. The AudioUnitSetProperty method looks like this:
func AudioUnitSetProperty(inUnit: AudioUnit, inID: AudioUnitPropertyID, inScope: AudioUnitScope, inElement: AudioUnitElement, inData: ConstUnsafePointer<()>, inDataSize: UInt32) -> OSStatus
But, if I change CConstVoidPointer to ConstUnsafePointer<()> I get the error:
'inout AUSamplerBankPresetData' is not convertible to 'ConstUnsafePointer<()>'
Anyone have any ideas? Thanks.
It doesn't appear necessary to have the intermediate pointer constant. You should be able to do this:
result = AudioUnitSetProperty(
samplerAudioUnit,
UInt32(kAUSamplerProperty_LoadPresetFromBank),
UInt32(kAudioUnitScope_Global),
0,
&bpData,
8)
The func has this definition:
func AudioUnitSetProperty(
inUnit: AudioUnit,
inID: AudioUnitPropertyID,
inScope: AudioUnitScope,
inElement: AudioUnitElement,
inData: ConstUnsafePointer<()>,
inDataSize: UInt32
) -> OSStatus
This line:
inData: ConstUnsafePointer<()>,
Is translated from this C:
const void * inData,
The inData parameter is a constant pointer to anything, which is what ConstUnsafePointer<()> means. I could not try this exact code within a Core Audio app, but I have gotten similar code to work.
In beta 6, it's changed again. Thankfully, this time it looks like the solution is here to stay and doesn't involve any C.
let description = AudioComponentDescription(componentType: OSType(kAudioUnitType_MusicDevice),
componentSubType: OSType(kAudioUnitSubType_Sampler),
componentManufacturer: OSType(kAudioUnitManufacturer_Apple),
componentFlags: 0,
componentFlagsMask: 0)
let instrument = AVAudioUnitSampler(audioComponentDescription: description)
let soundBankPath = NSBundle.mainBundle().pathForResource(fileName, ofType: "sf2")
let soundBankURL = NSURL.fileURLWithPath(soundBankPath!)
var error: NSError? = nil
instrument.loadSoundBankInstrumentAtURL(soundBankURL, program: program, bankMSB: UInt8(kAUSampler_DefaultMelodicBankMSB), bankLSB: UInt8(kAUSampler_DefaultBankLSB), error: &error)
For my sf2 files, the program is usually 1 or 0. I'm not really sure what this refers to, and any help is appreciated. However, this should get you unblocked for your program with a little trial and error.