Inter-app comm? Other stupid tricks?


#1

Hello,

I thought I saw somewhere in the hefty framework docs, a way for two different Juce apps to communicate with one another - but now I can’t seem to find it. Is it real, or am I stoned? Example?

Also - barring platform issues, let’s just say for the moment I’m targeting Windows only - is there a way to make a Juce app hook into the system tray, and not a normal window?

These two questions are linked, because I’m thinking of an app that really runs as more of a service (in the tray) to which another Juce app might want to “connect” to, either via IPC, app-comm, or net-comm…

Thanks for any insights,
Martin Sz.


#2

Well there’s a MessageManager::broadcastMessage() method that’ll send a string out to other juce apps. (Don’t think it works on the mac at the moment).

And you can definitely do a tray app, because I did one myself a few years ago. Requires a bit of hackery, but isn’t too hard. Anyone else got some code to do it?


#3

OK, then - Trying to get MessageManager::broadcastMessage to work.

This is what my ‘receiver’ side looks like. It’s a console app using Juce, and that seems to be OK.

// -----------------------------------------------------------------------------
// (public, overload) initialize - the true entry point of the application.
// -----------------------------------------------------------------------------
void JuceApp_LogConsole_::initialise(const String& commandLine)
{
   puts("JuceLogConsole: Hello!");

   MyMessageListener_ MyMessageListener;
   MyActionListener_  MyActionListener;

   while (MyActionListener.IsDone() == false) // forever
   {
      Thread::sleep(2500);
   }

   JUCEApplication::quit(true);
}

// -----------------------------------------------------------------------------
// (public, overload) shutdown - the true last chance before exit
// -----------------------------------------------------------------------------
void JuceApp_LogConsole_::shutdown()
{
   puts("\nGoodbye from JuceLogConsole");
}

MyActionListener does the minimum you’d expect - puts() the received string to console. MyMessageListener does even less - I only added it to see if it would make a difference, and it doesn’t.

My ‘sender’ side simply uses the broadcast message call, but for reasons unexplained, I don’t see the string making it to the receiver.

Is my way of doing the ActionListener in a console app bad? I need some sort of persistant wait to be ready for callbacks, and it seems I need to do this myself with that sleep loop. Bad idea? Wrong idea? What what? It’s not like I have any dialogs…

Thanks for any help,
Martin Sz.


#4

Ah, maybe you’re not used to event-based programming, but if your thread sits there spinning, no events can get delivered. You need to let the MessageManager run the event loop, and you respond to them with callbacks.


#5

Jules - au contraire - I’ve been doing event-based coding since 1992 (Windows 3.0…) - I’m just not used to it being this easy to do. In the one test Juce app I wrote that has dialogs, I didn’t have to explictly set up and run the MessageManager myself. And so I thought it was some under-the-hood, separate-thread Juce magic. But now I see how it works on the console side - and I consider myself a bit smarter for the experience.

FWIW, here’s my ‘receiver’ code now. It might make a good example for the next person as dumb as me who wanders in. On the ‘sender’ side, I’ll be making a JuceConsoleLogger that derives from Logger - that seems to be the polite thing to do. Ain’t life grand?

[code]// -----------------------------------------------------------------------------
// JuceApp_LogConsole_.cpp
// -----------------------------------------------------------------------------
// JUCE! framework application execution start point for Juce Log Console
// -----------------------------------------------------------------------------
// Who When What
// ---------------- ---------- -----------------------------------------------
// Szinger,MR 2005-07-29 Created
// -----------------------------------------------------------------------------
// © 2005 Martin R. Szinger
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
// the action listener is very simple
// -----------------------------------------------------------------------------
class MyActionListener_ : public ActionListener
{
public:
MyActionListener_() {}
virtual ~MyActionListener_() {}

void /* ActionListener:: */ actionListenerCallback(const String &M)
{
if (M.startsWith(“LOG:”)) printf(M.operator const char *() + 4);
}
};

// -----------------------------------------------------------------------------
// Version info
// -----------------------------------------------------------------------------
const String AppName = T(“Juce Log Console”);
const String AppVersion = T(“00.50.2005.0805”);

