How to implement Kineme Plugins when building custom .app

franz's picture

Hi, I'm currently compiling my graphs into one autonomous application -non commercial- using apple's template (QCPlayer actually). Is it possible to bundle the "kineme".plugin that i use into the .app so that it can be portable to other computers... and so that my app runs out of the box ... ? Thxx

cwright's picture
Bundling

I'm guessing you can store the plugins in MyApp.app/Contents/Resources, and then when the program runs, it can check for /Library/Graphics/Patches (-- By The Way, does ~/Library/Graphics/Patches work in Quartz Composer? If so, this might be the preferred way for distributed stuff --), and copy the files out of its own bundle into the destination.

Hmm... I smell a feature request :)

smokris's picture
Requires installation, but maybe...

Currently you need to install patches to /Library/Graphics/Patches. Unfortunately QC doesn't seem to load patches placed in /Users/foo/Library/Graphics/Patches, so you will need to have administrator privileges to install the patches.

There may be a way to force QC to load a particular patch binary (I remember seeing a function to do this somewhere in the core) for a particular QCView / QCRenderer instance, but that'll require more hacking to figure out.

franz's picture
Complex

Mmm, so you mean QC can't load any plugins that are not placed in /Library/Graphics/Patches , and that even when placing the needed plugs into MyApp/Content/Resources, i would still have to copy them in the system's lib -- thus leading to an authentification pop-up (because of administrator privileges)... This seems very complicated, but obviously because Apple didn't plan to let users build custom plugs. With all the feedback going on QCdevlist, let's hope POL and his fellows will take this fact into account and prepare something nicer for QCLeopard.

to be continued, i guess....

smokris's picture
Leopard = good

I have an Apple Developer membership and have been working with the pre-release version of Leopard --- there is a lot of amazing new stuff in Quartz Composer Leopard. Apple's team has been working hard on it.

In Leopard, you will be able to install custom patches into the /Users/foo/Library directory, or you will be able to include it in your application bundle and register it explicitly with QC, without having to install it to a shared directory. Very cool.

franz's picture
you got me

this is very kewl. I'll then wait for QCleopard..... Including plugins in the app bunble is a TOP feature for me.... and i guess Core Animation will bring good surprises too !!!

cwright's picture
Too little too late

Sorry to figure this out moments before Leopard is released (I don't know if it solves this problem, if it doesn't this might still be of use)

QCPatch implements 3 class methods:

+(void)loadPlugInAtPath:(NSString)path; +(void)loadPlugInsInFolder:(NSString*)path; +(void)loadPlugInsInLibrary:(id)something;

These can be used outside of QC stuff, in application code, and they load plugins that can be anywhere (for example, in an application bundle :)

So, code like this:

[QCPatch loadPlugInsInFolder:@"/Users/cwright/Library/Graphics/Patches2/"];

will load plugins found in ~/Library/Graphics/Patches2/

This solves your problem.

smokris's picture
Or from within bundle

To load a specific plugin from the Resources/ folder within your application bundle, you should be able to do something like this:

[QCPatch loadPlugInAtPath:[Bad link];

cwright's picture
Bundle stuff

Thanks smokris for the Bundle demo.

One Really Important Note before running around using this: Tiger's QCRenderer (actually, its node manager) will throw an exception if a node gets registered multiple times. This happens when you instantiate a QCRenderer (it'll load from the default path). Unfortunately, by the time you're able to catch this exception, it's too late, and you can't handle it gracefully (you can't tell QCRenderer to continue where it left off).

Solving this is pretty easy: Patches need to see if they're already registered before blindly registering themselves. At present (20071020), zero kineme.net patches do this. Expect some updates to resolve this issue Really Soon :) There are methods in place to do this.

Fun Fact: the extra patches don't check this either, so if you load them manually, you'll die when the QCRenderer loads them again.

franz's picture
scan and compare ?

Can't i scan the local patch directory and search for the wiimote patch, if it is not present locally, then it would load from the ressources dir ?

and if i rename the extra patches ?

Note: my app is for 10.5 only !

cwright's picture
Should work

That should work, as long as they haven't renamed the patch bundle to something weird.

I woulnd't worry about Extra Patchs, thats taken care of automatically by QC, and unless the user is doing some really nasty stuff, it shouldn't be duplicated, and should always be loaded. I Only posted that for comparison.

10.5's patch registration is smart enough to not stall on duplicate loads, so you'll be relatively safe if you double-load a plugin.

franz's picture
great news

thanks for the info, i'll try that then.

psonice's picture
Leopard

What happened with the leopard release? Will QC based apps now pick up plugins in the resources folder automatically, or do they still need to be loaded manually?

As a side note, how are paths handled within the app?

