CMake Console App with Optional GUI Window

Hey folks,

After a bit of searching, I see that this question has been asked in a few different ways before, but none which seem to include discussion of the new JUCE6 CMake features, so please excuse my potential duplicate :slight_smile:

I’m working on a little tool that I’ll generally be using from the command line. I started out my app structure using CMake with the juce helper juce_add_console_app(${TargetName} PRODUCT_NAME "MyHelperTool") . That all worked great and I’ve been able to get quite far with that, but now I want to introduce an optional window to display some values that can be opened by passing some flag to the CLI when invoking the program.

So I followed along this tutorial: and I’m able to create and open a DocumentWindow without problem. The strange thing is that when I do this from my CLI app, the window refuses to take the foreground. I’m on MacOS, and I can see the window pops open but if I try to focus on the window it immediately snaps to behind any other application window I have open. When I press cmd+Tab, I don’t see the application window open as something I can cmd+Tab to.

Eventually I took a wild guess and changed my CMake config to the following: juce_add_gui_app(${TargetName} PRODUCT_NAME "MyHelperTool"), and now my window behavior all works as expected. The unfortunate part is that now my project has the full MacOS application structure with the resources folder and everything, and when I invoke just the CLI behavior I still get the full application window treatment. Really the only build product I want out of this is a single little CLI binary that generally operates as a command line tool but can pop up a window and behave as expected by the OS if given the right flag.

Is there a way to do that? Maybe there’s some hybrid CMake flags I can set? Thanks.

Unfortunately I don’t think there’s a way to achieve this. At least on Windows, the build for a GUI app is substantially different to the build for a console app - the executables have different entry points - and so it’s not possible to build a console app which can open windows on the desktop (happy to be corrected on this by someone more knowledgeable though).

The Projucer and Pluginval can both be run in either windowed or command-line mode, but they are both full GUI apps with command-line options. If you need to support both windowed and command-line modes, I think this kind of approach is probably the best-supported way to achieve that.

Thanks @reuk, that’s good to know.

I wonder, if I ignore Windows (and target only MacOS and maybe linux), does this get easier? I don’t often use Windows personally so I don’t necessarily need this tool to support Windows.

Also, I think I’m missing something– I’ve studied the Projucer source, and I’m noticing that if I run certain options via the CLI (e.g. --server), it really properly works as a CLI tool. That is, doesn’t show up as an available app in my Cmd+Tab application switcher, for example. In my own tests, even when I don’t open up a juce::DocumentWindow, when I run my tool from the CLI I get this MacOS “application harness” which shows my app icon in the Cmd+Tab switcher, occupies the top MacOS Menu bar with my app name and everything. What’s the trick? How do I get the expected command-line-only behavior when building as a GUI app like Projucer does?


I think that’s due to the commandline handling in ProjucerApplication::initialise (jucer_Application.cpp:56). In the server case, there’s a manual call to Process::setDockIconVisible. In other cases, it processes the command and then immediately quits the app, which I’m guessing happens quickly enough that the icon never shows up in the dock.

That seems to do the trick, thanks @reuk!

Hey @reuk I’ve been poking at this a little bit more today, and I found an interesting edge case: if I symlink the Projucer executable–

ln -s juce/extras/Projucer/Builds/MacOSX/build/Release/ projucer

And then use the symlink to open my jucer file: projucer myapp.jucer, I see the same behavior I mentioned earlier. Projucer will open a window, but that window won’t come to front, and when I try to focus the window manually it immediately jumps behind whatever other apps I have open. I notice as well that Projucer doesn’t set up the application chrome– like, I can’t alt+tab to it or anything.

Now, if I run the projucer executable from that nested directory manually, it does work as you’d expect, but the window doesn’t pop to front, you have to go find it.

I have found that I can resolve the former case by explicitly calling juce::Process::setDockIconVisible(true); by default, but this feels a bit hacky. Also, it’s a partial fix, because the app chrome will take the name of whatever the symlink file is named. It’s odd.

So, this feels like a solution that’s 90% there, and I wonder if there’s some clever mac windowing tricks to be pulled to make this feel as you’d expect.

In principle It’s absolutely possible to get standard c++ console I/o working in Windows desktop apps and it’s also possible to do windowing stuff in a console app (did both ages ago for a non-juce game product). Here’s an article that a quick Google search revealed:

Wasn’t there already an awkward way to start the message loop of a juce app from a console app via some macros? I.e. do whatever is done in WinMain() in main() ?

Definitely, there’s the juce::ScopedJuceInitialiser_GUI I think. But if you use JUCEApplication or JUCEApplicationBase I think this is handled for you, so long as you keep the beef of your app within the initialize and shutdown method overrides (which I’m doing here).

So I suspect it’s more along the lines of convincing MacOS that we’re running a proper GUI app even when invoked from the CLI