Terminating an app on OSX

332 views
Skip to first unread message

Mathieu Peyréga

unread,
Jan 24, 2018, 11:28:44 AM1/24/18
to fltk.general
Hello,

i'm trying to port an app on OSX, everything comiles and works pretty well but one behaviour I don't like.

When I close the app thought the small left red cross button, everything works as expected and as windows or Linux buttons.

If I use cmd + q or the system menu, then the app exit, but none of the instantiated classes constructors get called, it seems like a really straight exit !
Is there a way to have the menu + cmd Q behaviours matching the red cross behaviour ?
Both seems to post the FL_CLOSE message, but on of the 2 methods does something else...

Regards

Manolo

unread,
Jan 24, 2018, 12:29:32 PM1/24/18
to fltk.general
You should assign a callback function to your window(s). The callback will be called
when cmd-Q is typed or selected in the menu.
The callback function will decide what to do,
for example to start a confirmation dialog, or ask to save modified data.

The callback function must have this prototype:
     void my_cb(Fl_Widget *widget, void *data)
and it's assigned to the window with
    mywindow->callback(my_cb, &data);
where &data points to some data structure of interest for the callback function.

Here is the sequence of events when cmd-Q is typed, under MacOS:

- the NSApplication object receives the terminate: message (in Cocoa parlance)
- the app delegate receives the applicationShouldTerminate: message
which calls
   Fl::handle(FL_CLOSE, win);
for each top-level FLTK window win.
- the FL_CLOSE event is processed by calling the window's callback function
- user code (the window callback function) decides what to do.

Manolo

unread,
Jan 24, 2018, 12:37:15 PM1/24/18
to fltk.general
If there's no callback function assigned to a window, the FL_CLOSE event
is processed by calling the default window callback function which does
   window->hide()
Thus, all the app's window close, and the app stops.

Albrecht Schlosser

unread,
Jan 24, 2018, 6:02:37 PM1/24/18
to fltkg...@googlegroups.com
On 24.01.2018 17:15 Mathieu Peyréga wrote:

> i'm trying to port an app on OSX, everything comiles and works pretty
> well but one behaviour I don't like.

Which FLTK version are you using?

> When I close the app thought the small left red cross button, everything
> works as expected and as windows or Linux buttons.
>
> If I use cmd + q or the system menu, then the app exit, but none of the
> instantiated classes constructors get called, it seems like a really
> straight exit !

Are these global, static objects? BTW, just to be sure, you mean the
destructors (not constructors), right?

> Is there a way to have the menu + cmd Q behaviours matching the red
> cross behaviour ?
> Both seems to post the FL_CLOSE message, but on of the 2 methods does
> something else...

Manolo is our macOS specialist and he posted two replies. I don't know
if these replies answer your question why there is a difference between
the two ways to close the window and/or stop the app. I can't help with
details on macOS, so please refer to Manolo's replies.

Maybe you are using an older FLTK version (that's why I asked the
question above)?

Or maybe you are doing something you should not do in the window
callback? The window callback should just hide() the window if you want
to react on the window close (red cross) button. Note that you should
not call _exit() (with the leading underscore) which would exit
immediately w/o calling the destructors of static objects.

Mathieu Peyréga

unread,
Jan 25, 2018, 4:04:15 AM1/25/18
to fltk.general
Hello,

I'm using version 1.3.4-2 of fltk

the code of main is like this :

I have a GUI class, which is superclass of Fl_Overlay_Windows
and an IO thread driven by boost.asio lbrary
at exit of fltk event loop, there is a termination sequence for the IO thread to exit cleanly