I've noticed that if I include music in the resources folder (in the same location as the .qtz file), it will load in QC fine but not in the app if I just use the filename as the path. If I use the full path, it works fine in both. Does the application give the composition it's path then, and if so is it the root of the bundle (so the path becomes "resources/tune.mp3"?

Sorry for all the questions, I'm a little pressed for time to get this done before breakpoint :)

cwright's picture
paths etc.

Leopard has somewhat official support for loading plugins from arbitrary paths (Tiger did not, so I had to do some hacks to pull that off). I'll try to dig up code to do that, if you'd like.

Paths in apps are probably not handled the same as in QC. in QC, you can use "Composition-relative addressing", meaning you can give it a path relative to the composition (as you've noticed) and it'll work. For this to work, each plugin needs to add some "secret-sauce" code to handle paths like that, and the QC runtime needs some variables to be set to tell the plugins how to do it. In a separate application, I would imagine that those variables aren't set (I've not done any testing though, so I could be mistaken).

You're probably best-off using a published input for the resources folder that the app can supply (based on Cocoa bundle stuff). The composition can then append any specific files to that string, and find resources wherever the user happens to put the app.

jean_pierre's picture
yeah, plugin loading is game

yeah, plugin loading is game on Leopard:

QCPlugIn Class Reference +[QCPlugIn loadPlugInAtPath:]

Discussion Call this method only if you need to load a plug-in bundle from a nonstandard location. [...] This method does nothing if the bundle is already loaded. [...]

kimba23's picture
So how difficult?

I have been compiling custom applications following this method:

http://developer.apple.com/documentation/GraphicsImaging/Conceptual/Quar...

I am not a programmer, so how hard would it be to load the kineme plug-ins using this method? Where do I put the QCPlugIn loadPlugInAtPath: ?

Thanks

cwright's picture
QCView = ugly

QCViews are loaded Very Early, and plugins have to be loaded before the QC stuff inits to be able to use the plugins.

Franz had a similar setup a while ago, and the best place I could find was to do the loading in main.m.

You'll need to declare the method, so the compiler won't freak out. Add these lines to main.m, before the "int main" line, and after the "#import Cocoa.h" stuff:

@interface QCPatch
-(void)loadPlugInsInFolder:(NSString*)path;
@end

In a typical Cocoa app, main.m will have a piece kind of like this:

int main(int argc, char *argv[])
{
    return NSApplicationMain(argc,  (const char **) argv);
}

To load stuff, it needs to look something like this:

int main(int argc, char *argv[])
{
   // Creating a pool here is necessary for plugin
   // loading to work without generating tons
   // of leaking messages.  Since we do this here, 
   // we have to do some cleanup after NSApplicationMain
   NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init];
   // this path is where to load plugins from...
   NSString *pluginPath = [[NSBundle mainBundle] builtInPlugInsPath];
   [QCPatch loadPlugInsInFolder: pluginPath];
   int ret = NSApplicationMain(argc,  (const char **) argv);
   [p release];
   return ret;
}

hope that helps. If you need more advice, please e-mail me and I'll see what I can do to get you up and running.

kimba23's picture
Will try

Thanks so much; I'll give it a shot, if I run into probs I'll e-mail you. Thanks again!

psonice's picture
Unable to load plugin from app

I'm using the same method to build an app, but having no luck getting my nice shiny new plugin to load from the bundle. I've tried cwright's method above, and also the one further above that.

It builds fine, and the app works fine, but the plugin isn't loading so I'm missing an effect. It works fine if the plugin is installed in the system plugins folder, so it's definitely the plugin loading code that's failing.

Any ideas on what could be wrong or where to look? And is the path for plugins in the example above just the Resources folder in the app bundle?

cwright's picture
NSLog to the rescue

Are you using official-API plugins, or unofficial ones? (the code above loads unofficial ones only, I believe). To load official-api ones, there's something similar, but slightly different to do (not sure off the top of my head, but I'm sure I could figure it out lickety-split upon request).

If all else fails, use NSLog everywhere -- print the path the bundle stuff returns, print stuff in the plugin when it registers/loads/does anything.... these can help iron out what it's doing (or not doing), for a more focused debug session.

psonice's picture
Official

Ahh. It's an official api plugin, so that's that one :D Some kind of clue on how to load official plugins would be very much appreciated.

And yes, NSLog is invaluable, I used it to check all the paths etc. Unfortunately I got as far as a console message telling me "Arse! Plugin failed to load. :(" and no further with it.

gtoledo3's picture
Re: QCView = ugly

I was loading this in an "awakeFromNib" routine in an appcontroller, with an autorelease pool like this (not sure if that's appropriate actually - it seems to cause a leak, but that "never gets bigger" and is tiny.)

Anyway, I decided to try out the patch loading routine in the main of my app after noticing this.

What I found, is that doing that would cause the menubar to double up on items like "services" and "quit", etc. I don't know if that's always the case, but at least it's what I found. Wanted to mention it here, because it's not like there's a ton of info about this on the web. Might help someone else. Not using a qcview, different scenario, so who knows.