Photoshop plug-ins with JUCE

I did a bit of googling, I remember back in OS9 we used to be able to do this(application utility level)
Anyway, the link is a bit old, but it may still apply…

[quote]Actually, what seems to work ok is doing a setLevel:NSNormalWindowLevel
on floater on app deactivate and restoring it to NSFloatingWindowLevel
on app activate. Sort of hacky but not too bad.[/quote]

Now…I’m not sure about plugins, but if this was an app you could register a Notification Observer to toggle this status presumably

NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; [center addObserver: self selector: @selector (applicationDidBecomeActive:) name: NSApplicationDidBecomeActiveNotification object: NSApp];

And applicationWillResignActive
(It should be as simple as toggling setAlwaysOnTop(true) on active and setAlwaysOnTop(false) on resign. Juce takes care of calling the setLevel

Anyway, I hope i’m not leading you on a wild goose chase. I think what you are trying to do should be possible, it just requires some OBJ-C++ knowledge to do!

@interface PSAppNotificationListener : NSObject
	ComponentPeer *peer;
- (id)initWithPeer:(ComponentPeer*)_aPeer;
- (void) applicationDidBecomeActive: (NSNotification*) aNotification;
- (void) applicationWillResignActive: (NSNotification*) aNotification;

@implementation PSAppNotificationListener

	if (self = [super init])
		NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
		[center addObserver: self selector: @selector (applicationDidBecomeActive:)
                       name: NSApplicationDidBecomeActiveNotification object: NULL];
       [center addObserver: self selector: @selector (applicationWillResignActive:)
       name: NSApplicationWillResignActiveNotification object: NULL];
	return self;
	[[NSNotificationCenter defaultCenter] removeObserver:self];
	[super dealloc];
- (void) applicationDidBecomeActive: (NSNotification*) aNotification
	if (peer)
- (void) applicationWillResignActive: (NSNotification*) aNotification
	if (peer)

The declaration would be something like this

#ifdef __OBJC__
@class PSAppNotificationListener;
.... your class declaration
void initNotificationWatcher();
void shutdownNotificationWatcher();
...down in the private:

Then instantiate it(again in a .mm impl file)
void YourComponentClass::initNotificationWatcher()
ComponentPeer *componentPeer=getPeer();
notificationWatcher=[[PSAppNotificationListener alloc]initWithPeer:componentPeer];
void YourComponentClass::shutdownNotificationWatcher()
if (notificationWatcher)
[notificationWatcher release];

You’ll no doubt run into issues with Objective C++ as you are cross platform. Basically follow Jules lead, put all the cross platform c++ code in the .cpp file, and on mac have a .mm file that includes this .cpp file and implements the two init / shutdown methods.

Look to Juce source code for inspiration!

Another thing to try(I just did this)…


Get the NSWindow(code above)

[window setHidesOnDeactivate:YES];

That way when the app(hopefully works as a plugin?) becomes inactive the window is hidden…

I am also attempting this.
But first, I believe a bit of background info is in order.
Over the years, Adobe had developed a cross-platform API for designing simple GUIs in Photoshop and Illustrator (and I believe in quite a few other products like After Effect, Acrobat, Première, etc… although I am familiar only with PS and AI). This API was called Adobe Dialog Manager (ADM). Adobe announced many years ago, that ADM was deprecated and was going to disappear at some point. It somehow managed to survived until now (Adobe Creative Suite 5 aka CS5). Perhap unsurprisingly then, Adobe announced that this API would not be ported to 64 bits (because it is based on Carbon on Mac maybe ?), and that developers had to find a substitute, as Adobe would not provide a replacement (although it suggested in that past to use wxWidget or Flex to build GUI). CS5 is the turning point, with a 64-bit PS coming to the market, which puts now the procrastining developper like me squarely in front of the task of throwing away the ADM-based code and to start again from scratch.
I have a vague feeling that the OP is in a similar situation, and, like me, has turned its attention to Juce to solve this…

Now, about PS (and AI) :
The programming interface of PS and AI is quite original. A plugin never gets linked against its host (AI or PS). You can see this with the Dependency Viewer (on Windows). Instead, the hosts calls a plugin through a single, well-known entry point and upon certain events (host startup, host shutdown, plugin loading/unloading, timer elapsed, menu selected, file opened, saved…). The host can load and unload a plugin at unpredictable time, presumably based on the memory needs, and the plugin must be prepared for this. Moreover, one parameter the host passes to the plugin is a pointer to a small set of function pointers. The bundling of function pointers into a C structure is ubiquitous in AI and PS and is called a “suite”. This initial small set of function pointers (the “basic suite” or SPBasic) provides a mean to request further suites. Suites are typically specialized in one domain and group together related function calls, such as memory allocation/deallocation, automation, artwork manipulation, text manipulation and dozens other, among which, of course, ADM stuff(s). This architecture is dubbed Pea (homophone of PI, for Plug In ?), and accordingly many Photoshop suites are named SPxxx for "Suite Pea XXX"
Suites must be acquired before usage and released after (IIRC, there is no imposed lifetime, other than guaranteing that you have released everything when your plugin gets unloaded) . The beauty of this seamingly complex mechanism is that it supports versioning. That is, the plugin requests a particular version of a particular suite. When Adobe evolves its product (say, by introducing Unicode-based API), it carefully leaves the existing suites intact and just adds new ones. This is how ancient plugins compiled with PS 6.0 SDK still work with PS CS5 today. The AI or PS SDK therefore contains a collection of C or C++ structs that group C or C++ function prototypes, both for legacy suites and the current ones.

PS plugins are also further distinguished by “type”. This is encoded statically in resources of the Windows DLL (or of the OSX bundle, and I believe used to be reflected in the Mac Type or the plugin extension 8bp, 8li etc…). Plugin of different types differ by the events they receive, their entry point signature and the suites they can acquire. Plugins of different types also appear in different PS menus (automation vs filter etc…). Certain things are possible in an “automation” plugin but not in an “export” plugin or a “filter” plugin, and vice versa.

Now equipped with this knowledge, back to Juce then. Note that I started playing around with Juce yesterday and I am absolutely not familliar with it. At all. So if I stand to be corrected, please, let me know…

The OP plugin gets called through the single plugin entry point. Based on the prototype, this is a “filter” plugin (“Dissolve” is indeed one). When it receives an event it is interested in, the plugin takes appropriate action(s). As already noted, “event of interest” almost certainly include “load” and “unload” events (unless the plugin is declared “persistent”). Juce initialization/shutdown must be placed so that they will occur in the proper sequence. (there are some guaranties about which sequences of events can or cannot occur).
Because I have a “automation” plugin and not an application, I simply dispensed with creating a JUCEApplication. I guess the OP can safely do the same ? Because ADM dialogs were necessarily modal in PS, and therefore lived (and die) inside a single host call to my plugin, I simply opted for using ScopedJuceInitialiser_GUI in the main entry point function. This nicely eliminates the need to think about load/unload issue and similar niceties.
I then made a message box via AlertWindow::showMessageBox(), followed by an (admittedly empty…) dialog box via DialogWindow::showModalDialog(). That’s it. 5 lines of code (6 if you count #include “juce.h”). Worked perfectly in Windows 32 bits (will try OSX 32 and 64 bits sometime next week).

For the record, I worked with VC8 (aka 2005) and Juce 1.52 did not compile as is, for lack of imapi.h in VC8 Platform SDK (#included by juce_win32_NativeIncludes.h). Because my PS plugin will never need to burn CDs, I simply sidestepped the issue by altering juce_Config.h and undefining JUCE_USE_CDBURNER, without altering any Microsoft header files.

I also had to recompile my plugin (and its dependencies) to statically link the MS CRT. This is because Juce is compiled this way, presumably to allow easy overloading of global operator new() ?

Speaking of operator new(), this is causing me some headache, but in AI this time (and I now slide slightly off topic).
Again some background info :
In AI (and PS), the host may unload you (the plugin) at will, but quite obligingly also hands you a pointer to chunk of memory that gets persisted for you (persisted in memory accross load/unload, that is, not persisted on disk). This avoids severe amnesia (or crashes) in your product.
AI (Windows only), however, silently erase and/or deallocate such memory if you used the compiler supplied operator new(). The only way I found to have AI hand you back this memory unchanged is to allocate it with the SPBlocks suite. So AI SDK also obligingly supplies you a small file (ASMemory.cpp) that contains global redefinitions of operator new/delete. Which IFAIK forces you to use the static CRT (like Juce).
So far, so good.
Until you consider a third partly library which calls operator new() during DLL startup. This obviously has to occur BEFORE the host had any chance to call the entry point of your plugin, hence before the SPBasic suite was available, let alone the SPBlocks suite acquired. Hence the plugin implementation of operator new() can do no better than returning NULL.
Obviously, the subsequent result is not pretty. (This scenario occurs when Juce tries to allocate the global String File::separatorString in juce_win32_Files.cpp)

Note that in PS, the documentation theoretically requires you to use SPBlocks as well for allocating your memory. However, experiments showed that the compiler-supplied operator new() / delete() can allocate/dellocate that memory without any ill effect. This in turns, allows to link the DLL version of MS CRT. Still, a PS plugin that conforms to the SDK documentation (and use SPBlocks) will suffer the issue outlined above for AI (access violation while constructing the String).

At this point, now that a real-world use case has been exposed, the discussion of how to deal with File::separatorString is better left for another thread.

Hope this helps