int main(int argc, char *argv[])
{
 
int res_code = 0;
 
try
 
{
 
Fl::lock();                 C_AppMainGUI theAppMainGUI;
 C_IO_Controller theIOController
(theAppMainGUI);
 theAppMainGUI
.linkToIO_Controller(theIOController);




 boost
::thread io_thread(boost::bind(&C_IO_Controller::run,&theIOController));




 theAppMainGUI
.show();




 
while (Fl::wait() > 0)
 
{
 
void *mess = Fl::thread_message();
 
if (mess)
 
{




 
}
 
}
 
 
Fl::unlock();




 std
::cout << "Termination sequence engaged" << std::endl;
 std
::cout << "App GUI terminated" << std::endl;
 std
::cout << "Posting IO thread stop command" << std::endl;




 
// At GUI exit send a stop command to IO thread from the main thread to let all pending IO terminate correctly
 C_AppInternalProtocolTerminateAppDataFrame
*stopCommand = new C_AppInternalProtocolTerminateAppDataFrame();
 stopCommand
->usePrivateMemory(true);
 stopCommand
->prepareForTransmission();
 theIOController
.addCommandFromGUI(*stopCommand);




 std
::cout << "IO thread stop command posted" << std::endl;
 std
::cout << "IO thread joining" << std::endl;




 io_thread
.join();
 std
::cout << "IO thread joined... exiting" << std::endl;




 
}
 
catch(std::exception const& e)
 
{
 std
::cerr << "Exception catched in main()" << std::endl << e.what() << std::endl;
 res_code
= 1;
 
}
 
catch(...)
 
{
 std
::cerr << "Unknown exception catched in main()" << std::endl;
 res_code
= 1;
 
}




 
return res_code;
}



the destructor of C_AppMainGUI is like this :

C_AppMainGUI::~C_AppMainGUI(void)
{
 std
::cout <<  "C_AppMainGUI::~C_AppMainGUI(void)" << std::endl;
 
this->unlinkIO_Controller();
}


If I stop the app with the red round button, I got this in console :
Termination sequence engaged
App GUI terminated
Posting IO thread stop command
IO thread stop command posted
IO thread joining
IO thread joined
... exiting
C_AppMainGUI
::~C_AppMainGUI(void)


The console is empty when exiting with the menu of cmd+Q so its not event going though the code after the event loop.

I already tried adding a callback to main windows, this is indeed called when exiting from the main menu or the red button or cmd+Q

In the end of whatever callback processing, I have to do a hide() somewhere (I believe and this match your comment) and when this hide occurs, the same behaviour exhibits : the exit thought red button goes a comprehensible path, the other way seem like a brutal process kill

I'm using up to date High Sierra  (in case it matters) and up to date Xcode version.

I'm not calling any form of exit... 

Manolo

unread,
Jan 25, 2018, 5:04:21 AM1/25/18
to fltk.general
I see.

When the window is closed by its red close button, the window is hidden
by the window's callback function (either yours or the default callback),
Fl:wait() returns 0, and your "termination sequence" code runs.

When cmd-Q is typed or activated from the menubar, this happens:
- the "terminate:" message is sent to the NSApplication object (which is the
MacOS object representing the running app)
- the application's delegate (another MacOS object) receives the
"applicationShouldTerminate:" message which FLTK processes by
sending the FL_CLOSE event to all visible windows
- the window's callback hides the window
- processing of the "applicationShouldTerminate:" message detects
that all windows have been closed (or hidden) and returns a value
that MacOS interprets as "The app should now stop". If any window
remains visible, the returned value lets the app continue.

One solution would be that your window callback function triggers
your "termination sequence" code, which would run before
control goes back to MacOS which stops the app.




Manolo

unread,
Jan 25, 2018, 5:18:28 AM1/25/18
to fltk.general
You could put your "termination sequence" code in a function, say
void termination() {
  printf("\nTermination\n");
  exit(0);
}

and call this function twice :
1) after your
while (Fl::wait() > 0) {
}
clause

2) in your window's callback function
void my_cb(Fl_Widget *wid, void*) {
  wid->top_window()->hide();
  termination();
}

Ian MacArthur

unread,
Jan 25, 2018, 5:24:49 AM1/25/18
to fltkg...@googlegroups.com
A question: The explicit exit(); call in the termination function is so that the termination function can not be called twice? Whichever of the two paths calls it first becomes the *actual* exit case, in that circumstance?




Manolo

unread,
Jan 25, 2018, 5:34:32 AM1/25/18
to fltk.general
Yes. Because termination would be called twice when closing the window
with the close button, first by the window's callback, second after the while(Fl::wait()) clause.

I realize now it could be possible to terminate always processing of the
"applicationShouldTerminate:" message by the value which makes MacOS
continue running the app. Control would normally then go to Fl::wait()
which would indicate no window is left, and would go the the app's termination
code.

I'm not sure what's the best choice, because cmd-Quit is a MacOS specific command
whose expected processing is what FLTK does now.

Mathieu Peyréga

unread,
Jan 25, 2018, 5:52:36 AM1/25/18
to fltk.general
If I want to try that, I just have to modify the Fl_Cocoa.mm message and replace the terminatenow ?

