Closed clarkema closed 5 years ago
OOC, how difficult would it be to do the necessary Cocoa calls with NativeCall
?
I have absolutely no idea. As the diff shows we don't need much Cocoa, and it would certainly be nice to have a reusable module in the Perl ecosystem for this rather than having to farm out to Python, but I haven't played with either NativeCall
or Cocoa.
After a bit more experimentation this evening I have a PoC version of CLI tool to set file icons in Swift. This allows for at least some minimal error checking, so might end up being the way forwards.
Note that the Python version will almost certainly support building on older versions of macOS than the Swift version, since the language spec for the latter is still evolving quickly. I don't have any versions of macOS older than 10.14 available right now to test on.
Not that I care about icons, but I own a Mac. Just for curiosity, I came out with a Perl 6 version of your script, although I had to make up a hack to load the AppKit framework and didn't dare to add any sugar to the objC interface, please feel free to take inspiration!
#!/usr/bin/env perl6
use NativeCall;
INIT symlink '/System/Library/Frameworks/AppKit.framework/AppKit', './libAppKit.dylib';
END unlink './libAppKit.dylib';
constant \kCFStringEncodingUTF8 = 0x08000100;
constant \LIB = "AppKit";
sub CFStringCreateWithCString(Pointer, Str, int32 --> Pointer) is native(LIB) { * };
sub NSSelectorFromString(Pointer --> Pointer) is native(LIB) { * };
sub sel_getUid(Str --> Pointer) is native(LIB) { * };
sub objc_getClass(Str --> Pointer) is native(LIB) { * }
sub objc_msgSend0(Pointer, Pointer --> Pointer) is native(LIB) is symbol('objc_msgSend') { * }
sub objc_msgSend1(Pointer, Pointer, Pointer --> Pointer) is native(LIB) is symbol('objc_msgSend') { * }
sub objc_msgSend3(Pointer, Pointer, Pointer, Pointer, Pointer --> Pointer) is native(LIB) is symbol('objc_msgSend') { * }
sub CFSTR(Str $str --> Pointer) {
CFStringCreateWithCString(Pointer, $str, kCFStringEncodingUTF8)
}
sub MAIN(Str $target, Str $icon) {
my $img = objc_getClass("NSImage");
$img = objc_msgSend0($img, NSSelectorFromString(CFSTR("alloc")));
$img = objc_msgSend1($img, NSSelectorFromString(CFSTR("initWithContentsOfFile:")), CFSTR($icon))
or die "Failed to load icon";
my $wrk = objc_getClass("NSWorkspace");
$wrk = objc_msgSend0($wrk, NSSelectorFromString(CFSTR("sharedWorkspace")));
$wrk = objc_msgSend3($wrk, NSSelectorFromString(CFSTR("setIcon:forFile:options:")),
$img, CFSTR($target), Pointer)
or die "Failed to set icon";
}
Thanks @scovit! Really interesting to see how it would look using NativeCall :)
In this instance I think I'm going to go ahead and merge the Swift version, just because it feels a little less magic is involved. It does add a dependency on /usr/bin/swift, but we know that has to be available anyway if we're building using the macOS dev tools.
The only problem with building on more recent versions (this was tested on 10.14) seems to be the deprecation of utilities used for setting the icon on the perl6 executable.
Evidently this is only possible now via Cocoa APIs. The usual solution seems to be using the system Python, which comes with a Cocoa module.