Open JohnLouderback opened 9 years ago
@JohnLouderback
I can take a closer look at this tomorrow, the one weakness of Tint (and all objective-c bridges) is they sometimes cannot pick up constants that are defined as macros in C++ (these are converted to literal numbers during a pre-compile phase).
So, basically, anything in all capital letters won't be defined for example; $.KERN_SUCCESS
will be undefined (although you set KERN_SUCCESS = 0 earlier), also $. MACH_PORT_NULL
will be undefined. You can find out the values by creating a simple C file to print out the value of it (usually as a hexadecimal) or find the .h header in the SDK to see what values it has.
I'll try my luck tomorrow and let you know, but at the first pass all of the functions you're requesting are defined in Tint, you can use a repl to inspect what came through the bridge on process.bridge.objc
or $
as you have in this.
Hi Trevor,
Thanks for the information on the constants. I did notice that with KERN_SUCCESS
, which is why I set it up top. It looks like I forgot to remove the $
, however. I also made a change to initialize MACH_PORT_NULL
at the top too. I think my problem is with passing the variables by reference or value... or at least that's one of my problems. I'm not entirely sure how to create a reference to a variable with no value. Also, I'm concerned about the casting between Objective C and JS. For instance: a variable of type CFMutableDictionaryRef
is initialized with a value of 0
in Objective C. Knowing that 0 is going to be an Number
in JS, I'm concerned that it won't be coerced correctly in Objective C. All that, and then I need reference to this variable. I'm getting relatively proficient in converting C# code to JS, but there are some idiosyncrasies of Objective C that are eluding me when it comes to writing them in JS. I really appreciate your help and insight. Once I understand better how to translate certain concepts, I should be able to make some worth-while contributions.
By the way, here is my updated code:
require('Common');
var $ = process.bridge.objc;
var getIdleTime = function() {
debugger;
var properties = $.alloc(0).ref(),
obj,
masterPort = $.alloc().ref(),
iter = $.alloc().ref(),
curObj,
KERN_SUCCESS = 0,
MACH_PORT_NULL = 0;
$.IOMasterPort(MACH_PORT_NULL, masterPort);
$.IOServiceGetMatchingServices(masterPort, $.IOServiceMatching("IOHIDSystem"), iter);
if (iter == 0) {
return -1;
}
else {
curObj = $.IOIteratorNext(iter);
}
if ($.IORegistryEntryCreateCFProperties(curObj, properties, $.kCFAllocatorDefault, 0) == KERN_SUCCESS && properties != null) {
obj = $.CFDictionaryGetValue(properties, $("HIDIdleTime"));
$.CFRetain(obj);
}
else {
return -1;
}
var tHandle = $.alloc(0).ref();
if (obj) {
var type = $.CFGetTypeID(obj);
if (type == $.CFDataGetTypeID())
{
$.CFDataGetBytes(obj, $.CFRangeMake(0, sizeof(tHandle)), tHandle);
}
else if (type == $.CFNumberGetTypeID())
{
$.CFNumberGetValue(obj, $.kCFNumberSInt64Type, tHandle);
}
else
{
// error
tHandle = 0;
}
$.CFRelease(obj);
tHandle /= 1000000; // return as milliseconds
} else {
tHandle = -1;
}
$.CFRelease(properties);
$.IOObjectRelease(curObj);
$.IOObjectRelease(iter);
return tHandle;
}
setInterval(function() {
console.log(getIdleTime());
}, 1000);
I don't know if it's helpful, but it seems the reason this may not be working is because I'm passing by reference in certain places I should pass by value. It seems that the deref()
method crashes Tint though.
@JohnLouderback You should check the address you're using on the deref(), (just use .address()) if the address is null you'll chase down a null pointer and Tint will crash. Just like in C, dereference a pointer that leads to no where (or to protected memory, when you're only supposed to pass it) and the world will collide.
I'm still playing around with your code sample, I think a good debug exercise might be to print out the address of all the pointers in actual C, then try it in tint and see what happens.
BTW, in C, you can use fprintf(stderr, "%p\n", somepointer); in Tint you'd do console.log(somepointer.address())
Also, dereferencing in C uses *pointer, in tint it would be somepointer.deref(), in C a reference is &pointer, in tint its somepointer.ref(). However passing references in C and tint are the same, somefunc(somepointer);
Hope this helps.
Thanks Trevor, that gives me a lot of insight into how this works. I've spent the better part of today trying to get this to work and learned quite a bit along the way, but no matter what I've done $.IORegistryEntryCreateCFProperties(curObj, properties, $.kCFAllocatorDefault, 0)
never returns the success code of zero. It appears the error suggests that the port number is invalid. I didn't have any success converting masterPort
from a buffer to an integer so I could compare it with the masterPort
value in Objective C. I tried all of the readUInt
methods on the buffer to see if I could get back the correct port number, but unfortunately none of the numbers were the expected value, or even close to it, for that matter. I had great success using CGEventSourceSecondsSinceLastEventType
as a one-liner, but I don't want to use a deprecated API. I've also tried searching this repo for instances where you switched between buffers and their primitive values frequently, but I couldn't find any. If you have any more insight or advice, I'd love to hear it.
@JohnLouderback
I've looked into this and with a few minor tweeks was able to get to the point where calls into ioiteratornext is valid.. however I ran into the same problems you've had referenced above, an invalid iterator object.
On closer inspection it appears were using mach ports through libuv and through the ffi bridge. Which the closest i can get to an answer is it "locks the mach port", e.g., making a mach port call locks the mach port making it sort of impossible to do mach port calls without compiling it down to native code and loading it through a standard node require();
I really don't have a whole lot to add here, but heres the code I validated was correct (at least to line 41).
require('Common');
var $ = process.bridge.objc;
var $$ = process.bridge;
$.import('CoreFoundation');
$.import('IOKit');
var GetIdleTime = function() {
var properties = $.alloc($$.ref.types.void),
obj,
masterPort = $.alloc($$.ref.types.uint32),
iter = $.alloc($$.ref.types.uint32),
curObj,
KERN_SUCCESS = 0,
MACH_PORT_NULL = 0;
console.log($.kIOMasterPortDefault);
var result = $.IOMasterPort(MACH_PORT_NULL, masterPort);
var defreffed = masterPort.deref();
console.log('result: ' + result + ' MACH_PORT_NULL: ',MACH_PORT_NULL,' masterPort.ref: ' , masterPort , ' masterPort: ',masterPort.deref());
var ioSM = $.IOServiceMatching("IOHIDSystem");
console.log('ioSM is: ', ioSM);
result = $.IOServiceGetMatchingServices(masterPort.deref(), ioSM);
console.log('result: ', result);
result = $.IOIteratorIsValid(iter);
console.log('is iterator valid result ' , result);
if (iter.isNull() || result === 0) {
console.log('iterator was null.');
return -1;
}
else {
curObj = $.IOIteratorNext(iter.deref());
}
console.log('curObj is: ',curObj);
var ioreturn = $.IORegistryEntryCreateCFProperties(curObj, properties.ref(), $.kCFAllocatorDefault, 0);
if (ioreturn == KERN_SUCCESS && properties.address() != 0) {
obj = $.CFDictionaryGetValue(properties, $$.ref.allocCString("HIDIdleTime"));
$.CFRetain(obj);
}
else {
return -1;
}
var tHandle = $.alloc(0).ref();
if (obj) {
var type = $.CFGetTypeID(obj);
if (type == $.CFDataGetTypeID())
{
$.CFDataGetBytes(obj, $.CFRangeMake(0, sizeof(tHandle)), tHandle);
}
else if (type == $.CFNumberGetTypeID())
{
$.CFNumberGetValue(obj, $.kCFNumberSInt64Type, tHandle);
}
else
{
// error
tHandle = 0;
}
$.CFRelease(obj);
tHandle /= 1000000; // return as milliseconds
} else {
tHandle = -1;
}
$.CFRelease(properties);
$.IOObjectRelease(curObj);
$.IOObjectRelease(iter);
return tHandle;
}
console.log(GetIdleTime());
@JohnLouderback As a side note, i don't see any notice that Quartz' CGEventSourceSecondsSinceLastEventType is deprecated, theres no notice on apple's docs:
Perhaps you can use that instead? There may also be an objective-c class laying around somewhere that returns the same values.
I'm trying to make a pull request for a feature that can be called to, to determine how long a user has been idle. So far, my attempts to convert this Objective C code to JS has been unsuccessful. I'm hoping to glean some information on how the bridge works. I'm trying to convert:
With the following code:
It always returns
-1
atif ($.IORegistryEntryCreateCFProperties(curObj, properties, $.kCFAllocatorDefault, 0) == $.KERN_SUCCESS && properties != null)
, however.