From my point of view, this would really be the best solution if the quit command exit the fltk event loop usual way...

Best regards

Ian MacArthur

unread,
Jan 25, 2018, 6:22:16 AM1/25/18
to fltkg...@googlegroups.com


> On 25 Jan 2018, at 10:39, Mathieu Peyréga wrote:
>
> If I want to try that, I just have to modify the Fl_Cocoa.mm message and replace the terminatenow ?
>
> From my point of view, this would really be the best solution if the quit command exit the fltk event loop usual way...


Possibly - though then you would need to maintain a modified fltk base, which might be harder than just doing the “shutdown” activity in the window close CB.

Unless the fltk core is modified in this way at some point, of course; though it looks as if what the fltk core currently does is what Apple would advise, so perhaps it is “right” the way it is, and the “special cases” have to be handled in other ways, e.g. via the window close callback or etc...?





Manolo

unread,
Jan 25, 2018, 6:36:22 AM1/25/18
to fltk.general


On Thursday, 25 January 2018 11:52:36 UTC+1, Mathieu Peyréga wrote:
If I want to try that, I just have to modify the Fl_Cocoa.mm message and replace the terminatenow ?
Yes. Just replace the last statement of applicationShouldTerminate: in Fl_cocoa.mm  by
      return NSTerminateCancel; //reply;



From my point of view, this would really be the best solution if the quit command exit the fltk event loop usual way...
I agree for your app it would be appropriate.

Albrecht Schlosser

unread,
Jan 25, 2018, 7:03:31 AM1/25/18
to fltkg...@googlegroups.com
On 25.01.2018 12:22 Ian MacArthur wrote:
>
>
>> On 25 Jan 2018, at 10:39, Mathieu Peyréga wrote:
>>
>> If I want to try that, I just have to modify the Fl_Cocoa.mm message and replace the terminatenow ?
>>
>> From my point of view, this would really be the best solution if the quit command exit the fltk event loop usual way...
>
>
> Possibly - though then you would need to maintain a modified fltk base, which might be harder than just doing the “shutdown” activity in the window close CB.
>
> Unless the fltk core is modified in this way at some point, of course; though it looks as if what the fltk core currently does is what Apple would advise, so perhaps it is “right” the way it is,

Is it? I can't comment on "what Apple would advise", but ...

> and the “special cases” have to be handled in other ways, e.g. via the window close callback or etc...?

... this seems to contradict the documentation because Fl::run() would
never return when cmd+q is used under macOS. This assumption may be
wrong, but that's what I understood from the discussion above. The OP
uses some kind of self-maintained Fl::wait() loop, but essentially this
would be the same issue.

OTOH Manolo wrote that it would be possible to avoid this premature
program termination by returning another value to the macOS message.
This looks like a better solution WRT FLTK standard behavior, but I
don't know what would happen from a user's perspective if the program
did not exit after the event loop returned.

So what happens with the standard test/hello.cxx program if you change
the last lines to be like:

window->show(argc, argv);
int ret = Fl::run();
printf("Program exited normally.\n");
return ret;
}

Would it output "Program exited normally." when cmd+q was used?

Second question: if not: shouldn't it do this?

Third question: as I understand, it is possible not to close at least
one window when the FL_CLOSE message is received after cmd+q has been
pressed by the user. If this happens, will the program be terminated or
will it keep running?

Albrecht Schlosser

unread,
Jan 25, 2018, 7:25:59 AM1/25/18
to fltkg...@googlegroups.com
Maybe the following discussion should go to fltk.coredev, but I'm
replying here to maintain the thread context.

On 25.01.2018 13:03 Albrecht Schlosser wrote:

> ... this seems to contradict the documentation because Fl::run() would
> never return when cmd+q is used under macOS. This assumption may be
> wrong, but that's what I understood from the discussion above. The OP
> uses some kind of self-maintained Fl::wait() loop, but essentially this
> would be the same issue.
>
> OTOH Manolo wrote that it would be possible to avoid this premature
> program termination by returning another value to the macOS message.
> This looks like a better solution WRT FLTK standard behavior, but I
> don't know what would happen from a user's perspective if the program
> did not exit after the event loop returned.
>
> So what happens with the standard test/hello.cxx program if you change
> the last lines to be like:
>
>   window->show(argc, argv);
>   int ret = Fl::run();
>   printf("Program exited normally.\n");
>   return ret;
> }
>
> Would it output "Program exited normally." when cmd+q was used?
>
> Second question: if not: shouldn't it do this?
>
> Third question: as I understand, it is possible not to close at least
> one window when the FL_CLOSE message is received after cmd+q has been
> pressed by the user. If this happens, will the program be terminated or
> will it keep running?