// -----------------------------------------------------------------------------
// (public, overload) moreThanOneInstanceAllowed
// (public, overload) getApplicationName
// (public, overload) getApplicationVersion
// -----------------------------------------------------------------------------
bool JuceApp_LogConsole_::moreThanOneInstanceAllowed() {return false;}
const String JuceApp_LogConsole_::getApplicationName() {return AppName;}
const String JuceApp_LogConsole_::getApplicationVersion() {return AppVersion;}
// -----------------------------------------------------------------------------
const char *DASHLINE = “----------------------------------------”
"----------------------------------------\n";
// -----------------------------------------------------------------------------
// (public, overload) initialize - the true entry point of the application.
// -----------------------------------------------------------------------------
void JuceApp_LogConsole_::initialise(const String& commandLine)
{
printf(DASHLINE);
printf(“JuceLogConsole: Hello!\n”);
printf(DASHLINE);

MyActionListener_ MyAL;
MessageManager::getInstance()->registerBroadcastListener(&MyAL);

while (MessageManager::getInstance()->dispatchNextMessage() == true) {}

// we never really get here…
MessageManager::getInstance()->deregisterBroadcastListener(&MyAL);
JUCEApplication::quit(true);
}

// -----------------------------------------------------------------------------
// (public, overload) shutdown - the true last chance before exit
// -----------------------------------------------------------------------------
void JuceApp_LogConsole_::shutdown() {}

// -----------------------------------------------------------------------------
// this macro creates wrapper code to actually launch the app properly.
// -----------------------------------------------------------------------------
START_JUCE_APPLICATION (JuceApp_LogConsole_)
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
// End of file (JuceApp_LogConsole_.cpp)
// -----------------------------------------------------------------------------
[/code]

Thanks for the pointers. It’ll be awhile (we all sincerely hope) before I’m back begging for task bar code help…

Martin Sz.


#6

If that works, cool - but it’s a very very bad idea to make your app spin during the initialise() method (in fact it’s a bad idea in general). In the code that wraps it there are a bunch of things that it does after the initialise() method returns, and before it jumps into the main event loop - have a look inside the JUCEApplication::enterMainApplicationLoop() code to see it in context.

So the “correct” thing to do is simply:

[code]void JuceApp_LogConsole_::initialise(const String& commandLine)
{
printf(DASHLINE);
printf(“JuceLogConsole: Hello!\n”);
printf(DASHLINE);

MyActionListener_* MyAL = new MyActionListener_();
MessageManager::getInstance()->registerBroadcastListener(MyAL);
} [/code]

in reality you’d store the pointer to your listener as a member of the app class, and delete it in your shutdown() method.


#7

Jules - I totally agree with your comments. I followed your example - much better now. But see - it IS like magic, inasmuch as there’s no visible cue that the message manager event loop runs on its own in between initialize() and shutdown(). It seems like a relatively straightforward assumption, but it’s never really stated in the docs, is it? And so many event-driven environments require you to put in some (placeholder if nothing else) code to drive the event loop…

And just a quibble here, but if I’m running this Juce console app and terminate it with a ^C, the app ends of course, but not via shutdown() that I can see. I wonder if I can catch this? Via keypresses? Or something more fundamental…? I like cleanup to be clean.

You just let me know when I get too annoying, OK? :wink:
Thanks,
Martin Sz.


#8

don’t worry - it’s not annoying, it’s always interesting to get different perspectives on this stuff, as I’ve spent so long staring at it that it all seems obvious to me, when I’m sure it isn’t!

Not sure about the ^C - in fact, I’m not sure how that’s handled at all - what’s the normal way of dealing with it? If it can be trapped, then there’s no reason it shouldn’t call the normal quit handlers.


#9

it’s all become dangerously obvious to me too… i fear the possiblility that some day someone may force me to write a non-juce app…

:shock: :shock: :shock: :shock: :shock: :shock:


#10

I should mention that it’s done that way not only to make things easier, but because it’s easier to make it cross-platform, as the mac + windows are a bit different in this regard.

It’s explained quite well in the JUCEApplication docs, isn’t it? If you think there are bits missing, let me know and I’ll expand on it a bit.


#11