Differences between undocumented 10.4 QCPatch API and official 10.5 QCPlugIn API

smokris's picture

After comparing the two, I think there is still reason to use the undocumented interface (or at least parts of it).

The official QCPlugIn API has some big limitations (IMO):

  • Not composition-compatible with custom patches on 10.4. Inside the composition plist, the custom patches are stored differently, so even if you create a 10.5 QCPlugIn with the same classname as a 10.4 patch, QC doesn't think it's equivalent to the 10.4 QCPatch. (From a developer's perspective, this is totally justifiable, but I still consider it a problem.)
  • I didn't see any documented way to create new QCPort types (the new API references port types using string constants rather than QCPort inheritance.. Why?!).
  • You can't make Environment patches.. there's no + (BOOL)allowsSubpatches; or equivalent.
  • You can't define your own "Category". It's fixed at "Plug-In".

I think I came across a few other issues I'm not remembering at the moment..

On the other hand, I really like - (BOOL)didValueForInputKeyChange.

cwright's picture
multiPatches

You also apparently can't bundle multiple patches into a single plugin like you can with the undocumented API. This makes packages like the texture plugin or gltools a real pain.

Port types will be a showstopper with some upcoming stuff we've got in the pipe. You also can't easily change the patch's actor (its editor UI representation). This is also justifiable, but the possibilities for this could make for some interesting UI tweaks (we've experimented with this, but not deployed anything that uses this technique yet).

From my experience thus far with QCTiger, the didValueChangeForKey behaviour could probably be pulled from the undocumented API, since I'm thinking the official API is simply a thin layer on top of the undocumented one to keep developers from getting too crazy (like us :) with the internals.

smokris's picture
GFPort didChangeValue!

Woah, - (void)didChangeValue; is part of GFPort in Tiger.

Can't believe I didn't notice that before..

Time to rewrite all the execute methods... :^P

cwright's picture
sort-of...

note the non-bool return type. I think that's from a "willChange/didChange" pair, so it's not exactly the same, but close. :)

smokris's picture
um yeah

sorry, need to actually read before i post.

nevermind about that..

but there is QCPort's - (BOOL)wasUpdated; which could be interesting..

cwright's picture
Subpatches!

There's been periodic discussion on custom subpatches on the QC mailing list. This morning, I decided to take a closer look, and try to see if there's a 1-line-of-code fix. Here's what I've reasoned/figured out.

Official-API plugins are subclasses of "QCPlugIn". These aren't actual QCPatches, but they're owned by a parent QCPlugInPatch, which is.

So effectively, QCPlugInPatch is a thin wrapper over your custom patch (we had suspected this early on). Most functions are implemented to pass-through to the QCPlugIn so your code does cool stuff.

+executionModeWithIdentifier:, for example, passes through to the QCPlugIn's appropriate method, does some sanity checks, and then continues.

+allowSubpatchesWithIdentifier:, however, looks like this (sorry Pierre):

push   %ebp
xor    %eax,%eax
mov    %esp,%ebp
leave  
ret 

For the non-programmers in the audience, this basically means "Always say NO" :)

It should be possible to implement this function in a similar fashion to executionMode... However, one thing we should not do is implement it with a category. So, with some objective-C runtime hackery, we've got a working example in http://kineme.net/QuartzComposerUIHacks/OfficialAPISubpatchSupport/0.1

It basically behaves identically to executeModeWithIdentifier (except that since it's a BOOL return type, there's not need for sanity checks or exception throwing, making it even easier). The final replacement method is something lame like 3 lines of code. The runtime hackery is equally small, at 4 or 5 lines. Fun stuff, but way over my 1-line-of-code hopes :)

cwright's picture
wasUpdated = not interesting

Ok, so I've been experimenting a lot with this - (BOOL)wasUpdated; method, and I am under the impression that it's 100% not interesting. As in, it always returns false.

Explicitly changing the port value doesn't activate it. Calling - (void)_resetUpdate; doesn't either. Calling - (void)willChangeValue;/- (void)didChangeValue; doesn't change it. Using - (void)setStateValue:; doesn't enable it. Manually reevaluating pieces of the graph via -(BOOL)_execute:arguments: (don't try that at home, kids) doesn't change the bit, or cause downstream nodes to take note of the change either. Neither does - (void)stateUpdated;.

Going even more sinister, I tried this: object_setInstanceVariable(port, "_updated", 1);. Surprise, it sets the bit! Bigger Surprise: The graph doesn't care!

So, I'm at a loss when it comes to actually changing port settings without changing the object entirely, and having the changes propagate through the graph. I guess observers it is (what a stupid solution ... there's a bit Right There for exactly that purpose...)