Citing the current docs (chapter "Operating System Issues"):
http://www.fltk.org/doc-1.3/osissues.html

Apple "Quit" Event

When the user presses Cmd-Q or requests a termination of the
application, OS X will send a "Quit" Apple Event. FLTK handles this
event by sending an FL_CLOSE event to all open windows. If all windows
close, the application will terminate.

-- End of citation --

This says: "the application will terminate". What if we changed this to
"the event loop (Fl::run()) will terminate. It is the responsibility of
the program to exit after Fl::run() returns."

BTW: the docs don't say what happens if at least one window is kept
open. Does this mean that the program continues running "normally"
(except that maybe some other windows were closed)?

Note: it is possible to use Fl::run() in a loop to simulate multiple
invocations of a program or something similar:

for (int i = 0; i < 10; i++) {
printf("Loop no %d:\n", i + 1);
// do something with index i
window[i].show();
Fl::run();
}

On all platforms closing the window with the close button would repeat
the loop, but on macOS using cmd-q would terminate the program. Is this
considered consistent, or should it be changed?

Manolo

unread,
Jan 25, 2018, 8:27:18 AM1/25/18
to fltk.general


On Thursday, 25 January 2018 13:03:31 UTC+1, Albrecht Schlosser wrote:


OTOH Manolo wrote that it would be possible to avoid this premature
program termination by returning another value to the macOS message.
This looks like a better solution WRT FLTK standard behavior, but I
don't know what would happen from a user's perspective if the program
did not exit after the event loop returned.

So what happens with the standard test/hello.cxx program if you change
the last lines to be like:

   window->show(argc, argv);
   int ret = Fl::run();
   printf("Program exited normally.\n");
   return ret;
}

Would it output "Program exited normally." when cmd+q was used?
No, because MacOS would stop the program before  Fl::run() returns.


Second question: if not: shouldn't it do this?
That's what we have to decide.

 

Third question: as I understand, it is possible not to close at least
one window when the FL_CLOSE message is received after cmd+q has been
pressed by the user. If this happens, will the program be terminated or
will it keep running?
MacOS stops the app only if all windows were closed. If any window callback
returns without having closed its window, the event loop continues.

 

Manolo

unread,
Jan 25, 2018, 8:37:03 AM1/25/18
to fltk.general


On Thursday, 25 January 2018 13:25:59 UTC+1, Albrecht Schlosser wrote:

Citing the current docs (chapter "Operating System Issues"):
http://www.fltk.org/doc-1.3/osissues.html

Apple "Quit" Event

     When the user presses Cmd-Q or requests a termination of the
application, OS X will send a "Quit" Apple Event. FLTK handles this
event by sending an FL_CLOSE event to all open windows. If all windows
close, the application will terminate.

-- End of citation --
What is programmed is exactly what is described here.
Thus, there's no contradiction with the documentation.
 

This says: "the application will terminate". What if we changed this to
"the event loop (Fl::run()) will terminate. It is the responsibility of
the program to exit after Fl::run() returns."
Yes, we can decide to change the FLTK behaviour slightly and get what is
described above.


BTW: the docs don't say what happens if at least one window is kept
open. Does this mean that the program continues running "normally"
(except that maybe some other windows were closed)?
Implicitly, the reader is expected to understand the program continues
when not all windows are closed, after reading the program stops when
all windows are.
 

Note: it is possible to use Fl::run() in a loop to simulate multiple
invocations of a program or something similar:

   for (int i = 0; i < 10; i++) {
     printf("Loop no %d:\n", i + 1);
     // do something with index i
     window[i].show();
     Fl::run();
   }

On all platforms closing the window with the close button would repeat
the loop, but on macOS using cmd-q would terminate the program. Is this
considered consistent, or should it be changed?

I notice there's no equivalent of cmd-Q on other platforms.
What can be done on X11/MSWindows is to close all windows with their closing button.
The result is that Fl::run() returns. The same behaviour happens under MacOS.

But I agree it may be logical that cmd-Q has the same effect as closing all windows
by hand.

