On 29.01.2018 19:20 Manolo wrote:
>
> ========== output after closing the window by click on "click to exit"
> button or on window close button ========
> -Info- MyWindow constructor.
> -Info- Starting main.
> -Info- Fl_Button: exit_cb() - closing main window.
> *IMPORTANT* After Fl::run(): closing files, terminating worker threads...
> -Info- Returning from main.
> *IMPORTANT* MyWindow destructor.
> ================================================
Okay, that's what I expected.
> ========== output after stopping app with cmd-Q ========
> -Info- MyWindow constructor.
> -Info- Starting main.
> -Info- MyWindow: win_cb() - closing main window.
> *IMPORTANT* MyWindow destructor.
> ================================================
I'm surprised that "MyWindow destructor" is executed when Cmd-Q is
pressed. Maybe I misinterpreted the OP's message and he didn't mean
destructors of _global_ variables but destructors of _local_ variables
in main(). This was probably not clear in his report.
Manolo and others, could you please test the new attached program with
Cmd-Q on macOS? TIA.
This one has an additional local (stack) variable (class MyButton) with
a destructor. This should display something like:
-Info- MyWindow constructor: global
-Info- Starting main.
-Info- MyButton constructor: local in main()
-Info- MyWindow: win_cb() - closing main window
*IMPORTANT* After Fl::run(): closing files, terminating worker threads...
-Info- Returning from main.
*IMPORTANT* MyButton destructor: local in main()
*IMPORTANT* MyWindow destructor: global
Note that we now have three *IMPORTANT* lines. Given the test results
above from Manolo (thank you!) and the OP's report I assume that we
won't see the line
*IMPORTANT* MyButton destructor: local in main()
if the program is terminated with Cmd-Q.
> As previously discussed, the present cmd-Q processing terminates
> the app before Fl::run() would return, thus "*IMPORTANT* After Fl::run():"
> does not appear.
Yep, that was to be expected - and I consider this a bug!
> In my view, it all boils down to what is considered a "normal" program.
Sorry, I can't agree. In my view it all boils down to two questions:
(1) Are destructors executed when the user types Cmd-Q?
(2) Do *valid* *existing* programs still work as expected and as they do
on other platforms when Cmd-Q is typed.
Question (1) will hopefully be answered by a test with my modified demo
program. If destructors (in this case: of local variables in main()) are
not executed, then this is a bug and we should NOT do this.
Question (2) is more difficult to answer. I wrote my demo program
because I think that this is a valid FLTK program. I don't think that it
is unusual to have code after Fl::run().
Side note (not entirely serious, but true): even our simple hello.cxx
has code after Fl::run() because it reads:
return Fl::run();
return is a statement that is executed after Fl::run() returned and it
returns the return value of Fl::run() to the calling program - i.e. the
runtime system that will return it as the exit status to the shell or
whatever executed the program.
Which makes me ask another question: what exit status does a program
return when it is terminated with Cmd-Q in our current version? My demo
returns 0 under Linux in all cases. `echo $?' should give the answer.
> Under non MacOS, a normal GUI program should cleanly stop
> when all its windows are closed, because there's no way to
> interact with it through its GUI.
True. And it does this because Fl::run() returns and the program
terminates (returns from main()) after optional cleanup etc.. As I wrote
in my example, I consider terminating other threads and closing files to
be "normal" behavior after Fl::run(). I've seen examples that do this,
and even if not, we must assume that there are lots of programs out
there in the wild that do this.
> What is a normal MacOS program expected to do when all its
> windows close?
> Firefox, MSWord, Mail, Xcode, XQuartz, etc.. keep running
> because, through their system menu bar, it's possible to open a
> new window.
I consider this "special macOS" behavior. The majority of cross platform
FLTK programs won't do this. Unless they are especially coded for macOS.
Again, my point is that we need a consistent behavior across platforms
with *existing* code that is not broken by a system function that can be
easily implemented in a non-breaking way (let the program continue and
finish cleanly).
Please don't understand me wrong: I'm not discussing for myself. I'm
concerned about the implications of the current implementation for
compatible behavior of, as I said before, existing programs. We can't
assume that every programmer of every FLTK program changes the way he
programmed cleanup code and destructors (if that applies, see new demo
program) only because macOS introduces a new feature (Cmd-Q) and we
(FLTK) implement it in a non-compatible way!
> Therefore, cmd-Q does more than close all windows, it also terminates
> these programs.
It would be easy to add a new handler (as I suggested before) or use the
Cmd-Q global keyboard shortcut in macOS programs that want to support
this new feature of programs that run without open windows. It's a
design decision of FLTK that Fl::run() returns when all windows are
closed and (as documented in the beginners documentation) that the
program returns from main() when Fl::run() returns.
Everything else can be considered non-standard and programmers that want
to support this non-standard behavior can be required to do something
special in their programs. See below.
> What do we want a normal FLTK application to do on MacOS when all
> windows close?
> 1) exit after having run the cleanup code placed after the Fl::run()
> statement,
> if there's one?
Yes.
> or
>
> 2) allow the possibility to keep running (which requires to have put
> something
> in the application's system menu bar) ?
We can have both. If you want that behavior then it is non-standard and
you have to do "something special". I don't know what this would be, but
it can't be that complicated w/o sacrificing compatibility and
correctness of other existing programs. See an idea below.
> Choosing 1) means that applications behave identically across platforms,
> and requires to change in FLTK the present processing of cmd-Q, so Fl::run()
> returns after cmd-Q is processed.
Cmd-Q processing in FLTK is a "new" feature. I don't know when it was
introduced, I'm sure you know this better. Anyway, I believe it should
be changed to return NSTerminateCancel to the macOS Cmd-Q processing and
decide later (on program level) what to do. It's not FLTK's
responsibility to "kill" a program in an unconventional way, i.e. by not
returning from Fl::run() and (again, if that applies) prevent
destructors from being executed.
> Choosing 2) means that the standard behaviour of a Mac app is obtained,
> with the present FLTK code, putting
> #if __APPLE__
> while (true) Fl::wait();
> #endif
> towards the end of main().
You can also do this with the modified Cmd-Q behavior.
> If we would change the processing of cmd-Q to stop it from terminating the
> app and instead terminate the event loop, then a MacOS app wanting to run
> with no open window would have to register a cmd-Q handler
> as a global shortcut and make it do what FLTK used to do.
That's exactly what I say. This is non-standard FLTK behavior and
(macOS) apps that want to support this must do "something" to make it
work. But not the majority of standard FLTK programs that terminate when
Fl::run() returns. It can be pretty easy to do, see below.
> With today's code, FLTK behaves unexpectedly only if its cleanup code
> does not find
> naturally its place in window callbacks. If it does, closing windows by
> cmd-Q
> calls the callbacks and the cleanup code runs.
Neither you nor me nor any other FLTK developer can decide what the
"natural" place of cleanup code in any application is or would be.
Particularly not if we consider all applications developed during the
last 20 years.
Again (sorry for the repetition): The main point is: we MUST NOT change
the behavior of existing programs that work as designed. We may add
features, but these added features may require the user (developer) to
do _something_.
> If an app is conceived so that ctrl-Q or cmd-Q stop it under
> all platforms, it is necessary to define a global shortcut and to make it
> cleanly stop the app, most probably by stopping Fl:run() and running code
> placed later. This is done with only platform-independent code,
> and requires no change to FLTK, because FLTK's handling of cmd-Q does
> not run.
>
> Another possibility is that cmd-Q is handled by the app only on the
> MacOS platform.
> My reasoning is that, in that case, cmd-Q is expected to terminate the
> program for good
> when all windows replied to the FL_CLOSE event by closing themselves,
> because that's
> what most MacOS app do.
> A consequence would be that all cleanup code should be placed in window
> callbacks
> or in atexit() functions.
>
> I am ready to conclude this reasoning is too MacOS inspired if it
> creates difficulties for FLTK developers.
I think it's only the _default_ behavior of the current Cmd-Q processing
in the way that it terminates the app within Fl::run(). This is not
compatible with existing - unmodified - FLTK applications and must
strictly be avoided.
I'm sure we can find another way to make it easier (if necessary) for
macOS apps that want to continue to run after all windows are closed
(which is, as I said, more the exception from the rule, although it may
be typical for macOS applications). One possibility would be the handler
proposed by me, which would act like this:
macOS Cmd-Q processing calls
- FLTK Cmd-Q processing which
--- calls the handler if registered
--- sends FL_CLOSE events to all windows if requested/allowed
-- exits with NSTerminateCancel to continue the program
It would be the handler's responsibility to
- either terminate the program directly (if applicable)
- or set a flag that the program is to be terminated
If all windows have been closed and Fl::run() has returned the program
has to decide if it wants to continue without open windows. If the Cmd-Q
handler set the "terminate program" flag the program should exit cleanly
as it would on all other platforms.
If the "terminate program" flag was not set (i.e. the user closed all
windows) a macOS application would decide to wait by running Fl::wait()
in a loop as you explained elsewhere in this thread.
We could even add a new global FLTK variable and let the programmer
query or even reset it with accessor methods:
int Fl::terminate_program()
and
void Fl::terminate_program(0)
to reset the flag if desired. This way the registration of a handler
would not be necessary. FLTK's internal Cmd-Q processing could set the
variable so programs that call Fl::terminate_program() know that they
should terminate instead of running w/o windows.
This could even be compatible across platforms because this flag could
be initialized with 1 on non-macOS platforms, hence they would always
terminate when all windows are closed if they support running w/o
windows. Or something like that.
These changes would be minimal (register a handler or potentially only a
variable, ie. the above-mentioned "terminate program" flag or nothing at
all (if we use Fl::terminate_program()) and query this flag after
Fl::run() before deciding whether to terminate or to run continuously.
The main difference would be that all other (one-shot, aka standard
FLTK) applications would continue to work unmodified, whereas "special"
macOS programs that want to continue running w/o windows must be
changed. Since this is a new feature (especially) for macOS I believe
this would be the better decision.