Writing new custom patchs for QC
Posted by roger on April 8th, 2006This information has been mirrored from ClockSkew’s blog in case his site goes down.
How to Enable Hidden Patches
he enabling of the disabled patches must be done by creating a new patch that enables the disabled patches. (Did that make sense?) Anyway, download the following custom patch:
Unzip it and dump it into /System/Library/Frameworks/Quartz.framework/Frameworks/QuartzComposer.framework/Resources/. You might need to give yourself write permissions (sudo chmod a+w etc.).
Let me know how it works. Not included in this patch is the enabling of iTunes Database, IM Services, and Syndication. I’m still working the bugs out on getting those to work.
How to Write a new Quartz Composer patch
First, a Quartz Composition, referred to as QC from here on, has the following structure:
MyPatch.plugin
- Contents
- MacOS
- Resources
- Info.plist
- Version.plist
The patches reside in the /System/Library/Frameworks/Quartz.framework/Frameworks/QuartzComposer.framework/Resources and have the .plugin extension.
MacOS contains the compiled binary and Resources contains patch XML description files and .nibs.
To create a custom patch, the binary must be compiled properly, resources populated, and a proper Info.plist and Version.plist must be created. All can be done from XCode. I am currently using XCode 2.1, but I am sure older versions will work the same.
To begin, create a new Cocoa Bundle project. Since there is no template for a Quartz Composer plugin (obviously), we’ll just modify the Cocoa Bundle to suit our needs. Before we start coding, some header files are needed to compile the patch properly. Add these to the project as source: GFPlugInRegistrationProtocol.h, QCPatch.h, GFNode.h, GFGraph.h. Also, you’ll need to add the Quartz.framework to the Frameworks and Libraries section of XCode. It can be found in /System/Library/Frameworks/. Lastly, change the target extension from bundle to plugin.
Now besides the classes for each patch, one loader class must be created. Call it whatever you want, but I’ll call mine PatchLoader. Create the PatchLoader.h and PatchLoader.m files. Make your equivalent of the PatchLoader.h file look like this:
#import
#import “GFPlugInRegistrationProtocol.h”
// List your patch class and any QC patches you use here.
@class GFNodeManager, MyPatch;
@interface PatchLoader : NSObject
}
+ (void)registerNodesWithManger:(id)fp8;
@end
And your PatchLoader.m file look like this:
#import "PatchLoader.h"
@implementation PatchLoader
+ (void)registerNodesWithManager:(id)fp8
{
[fp8 registerNodeWithClass: [MyPatch class]];
}
@end
That’s all you need for the loader. Now comes the juicy part — the patch file implementation. I’ll call mine MyPatch to match the references in the above code. Call yours whatever, just make sure the references in the PatchLoader class match. Make your skeleton patch look like this MyPatch.h:
#import
#import “QCPatch.h”
// These are possible input/output types. You don’t need these if you
// don’t have inputs/outputs, but it doesn’t hurt to leave them.
//
// TODO: I need to post the headers to each of these so you can
// see the methods available.
@class QCIndexPort, QCNumberPort, QCStringPort,
QCBooleanPort, QCVirtualPort, QCColorPort,
QCImagePort;
@interface MyPatch : QCPatch {
}
+ (int)executionMode;
+ (BOOL)allowsSubpatches;
- (id)initWithIdentifier:(id)fp8;
- (id)setup:(id)fp8;
- (BOOL)execute:(id)fp8 time:(double)fp12 arguments:(id)fp20;
@end
and this MyPatch.m:
#import “MyPatch.h”
@implementation MyPatch
+ (int)executionMode
{
// I have found the following execution modes:
// 1 - Renderer, Environment - pink title bar
// 2 - Source, Tool, Controller - blue title bar
// 3 - Numeric, Modifier, Generator - green title bar
return 2;
}
+ (BOOL)allowsSubpatches
{
// If your patch is a parent patch, like 3D Transformation,
// you will allow subpatches, otherwise FALSE.
return FALSE;
}
- (id)initWithIdentifier:(id)fp8
{
// Do your initialization of variables here
return [super initWithIdentifier:fp8];
}
- (void)dealloc
{
[super dealloc];
}
- (id)setup:(id)fp8
{
// setup vars here
return fp8;
}
- (BOOL)execute:(id)fp8 time:(double)fp12 arguments:(id)fp20
{
// This is where the execution of your patch happens.
// Everything in this method gets executed once
// per ‘clock cycle’, which is available in fp12 (time).
// fp8 is the QCOpenGLContext*. Don’t forget to set
// it before you start drawing.
// Read/Write any ports in here too.
return TRUE;
}
@end
We now need to create a MyPatch.xml file and put it in the Resources/English.lproj directory. You can do this by hand or have it created automatically by modifying the default Target in XCode. The format of the file will look like this:
Root [Dictionary]
- inputAttributes [Dictionary]
- nameOfInputVarUsed [Dictionary]
- name [String]
- description [String]
- outputAttributes [Dictionary]
- nameOfInputVarUsed [Dictionary]
- name [String]
- description [String]
- nodeAttributes [Dictionary]
- category [String]
- copyright [String]
- description [String]
- name [String]
If you aren’t sure what I mean above, just pop open one from an already created patch.
Now we’re in the homestretch. Two files are left to create, Info.plist and Version.plist. Both these can be added to the XCode Target. In fact, Info.plist is already there, so you just need to add some fields to it. They should look like this. Info.plist:
Root [Dictionary]
- CFBundleDeveleopmentRegion [String -> English]
- CFBundleExecutable [String -> Whatever you called your executable]
- CFBundleIdentifier [String -> com.whatever.WhateverYoursIsCalled]
- CFBundleInfoDictionaryVersion [String -> 6.0]
- CFBundleName [String -> Whatever yours is called]
- CFBundlePackageType [String -> BNDL]
- CFBundleShortVersionString [String -> number]
- CFBundleVersion [String ->number]
- GFPlugin [Boolean - Yes]
- NSPrincipalClass [String -> You Loader Class]
Version.plist:
Root [Dictionary]
- BuildVersion [String -> whatever]
- CFBundleShortVersionString [String -> whatever]
- CFBundleVersion [String -> whatever]
- ProjectName [String -> whatever]
- SourceVersion [String -> whatever]
Just add the missing fields to Info.plist and create a new Version.plist like above . Well, that about does it. What is needed is someone to try out the steps and tell me what I forgot. It’s always hard to remember everything when you already know how to do something.