Mathieu Peyréga

unread,
Jan 25, 2018, 9:54:33 AM1/25/18
to fltk.general
At least i'm happy that I have not done things the wrong way :-) and explanation of the behaviour has a rationnal...

Could a in between solution be the implementation of a global function in fltk to set the behaviour to one or another ?

That would allow not breaking existing apps and would allow users to switch to what I consider a more portable behaviour (and no, I don't want to maintain a separate branch of fltk to have that... my question in a post above was just for a private try)

At least documentation should be updated a specify that the Fl::run() should be that very last call of your "main".

Regards

Ian MacArthur

unread,
Jan 25, 2018, 10:29:34 AM1/25/18
to fltkg...@googlegroups.com


> On 25 Jan 2018, at 12:25, Albrecht Schlosser wrote:
>
> Note: it is possible to use Fl::run() in a loop to simulate multiple invocations of a program or something similar:
>
> for (int i = 0; i < 10; i++) {
> printf("Loop no %d:\n", i + 1);
> // do something with index i
> window[i].show();
> Fl::run();
> }
>
> On all platforms closing the window with the close button would repeat the loop, but on macOS using cmd-q would terminate the program. Is this considered consistent, or should it be changed?


Well, here’s the thing (and I freely admit I do not know what is “right” here, and am happy to be wrong... This is based on how I “experience” OSX to work, mixed with what I *think* I read in some Apple doc, somewhere, once upon a time...)

In my head, Cmd+Q and red-X are *not* the same thing on OSX.

The red-X means “close the current window”, but Cmd+Q means “terminate this app”.

So in this example, where we pop a series of windows in sequence, then if I click red-X, I expect that the next window in the sequence can appear.

But if I click Cmd+Q, then I am saying I *really* want to quit, so if another window then pops up, I’d be surprised by that.

So... this is why I *think* the current fltk behaviour is actually "correct" on OSX, but maybe it is not? Or, if not “correct” then perhaps "least surprising".
I really do not know.


Other platforms are different, in that they do not (at least in my imagination!) have a key combo quite like Cmd+Q. For example, Windows has Alt+F4, but to me that does mean “close current window” and so is *the same* as clicking the red-X; but it *is not the same* as Cmd+Q.

And... I can’t even remember what key combos I use with an X11 WM these days...




Ian MacArthur

unread,
Jan 25, 2018, 10:42:02 AM1/25/18
to fltkg...@googlegroups.com


> On 25 Jan 2018, at 13:34, Mathieu Peyréga wrote:
>
> At least documentation should be updated a specify that the Fl::run() should be that very last call of your "main”.


No, I don’t think that’s a good idea - there’s no reason for Fl::run() to be the last thing in main(), (though it often is in many, possibly most, programs) and I frequently do “stuff” after Fl::run() returns (for example unloading private fonts, as discussed in another thread just now.)

Under OSX, there’s the special case of handling Cmd+Q though, which departs from the "normal” behaviour and is unlike X11 or WIN32 cases.

Oddly, this has not caught me out in practice - though this appears to be by happy accident rather than by design, in that I do a lot of cleanup in the window close CB anyway and so have made it work without necessarily knowing why!



Greg Ercolano

unread,
Jan 25, 2018, 1:21:21 PM1/25/18
to fltkg...@googlegroups.com
On 01/25/18 05:34, Mathieu Peyréga wrote:
> At least i'm happy that I have not done things the wrong way :-) and explanation of the behaviour has a rationnal...

Yes, and I'm kinda +1 with you on modifying FLTK to handle
the %Q consistently, though some details are brought up that
perhaps bear consideration before we make a change.

> Could a in between solution be the implementation of a global function in fltk to set the behaviour to one or another ?

I would agree that any OS specific behavior desirable to
/less than/ 50% of apps should be an option with the default turned off.
If /more than/ 50%, an option with the default turned on.

And the way to use this option should be consistent across
all platforms.

This, as opposed to recommending an #ifdef or a special coding
practice that might not have an obvious intention; a clear
option flag setting would IMHO be a better thing.

But I defer to the other devs here, as they're digging into
this issue pretty deeply in this thread, more than I have
time to do at present (I'm on a time intensive project)

> At least documentation should be updated a specify that the Fl::run()
> should be that very last call of your "main".

Most example code shows using Fl::run() as the last call in main(),
but it's not a requirement; your approach with Fl::wait() in a loop
is certainly a valid option, and indeed some of our own test programs
show this alternative use, without calling Fl::run() at all.


Mathieu Peyréga

unread,
Jan 25, 2018, 3:43:46 PM1/25/18
to fltk.general
I confirm that changing line 1505 of Fl_cocoa.mm from

  NSApplicationTerminateReply reply = NSTerminateNow;

to

  NSApplicationTerminateReply reply = NSTerminateCancel;

in order to have a behaviour where the Fl::run() loop exit normaly and main keeps running until the end.

I would really appreciate if a global function is added to mainstream version in order to switch from one behaviour to the other.

Regards

Mathieu

Manolo

unread,
Jan 26, 2018, 3:43:49 AM1/26/18
to fltk.general
Attached termination.cxx illustrates two methods to write platform-independent
FLTK source code and support cmd-Q under the MacOS platform
(toggle between the two "#define VERSION1  1/0" lines therein).

The window callback-based method would make most sense
when the termination code is conceptually related to the closure of a
window.

The atexit-based method would make most sense when the termination
code is conceptually related to appllcation termination.

termination.cxx

Albrecht Schlosser

unread,
Jan 26, 2018, 2:16:21 PM1/26/18
to fltk.general
All,

I'm still concerned about cross platform compatibility and the lack of
control a programmer seems to have when the macOS specific Command-Q
keyboard shortcut is used as it is now. I have to admit that I didn't
find much documentation about Command-Q or what it should do. The "best"
;-) documentation I found was in "Mac keyboard shortcuts":

Command-Q Quit the app.

https://support.apple.com/en-us/HT201236

What does that mean? I think this should be an action comparable with
the application menu in many apps I'm used to: CTRL-Q = "Quit". Note
that it doesn't say "Abort the app" which would IMHO mean an emergency
exit. "Quit the app" seems more like a graceful exit.

Although a user often wouldn't mind I think the current behavior of
Command-Q is bad since the app programmer doesn't have control over the
behavior, particularly if the app has a menu that uses CTRL-Q to quit
the app which likely calls a menu callback function so the programmer
gets control _before_ any windows are closed. In this menu callback
(quit function) a programmer can check things like open documents, open
network connections, and ask the user if he wants to quit. Only if the
user confirms the application does its cleanup and quits.

The OP reported that global destructors were not called when he used
Command-Q. Is this really true? Besides the fact that the programmer has
no control over the quitting process (other than not to close the
windows when the window callbacks are called) this seems to be *really*
bad to me. It sounds as if _exit() or something similar to `kill -9' is
called to abort the program right away after the windows are closed. Not
running global destructors (of static objects) seems to be a bad
decision. Again, is this really true? Can anybody confirm this? I would
test it, but I don't have a Mac at hand. ;-)

Given this report of a "hard exit" or abort I have to ask: Manolo, did
you confirm with your posted hello program that the

void termination() {
printf("Run termination code here\n");
}

that is established with 'atexit(termination)' is really called when
Command-Q is used (and VERSION1 is defined as 1)? I assume you did, but
just for confirmation, to be absolutely sure. I'm asking because
functions registered with atexit() are usually not called when you call
_exit() as opposed to calling exit(). [3]

I don't know what macOS does internally after it receives the return
value NSTerminateNow, it may be something totally different.

That all said, I think that we should have a user (programmer) option to
modify the handling of Command-Q as Greg proposed and the OP confirmed.


A possible solution:

I believe the best we could do is to add a function to register a "Quit
Handler" [1] so the programmer gets control _before_ FLTK's internal
Command-Q processing sends FL_CLOSE events to all windows. This
registered handler function could return an int value to the internal
processing to tell what to do next, something like:

0: not handled - continue sending FL_CLOSE events as it is now (and in
FLTK 1.3.x), return NSTerminateNow (IMHO not recommended).

1: handled - send FL_CLOSE events to all windows, but don't return
NSTerminateNow (return NSTerminateCancel instead). This would close all
windows and return from Fl::run() or equivalent (Fl::wait()) and let the
programmer do the cleanup after Fl::run(). This would be compatible with
the normal rundown of an app when the user closes all windows. Hence
Command-Q would be a simple shortcut for closing all windows, but the
order of the windows getting FL_CLOSE events may be arbitrary.

-1 (or 2 or whatever): handled - abort the Command-Q processing
immediately, don't send FL_CLOSE events and return NSTerminateCancel.
This could be used for instance if there are open files and the user did
not confirm the question (popup window) to save the file(s) and exit,
maybe because he typed Command-Q accidentally. [2]

Of course there could be other options I didn't think of yet. The main
reason to have such a function is to give the application _programmer_
more control over quitting the app (at least if they decide to register
a handler). The current behavior to send FL_CLOSE events can also lead
to closing windows in a suboptimal order: for instance closing a main
window before closing another modal window that is related to the main
window or closing windows in any arbitrary order. If the programmer
would have taken care that a window is NOT closed by receiving the
FL_CLOSE event, i.e. in its window callback, then there would be a
chance that all the other windows have been closed before the last
window prevents closing of the app. This looks like asking for trouble...

I'm not sure what the best default value of the returned value of the
"Quit Handler" would be (i.e. the behavior of Command-Q in FLTK if no
handler was registered). I think I'd prefer the _modified_ behavior
(compared to FLTK 1.3.4 and 1.4-current) as if the handler returned 1 in
my proposal (above). This would give the application the best cross
platform behavior: close all windows as if the user closed them all
manually, but return from Fl::run() so the application can run cleanup
code after Fl::run() and exit normally which calls all global / static
destructors.

I'm open for comments and other suggestions. All comments are welcome.

Here is a simple example of a "Quit Handler":

// This is called only under macOS from the system menu Command-Q
int quit_handler( .. ) {
quit_cb(); // call my program's "quit" menu callback
// Note: quit_cb() would close all windows (or not if
// the user didn't confirm to quit for some reason)
return -1; // don't send FL_CLOSE, let me control the exit
}


-----
[1] I propose to make this a cross platform function and call it "Quit
Handler" so we can extend it to other platforms than macOS if necessary.
Documentation should explain that this handler is currently only used by
the macOS Command-Q processing. Maybe other platforms like IOS or
Android would also have such a Command-Q key?

[2] When I searched for Command-Q I found *lots* of questions from users
how to *disable* Command-Q, particularly in Safari when they wanted to
close a tab with Command-W but accidentally hit Command-Q where Safari
closes all tabs and quits. IMHO we should not add FLTK apps to this
typical user problem. ;-)

[3] From `man _exit' (Linux, if that matters):

"The function _exit() is like exit(3), but does not call any functions
registered with atexit(3) or on_exit(3)."

Manolo

unread,
Jan 27, 2018, 12:57:15 AM1/27/18
to fltk.general


On Friday, 26 January 2018 20:16:21 UTC+1, Albrecht Schlosser wrote:


Given this report of a "hard exit" or abort I have to ask: Manolo, did
you confirm with your posted hello program that the

void termination() {
   printf("Run termination code here\n");
}

that is established with 'atexit(termination)' is really called when
Command-Q is used (and VERSION1 is defined as 1)? I assume you did, but
just for confirmation, to be absolutely sure. I'm asking because
functions registered with atexit() are usually not called when you call
_exit() as opposed to calling exit(). [3]

Yes, I confirm.
[if that was not the case, my post would make no sense]

Manolo

unread,
Jan 27, 2018, 1:53:59 AM1/27/18
to fltk.general
Trying to add a few more relevant elements to the discussion.

In my view, cmd-Q is expected to trigger a clean exit of the app. Typically, the user sees
dialog windows asking for confirmation, or allowing modified data to be saved.
These dialogs are usually handled by the window callbacks (which are also triggered
by cmd-Q). If there are global operations to be done before exiting the app,
they also should be run.

The OP app assumes the termination code is run only after completion of the
Fl::run() loop. The problem is that it does not run after cmd-Q because the app terminates
before. The posted modified hello program shows two cross-platforms
ways to have termination code run: put it in a window callback or an atexit callback.

I checked that an object created on the stack in main doesn't run its destructor
after cmd-Q because main() never returns. Thus, objects whose destructors
must be run should be accessible for destruction by the termination callback.

Two elements suggest to me that cmd-Q is somewhat different from closing all windows:

1) as Ian noticed, a program that would do
  open a window
  Fl::run()
  open another window
  Fl::run()
  exit(0)
