Skip to content

Instantly share code, notes, and snippets.

@g-l-i-t-c-h-o-r-s-e
Last active November 27, 2025 05:03
Show Gist options
  • Select an option

  • Save g-l-i-t-c-h-o-r-s-e/4b392c826d82102c486eb0b6f21d9208 to your computer and use it in GitHub Desktop.

Select an option

Save g-l-i-t-c-h-o-r-s-e/4b392c826d82102c486eb0b6f21d9208 to your computer and use it in GitHub Desktop.
Test External Window in QC Plugin
//
// BasicExternalWindowPlugIn.h
// BasicExternalWindow
//
#import <Quartz/Quartz.h>
@interface BasicExternalWindowPlugIn : QCPlugIn
{
double _tick;
}
// Output: dummy number so QC knows we’re “doing something”
@property (assign) double outputTick;
@end
//
// BasicExternalWindowPlugIn.m
// BasicExternalWindow
//
#import "BasicExternalWindowPlugIn.h"
#import "BasicExternalWindowController.h"
#define kQCPlugIn_Name @"External Window Test"
#define kQCPlugIn_Description @"Simple QC plug-in that always opens a Cocoa window."
@implementation BasicExternalWindowPlugIn
@dynamic outputTick;
+ (NSDictionary *)attributes
{
return @{
QCPlugInAttributeNameKey : kQCPlugIn_Name,
QCPlugInAttributeDescriptionKey : kQCPlugIn_Description
};
}
+ (NSDictionary *)attributesForPropertyPortWithKey:(NSString *)key
{
if ([key isEqualToString:@"outputTick"]) {
return @{
QCPortAttributeNameKey : @"Tick"
};
}
return nil;
}
+ (QCPlugInExecutionMode)executionMode
{
// Simple provider (no image output)
return kQCPlugInExecutionModeProvider;
}
+ (QCPlugInTimeMode)timeMode
{
// Ask QC to call execute: regularly while the viewer is running
return kQCPlugInTimeModeIdle;
}
+ (BOOL)allowsSubpatches
{
return NO;
}
#pragma mark - Lifecycle
- (BOOL)startExecution:(id<QCPlugInContext>)context
{
_tick = 0.0;
NSLog(@"[BasicExternalWindow] startExecution");
dispatch_async(dispatch_get_main_queue(), ^{
BasicExternalWindowController *controller = [BasicExternalWindowController sharedController];
NSLog(@"[BasicExternalWindow] startExecution -> showWindow (unconditional)");
[controller showWindowFromPlugin:self];
});
self.outputTick = _tick;
return YES;
}
- (void)stopExecution:(id<QCPlugInContext>)context
{
NSLog(@"[BasicExternalWindow] stopExecution");
dispatch_async(dispatch_get_main_queue(), ^{
[[BasicExternalWindowController sharedController] hideWindow];
});
}
#pragma mark - Execution
- (BOOL)execute:(id<QCPlugInContext>)context
atTime:(NSTimeInterval)time
arguments:(NSDictionary *)arguments
{
_tick += 1.0;
self.outputTick = _tick;
// Still log so you can see it's alive
NSLog(@"[BasicExternalWindow] execute: t=%f tick=%f", time, _tick);
return YES;
}
@end
//
// BasicExternalWindowPlugIn.h
// BasicExternalWindow
//
#import <Quartz/Quartz.h>
@interface BasicExternalWindowPlugIn : QCPlugIn
{
BOOL _lastShow;
double _tick;
}
// Input: show/hide window (default YES)
@property (assign) BOOL inputShowWindow;
// Output: dummy number so QC happily runs execute:
@property (assign) double outputTick;
@end
//
// BasicExternalWindowPlugIn.m
// BasicExternalWindow
//
#import "BasicExternalWindowPlugIn.h"
#import "BasicExternalWindowController.h"
#define kQCPlugIn_Name @"External Window Test"
#define kQCPlugIn_Description @"Simple QC plug-in that opens a Cocoa window."
@implementation BasicExternalWindowPlugIn
@dynamic inputShowWindow;
@dynamic outputTick;
+ (NSDictionary *)attributes
{
return @{
QCPlugInAttributeNameKey : kQCPlugIn_Name,
QCPlugInAttributeDescriptionKey : kQCPlugIn_Description
};
}
+ (NSDictionary *)attributesForPropertyPortWithKey:(NSString *)key
{
if ([key isEqualToString:@"inputShowWindow"]) {
// Default ON so the window appears as soon as the patch starts
return @{
QCPortAttributeNameKey : @"Show Window",
QCPortAttributeDefaultValueKey : @YES
};
}
else if ([key isEqualToString:@"outputTick"]) {
return @{
QCPortAttributeNameKey : @"Tick"
};
}
return nil;
}
+ (QCPlugInExecutionMode)executionMode
{
// Simple provider (no image output)
return kQCPlugInExecutionModeProvider;
}
+ (QCPlugInTimeMode)timeMode
{
// Ask QC to call execute: regularly while the viewer is running
return kQCPlugInTimeModeIdle;
}
+ (BOOL)allowsSubpatches
{
return NO;
}
#pragma mark - Lifecycle
- (BOOL)startExecution:(id<QCPlugInContext>)context
{
_tick = 0.0;
_lastShow = YES;
BOOL show = self.inputShowWindow;
NSLog(@"[BasicExternalWindow] startExecution (inputShowWindow=%d)", show);
// For now, ALWAYS force window to show on start so we know the plumbing works
dispatch_async(dispatch_get_main_queue(), ^{
BasicExternalWindowController *controller = [BasicExternalWindowController sharedController];
NSLog(@"[BasicExternalWindow] startExecution -> forcing showWindow");
[controller showWindowFromPlugin:self];
});
self.outputTick = _tick;
return YES;
}
- (void)stopExecution:(id<QCPlugInContext>)context
{
NSLog(@"[BasicExternalWindow] stopExecution");
dispatch_async(dispatch_get_main_queue(), ^{
[[BasicExternalWindowController sharedController] hideWindow];
});
}
#pragma mark - Execution
- (BOOL)execute:(id<QCPlugInContext>)context
atTime:(NSTimeInterval)time
arguments:(NSDictionary *)arguments
{
BOOL show = self.inputShowWindow;
_tick += 1.0;
self.outputTick = _tick;
NSLog(@"[BasicExternalWindow] execute: t=%f inputShowWindow=%d last=%d tick=%f",
time, show, _lastShow, _tick);
if (show != _lastShow) {
NSLog(@"[BasicExternalWindow] execute: detected Show Window change -> %d", show);
_lastShow = show;
dispatch_async(dispatch_get_main_queue(), ^{
BasicExternalWindowController *controller = [BasicExternalWindowController sharedController];
if (show) {
NSLog(@"[BasicExternalWindow] -> showWindow (from execute:)");
[controller showWindowFromPlugin:self];
} else {
NSLog(@"[BasicExternalWindow] -> hideWindow (from execute:)");
[controller hideWindow];
}
});
}
return YES;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment