Comments (14)
This is an interesting issue. I really have doubts Apple has spent a lot of time with this.
In general with OS X/iOS, you are not supposed to work with any *Ref
type directly. These are opaque for that reason. You only use the pointers with functions and methods that accept them. Reading the data directly for any reason is generally pointless and discouraged.
In this case, you get back a TISInputSourceRef
and you should now be calling any functions/methods that accept that data type, like $.TISSelectInputSource()
. There is no casting in JXA. In the code you are pointing to, I see little reason for all the casting I see there, except any casts with __bridge*
for going to Objective-C.
All your code involving CoreFoundation data types has to be treated as if there is no such thing as Objective-C or casting. You have to manually convert CFArray
to NSArray
for example:
var objcArray = NSArray.alloc;
for (var i = 0, len = $.CFArrayGetCount(cfarr); i++) {
objcArr.addObject(
$.CFArrayGetValueAtIndex(cfarr, i) // still not converted to an Objective-C object or JS; probably useless for calls accepting NSArray
);
}
Note that ObjC.unwrap()
will generally do nothing with CF data types. I found that calling $.CFBridgingRetain(cf_something)
can give some clues once you inspect the return value fully. The return value will be an Objective-C type, but it does not seem to convert to the respective type (i.e. CFArray
to NSArray
). Calling $.CFBridgingRelease()
as you would do in Objective-C will cause a segfault.
You probably to have to call $.CFStringCreateExternalRepresentation(null, cfstringref, someEncoding, 0)
(converting to CFData
first) to convert from CFString
to JavaScript String
.
from jxa-cookbook.
That's a really helpful set of insights - many thanks !
from jxa-cookbook.
Figured out how to go back and forth with a CFString
:
var strToStore = 'My String';
var len = strToStore.length + 1; // add 1 for null byte
var cfs = $.CFStringCreateWithBytes(null, strToStore, len, 'UTF-8', false);
// Get back the string as a JS string (manual says char * is converted to JS string)
var cPtr = $.CFStringGetCStringPtr(cfs, 'UTF-8');
console.log(cPtr);
// Another way: call CFStringCreateExternalRepresentation() and CFDataGetBytePtr()
var data = $.CFStringCreateExternalRepresentation(null, cfs, 'UTF-8', 0); // CFDataRef
cPtr = $.CFDataGetBytePtr(data);
console.log(cPtr);
So you do not have to call CFStringCreateExternalRepresentation()
for CFString. Once you have a JS string in any case, you should not need to re-wrap to make an Objective-C or C call that accepts char *
or NSString *
.
Internally, every time something returns char *
, the interpreter is actually calling +[NSString stringWithUTF8String:]
with that return value. You can see this if you get a NULL pointer back from a C function that returns char *
(you will get an Error
thrown).
Try hard to stay away from CF
stuff :)
from jxa-cookbook.
Ha ! wonderful ... thank you :-)
from jxa-cookbook.
Ah ... my (updated) impression is that we may be looking at CFDictionary objects here,
( $.TISCopyCurrentKeyboardInputSource()
)
before we get to the CFStringsRefs.
(function () {
'use strict';
ObjC.import('CoreServices')
ObjC.import('Carbon');
ObjC.import('CoreFoundation');
// CFObjectRefce -> s
function CFTypeName(objRef) {
return $.CFDataGetBytePtr(
$.CFStringCreateExternalRepresentation(
null,
$.CFCopyTypeIDDescription(
$.CFGetTypeID(
objRef
)
),
'UTF-8',
0
)
);
}
return CFTypeName(
$.TISCopyCurrentKeyboardInputSource()
);
// --> "CFDictionary"
})();
Do you think that CFDictionary keys and values might conceivably be within reach from the JS side ?
from jxa-cookbook.
Try passing the CFDictionaryRef to NSDictionary.dictionaryWithDictionary() to convert it then you can unwrap it. Internal values will still be CF types but the keys will be accessible (for (k in obj)).
Still not sure what to do when it comes to structs and direct access.
from jxa-cookbook.
Thanks – that certainly yields some progress – we do get a tractable dictionary.
There seems, though, at first sight, to be some collapse of the value to a key string of some kind rather than a reference to a CF value holding the input information.
(function () {
'use strict';
ObjC.import('CoreServices')
ObjC.import('Carbon');
ObjC.import('CoreFoundation');
return $.NSDictionary.dictionaryWithDictionary(
$.TISCopyCurrentKeyboardInputSource()
)
})();
// --> $({"type":$("{__TISInputSource=}")})
from jxa-cookbook.
Yes I noticed that. I think you cannot convert this way after all, despite this working in 'real' Objective-C.
What's happening is sort of what would happen in Objective-C though, here. It is confusing though.
TISCopyCurrentKeyboardInputSource()
gives a TISInputSourceRef
opaque pointer return value, which points to a struct __TISInputSource
(and the fields to this are not documented). I think what happens in JXA with structs is they get converted to CFDictionary
, and this can work as long as you keep using the CFDictionary
functions to get values, as long as you know the field names.
I think also this means passing a normal JS literal object to a C function that accepts a struct will work as long as it is a non-pointer? There is no way to do &someVar
in JXA and I doubt this auto-translates. I am also curious how one would call int f(char a)
in JXA. Pass a 0-255 integer?
Seems kind of lame all around (and very untested considering you can get a segfault or bus error even with documented functions (not something I expect from an interpreter)). You might want to consider using Apple's Python and their libraries and/or FFI in Python to fix up everything else for this part. You would be able to correctly call any C function. You can execute the Python code from JXA with $.NSTask
.
from jxa-cookbook.
That's a really helpful insight and suggestion – many thanks for taking time to look at this.
from jxa-cookbook.
By the way I think it isn't possible to call TISGetInputSourceProperty()
and read the string value out. For void *
return value, the conversion to a C string is not automatically made (as this cannot be done a safe manner), and CFString*
functions will not accept a void *
type (the interpreter stops the function call). There is no type casting in JXA so it seems there is no way around this. I think a bug report to Apple is in order.
from jxa-cookbook.
I will do that – your analysis makes a lots of sense, and I think you may have saved me quite a lot of time :-) Thanks !
from jxa-cookbook.
You can coerce a CF type to an NS type by first re-binding the CFMakeCollectable function so that it takes 'void *' and returns 'id', and then using that function to perform the coercion:
ObjC.bindFunction('CFMakeCollectable', [ 'id', [ 'void *' ] ]);
var cfString = $.CFStringCreateWithCString(0, "foo", 0); // => [object Ref]
var nsString = $.CFMakeCollectable(cfString); // => $("foo")
To make this easier to use in your code, you might define a .toNS() function on the Ref prototype:
Ref.prototype.toNS = function () { return $.CFMakeCollectable(this); }
Here is how you would use this new function with the TIS* functions:
ObjC.import('Carbon');
var current_source = $.TISCopyCurrentKeyboardInputSource();
var cfs = $.TISGetInputSourceProperty(current_source, $.kTISPropertyInputSourceID);
cfs.toNS() // => $("com.apple.keylayout.US")
from jxa-cookbook.
Thank you !
from jxa-cookbook.
Closing due to age.
from jxa-cookbook.
Related Issues (20)
- Possible to intercept http link clicks from an app?? HOT 3
- better integrating Objective-C Bridge documentation + proposed expansions HOT 6
- JXA chat network HOT 1
- Problems with Mojave? HOT 20
- Can't import a custom framework HOT 4
- Using IconRef HOT 2
- How can I create a http request with JXA? HOT 5
- How to copy image or file to clipboard? HOT 15
- Integrating with JXA-Snippets example scripts HOT 1
- Object specifiers and Apple Music HOT 4
- How do you get a random number in JXA? HOT 2
- How to make FSEventStream Work HOT 4
- NSInvalidArgumentException: NSGetSizeAndAlignment(): unsupported type encoding spec 'G' at 'GPoint}"size"{CGSize}}' in '{CGPoint}"size"{CGSize}}' HOT 7
- Arrow functions now work in shebang files HOT 2
- Please state a license for use/reuse of the text
- How do I initialize a pointer to a specific C struct? HOT 3
- Mismatched method names in ObjC example
- writeToFileAtomicallyEncodingError has "getFileSystemRepresentation:maxLength"
- Add Missing Event Function `GURLGURL` (JXA-equivalent of `open location`)
- Library impoting
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from jxa-cookbook.