should not open the second window if cmd-Q is typed while the 1st window runs.

2) a MacOS program can run without any mapped window: when in the foreground,
its system menu bar is visible and typically allows to create a new window.
In such a program, when the last window is closed by a window-close command,
the app continues with its menu bar only, whereas when it's closed by cmd-Q, the app
should stop (provided the window's callback confirmed that the window can be closed).
This behaviour is very easily obtained in FLTK :
    while (true) Fl::wait();

Manolo

unread,
Jan 27, 2018, 2:32:39 AM1/27/18
to fltk.general
One more element:

FLTK deactivates the "Quit program" item of the system menu bar
(thus, cmd-Q is not recognized as a shortcut) when a modal window runs,
and reactivates it when the modal window closes.

Manolo

unread,
Jan 28, 2018, 4:10:30 AM1/28/18
to fltk.general
FLTK's global shortcut mechanism allows the program to handle cmd-Q events
before MacOS attempts to handle it. See attached quit_handler.cxx for a toy example.

This global shortcut handler gives the program :

- Cross-platform support of ctrl-Q/cmq-Q keystroke.

- Because the handler returns 1 after cmd-Q, the MacOS
default procedure discussed at length here doesn't run.
Thus, if all windows close, Fl::run() will return and termination
code placed after that will run.

- A MacOS program that needs to keep running without any
mapped window should rather not register this handler,
and use FLTK's default cmd-Q processing for the MacOS platform.

At this point, my impression is that there's no need for FLTK to define a
new kind of handler, because the global shortcut mechanism
allows the program to fully control how ctrl-Q/cmd-Q keystrokes are
processed.

All comments and opinions welcome.


quit_handler.cxx

Ian MacArthur

unread,
Jan 28, 2018, 4:41:26 PM1/28/18
to fltkg...@googlegroups.com
I am inclined to agree.




Albrecht Schlosser

unread,
Jan 29, 2018, 10:59:16 AM1/29/18
to fltkg...@googlegroups.com
On 28.01.2018 22:41 Ian MacArthur wrote:
>
>> On 28 Jan 2018, at 09:10, Manolo wrote:
>>
>> FLTK's global shortcut mechanism allows the program to handle cmd-Q events
>> before MacOS attempts to handle it. See attached quit_handler.cxx for a toy example.

That's good news. Thank you, Manolo.

>> This global shortcut handler gives the program :
>>
>> - Cross-platform support of ctrl-Q/cmq-Q keystroke.
>>
>> - Because the handler returns 1 after cmd-Q, the MacOS
>> default procedure discussed at length here doesn't run.
>> Thus, if all windows close, Fl::run() will return and termination
>> code placed after that will run.

Okay, but this needs code changes in existing applications.

>> - A MacOS program that needs to keep running without any
>> mapped window should rather not register this handler,
>> and use FLTK's default cmd-Q processing for the MacOS platform.

I don't know what usage such a program would have. But if there is a
real need to have such a program: would this also work if we changed

>> At this point, my impression is that there's no need for FLTK to define a
>> new kind of handler, because the global shortcut mechanism
>> allows the program to fully control how ctrl-Q/cmd-Q keystrokes are
>> processed.
>>
>> All comments and opinions welcome.
>>
>
>
> I am inclined to agree.

So far I can agree as well that we don't need an additional handler to
achieve what I proposed to program a clean exit on Command-Q although
one or the other way would definitely require program changes. This is
acceptable though since we introduce a new feature. Okay. But...

The remaining important issue is compatibility with existing programs.
Short info: I still believe that we should change the existing behavior
to send NSTerminateCancel instead of NSTerminateCancel.

Please see the new thread I opened in fltk.coredev for further
discussion: "RFC: macOS Command-Q behavior".
https://groups.google.com/d/msg/fltkcoredev/cMsoMhbfTnE/0oeVCHFqBAAJ

Albrecht Schlosser

unread,
Jan 29, 2018, 11:13:10 AM1/29/18
to fltkg...@googlegroups.com
On 29.01.2018 16:59 Albrecht Schlosser wrote:

[sorry for an incomplete sentence, continuing below...]

>>> - A MacOS program that needs to keep running without any
>>> mapped window should rather not register this handler,
>>> and use FLTK's default cmd-Q processing for the MacOS platform.
>
> I don't know what usage such a program would have. But if there is a
> real need to have such a program: would this also work if we changed

... NSTerminateNow to NSTerminateCancel ? I assume yes, but please test
and confirm in the new thread in fltk.coredev.
Reply all
Reply to author
Forward
0 new messages