Is FLTK (Linux, 1.5.0) usable in batch mode (without display)?

26 views
Skip to first unread message

Basile Starynkevitch

unread,
Sep 29, 2026, 8:50:31 AMSep 29
to fltk.general
Hello all,

Is it possible to code some open source FLTK software which can (like GNU emacs with its --nw option) be used in batch mode (without screen, keyboard, mouse).

An interesting example might be for instance to improve the CubeView example so that with the --show-pid-in-batch program option it would just output a message showing its pid (using getpid()) and would still work on Linux computer when started using the batch utility (or crontab).

My open source project is (on github.com/RefPerSys/ ....) an inference engine and would need thay feature.

Regards from France

Basile STARYNKEVITCH <bas...@starynkevitch.net>
8 rue de la Faïencerie
92340 Bourg-la-Reine
France

Albrecht Schlosser

unread,
Sep 29, 2026, 9:57:45 AMSep 29
to fltkg...@googlegroups.com
On 9/29/26 14:25 Basile Starynkevitch wrote:
Is it possible to code some open source FLTK software which can (like GNU emacs with its --nw option) be used in batch mode (without screen, keyboard, mouse).

Yes, see fluid. If used with fluid -c (or some other commands), it doesn't open a display.


An interesting example might be for instance to improve the CubeView example so that with the --show-pid-in-batch program option it would just output a message showing its pid (using getpid()) and would still work on Linux computer when started using the batch utility (or crontab).

The following program (taken from test/hello.cxx and modified) displays its PID without having a display:

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Box.H>
#include <unistd.h>
int main(int argc, char **argv) {
Fl_Window *window = new Fl_Window(340, 180);
Fl_Box *box = new Fl_Box(20, 40, 300, 100, "Hello, World!");
box->box(FL_UP_BOX);
box->labelfont(FL_BOLD + FL_ITALIC);
box->labelsize(36);
box->labeltype(FL_SHADOW_LABEL);
window->end();
// window->show(argc, argv); // must not show a window !
auto pid = getpid();
fprintf(stderr, "PID = %d\n", pid);
return Fl::run();
}
You can test it, for instance, like this:

$ unset DISPLAY
$ WAYLAND_DISPLAY=NONE
$ ./get_pid
PID = 182885

Your code must determine whether it's run in batch mode and must not open the display. There are a lot of things that would open the display, for instance any drawing calls, some font handling stuff, and - obviously - showing a window.

Note that I left everything except showing the window in the demo code above to demonstrate that a lot of stuff "works" w/o a display.

There is no switch that would just not open the display and skip anything that would be done *if* the display was opened.

Matthias Melcher

unread,
Sep 29, 2026, 9:58:09 AMSep 29
to fltk.general
Yes, absolutely. Fluid is the prefect example. It's a full blown GUI app with complex dialog windows, created itself in Fluid. But if you launch Fluid with `-c filename.fl` it never opens anything related to the user interface, windows, or graphics. We took special care that it doesn't even open a display connection on X11. That way, we use Fluid in our CI script to generate code from layout.

There is on caveat on Windows though. Window application can always only bei *either* graphics apps *or* command line apps. It's a limitation of Windows. If you launch a command line app from finder, a CMD.EXE window will pop up with it. If you launch a graphics app fro the commend line, you don;t get access to stdin and stdout . Or something like that ;-)

Rob McDonald

unread,
Sep 29, 2026, 8:31:21 PMSep 29
to fltk.general
In addition to the way fluid approaches it, you can also design your program to have separation more deeply.

In my program, we have a batch version that can be built with none of the GUI and OpenGL stuff at all.  That way, the batch executable has no dependencies on any graphics libraries from the operating system at all -- no OpenGL, no X, no Wayland, etc.

This is important because my program is sometimes run in HPC (cluster) environments where the headless nodes don't have those libraries installed -- and getting the sys admins to install things like that on their machine is not practical.

My main() is the same for both the headless and the GUI application.  It gets compiled twice - once with an #define for NO_GRAPHICS.  There are some #ifdef's that basically turn some things like gui_start() into a no-op.

Then, all the GUI code is written pretty normal -- it has full access into the core code, it can do whatever it wants.

The core code however must be able to be compiled without any knowledge of the GUI / OpenGL code at all.  This means it can't even #include any headers from the GUI side of things (fltk, or the application's own code).

So, put your core code in one directory and your GUI code in a different directory.  Then, set up your include paths such that the GUI sees the core, but not the other way around.

For the situation where the core code needs to communicate with the GUI, we devised a message passing system.  Listeners subscribe to the messages, but senders never know who is subscribed.  So, the GUI subscribes and the core sends messages that will be received if the listener is there -- but won't if it isn't.

As it turns out, there is basically only one message that needs to be sent from the core to the GUI -- "Needs_Update".  I.e. something in the core state has changed and you need to trigger a GUI update to reflect that state.

The GUI side of things can #include the core code and has full access, so when it goes through and does Update(), it has full access to all the classes, data, and state of the core.

This approach seems like it would be very limiting, but it really isn't that bad.

On my Mac desktop, the GUI executable (with everything statically linked) is 25MB, the headless executable is 20MB.

The GUI application requires these libraries (otool -L ./app):

/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa (compatibility version 1.0.0, current version 24.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1345.120.2)
/System/Library/Frameworks/UniformTypeIdentifiers.framework/Versions/A/UniformTypeIdentifiers (compatibility version 1.0.0, current version 709.0.0, weak)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 1700.255.5)
/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 2487.60.105)
/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices (compatibility version 1.0.0, current version 64.0.0)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 2503.1.0)
/System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics (compatibility version 64.0.0, current version 1774.5.3)
/System/Library/Frameworks/CoreText.framework/Versions/A/CoreText (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 2503.1.0)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)

While the headless app requires:

/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 1700.255.5)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1345.120.2)

The strict headless environments are all Linux, but you get the idea.

Rob
Reply all
Reply to author
Forward
0 new messages