exiting from Fl::run()

90 views
Skip to first unread message

Edward Vigmond

unread,
May 28, 2020, 9:47:16 AM5/28/20
to fltk.general
Hello

I was wondering if it would be possible to have an FLTK exit function that closes all the FLTK windows and returns from Fl:run().
The issue is that calling system exit(), although simple, does not return from Fl::run()  and destructors do not get called. I could keep track of all open windows and close them but I have a lot of windows to check. 

In particular, on the Mac, I need to close message queues which are a precious resource.

Philip Rose

unread,
May 28, 2020, 11:53:25 AM5/28/20
to fltkg...@googlegroups.com

I don’t find that much of a hassle. These are declared in main.cpp.

 

// Any sub-windows opened

set<Fl_Window*> sub_windows_;

 

// Add free-standing windows to remeber to close them when we close the main window

void add_sub_window(Fl_Window* w) {

     // Don't add the same window twice!

     if (sub_windows_.find(w) == sub_windows_.end()) {

           sub_windows_.insert(w);

     }

}

 

// Remove free-standing menu from list

void remove_sub_window(Fl_Window* w) {

     sub_windows_.erase(w);

}

 

Call the first when you create the window and the second when you delete it.

 

Have the following code in the main window callback (on close):

 

           // delete all additional windows created - note the status file viewer is one.

           for (auto it = sub_windows_.begin(); it != sub_windows_.end(); it++) {

                (*it)->clear();

                delete* it;

           }

 

Phil.

 

Sent from Mail for Windows 10

--
You received this message because you are subscribed to the Google Groups "fltk.general" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkgeneral...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/fltkgeneral/01c1a8ab-fafa-4a3d-aeae-eebedc6b26de%40googlegroups.com.

 

Edward Vigmond

unread,
May 31, 2020, 6:47:28 AM5/31/20
to fltk.general
Hi

Thanks but this is not ideal. I have many different places where I add windows and many kinds of windows, so I would need to add the line of code to all of them, which is error prone, and cumbersome to change.

If FL already has a list of windows, why not use it?

Cheers
Ed

To unsubscribe from this group and stop receiving emails from it, send an email to fltkg...@googlegroups.com.

imm

unread,
May 31, 2020, 9:18:26 AM5/31/20
to general fltk
On Sun, 31 May 2020, 11:47 Edward Vigmond, wrote:
Hi

Thanks but this is not ideal. I have many different places where I add windows and many kinds of windows, so I would need to add the line of code to all of them, which is error prone, and cumbersome to change.

If FL already has a list of windows, why not use it?

Cheers
Ed



That's basically what I was trying to show with my Fl::first_window() example.

Though Gonzalo says my example is wrong (it may well be, I was winging it from memory!) and you need to combine ::first_window() with FL::next_window() to walk the list properly.

Did you try that, and does it work for you?
It's worked well for me in the past 
-- 
Ian
From my Fairphone FP3

Greg Ercolano

unread,
May 31, 2020, 9:50:20 AM5/31/20
to fltkg...@googlegroups.com
On 2020-05-31 06:18, imm wrote:
> On Sun, 31 May 2020, 11:47 Edward Vigmond, wrote:
> If FL already has a list of windows, why not use it?
>
> That's basically what I was trying to show with my Fl::first_window() example.
>
> Though Gonzalo says my example is wrong (it may well be, I was winging it from memory!) and you need to combine ::first_window() with FL::next_window() to walk the list properly.

Ya, if you just hide() all the windows that Fl::first_window()
and Fl::next_window(win) find, Fl::run() should return.

Fl::run() returns when no windows are show()n.

Should be as simple as (I'm winging it too, sorry):

void close_all_windows() {
Fl_Window *win = Fl::first_window();
while ( win ) {
win->hide();
win = Fl::next_window(win);
}
}

We could probably add that to the Fl class, assuming there isn't
such a thing already. But you can certainly write and call that yourself.

Albrecht Schlosser

unread,
May 31, 2020, 2:40:59 PM5/31/20
to fltkg...@googlegroups.com
On 5/31/20 3:18 PM imm wrote:
> On Sun, 31 May 2020, 11:47 Edward Vigmond, wrote:
>
> If FL already has a list of windows, why not use it?
>
> That's basically what I was trying to show with my Fl::first_window()
> example.
>
> Though Gonzalo says my example is wrong (it may well be, I was winging
> it from memory!) and you need to combine ::first_window() with
> FL::next_window() to walk the list properly.

I didn't see this. Where did he say it? In a private mail, maybe?

Anyway, see my other reply: IMO your code is correct (safer) and using
Fl::next_window() would not be reliable (safe) in this context.

Ian MacArthur

unread,
May 31, 2020, 5:19:00 PM5/31/20
to fltkg...@googlegroups.com


> On 31 May 2020, at 19:40, Albrecht Schlosser wrote:
>
> On 5/31/20 3:18 PM imm wrote:
>> On Sun, 31 May 2020, 11:47 Edward Vigmond, wrote:
>> If FL already has a list of windows, why not use it?
>> That's basically what I was trying to show with my Fl::first_window() example.
>> Though Gonzalo says my example is wrong (it may well be, I was winging it from memory!) and you need to combine ::first_window() with FL::next_window() to walk the list properly.
>
> I didn't see this. Where did he say it? In a private mail, maybe?

Huh, oh yeah. I hadn’t noticed... Well anyway...

>
> Anyway, see my other reply: IMO your code is correct (safer) and using Fl::next_window() would not be reliable (safe) in this context.

Yes, I agree, it does look like I was actually “right” the first time; now that I’m actually at a real computer I checked, and first_window() does seem to be the first *shown* window, so looping on it seems to be the right thing in this case.

I’d also said it was what I’d done in the past; so I went looking for an actual worked example; the first one I found was as follows:

/*****************************************************************************************/
static void quit_cb(Fl_Widget *)
{
main_win->hide();
if (plot_win) plot_win->hide();
if (slice_win) slice_win->hide();
if (flat_win) flat_win->hide();

Fl_Window *pw = Fl::first_window();
while (pw)
{
pw->hide();
pw = Fl::first_window();
}
} // quit_cb

/*****************************************************************************************/


This example is, I think, interesting in that it would appear that I started out keeping some kind of track of which windows I had open, but after the first 3 or 4 I got bored with that and just had it loop on first_window() to close them all.

This particular code analyses some logged data and does (potentially) open *a lot* of sample windows. FWIW, it does seem to reliably close them all, too, so I believe this works!





Greg Ercolano

unread,
May 31, 2020, 6:47:24 PM5/31/20
to fltkg...@googlegroups.com
On 2020-05-31 14:18, Ian MacArthur wrote:
> This particular code analyses some logged data and does (potentially) open *a lot*
> of sample windows. FWIW, it does seem to reliably close them all, too, so I believe this works!

So Ian, Albrecht, should we add an Fl::close_all()?

I think it would satisfy the OP's initial request, and would show the proper way
to loop through closing all windows by example, which could be shown as \code
in the doxygen docs.

Albrecht Schlosser

unread,
Jun 1, 2020, 1:10:49 PM6/1/20
to fltkg...@googlegroups.com
On 5/31/20 11:18 PM Ian MacArthur wrote:
>
>> On 31 May 2020, at 19:40, Albrecht Schlosser wrote:
>>
>> On 5/31/20 3:18 PM imm wrote:
>>> On Sun, 31 May 2020, 11:47 Edward Vigmond, wrote:
>>> If FL already has a list of windows, why not use it?
>>> That's basically what I was trying to show with my Fl::first_window() example.
>>> Though Gonzalo says my example is wrong (...) and you need to combine ::first_window() with FL::next_window() to walk the list properly.
>> ...
>> ... IMO your code is correct (safer) and using Fl::next_window() would not be reliable (safe) in this context.
FTR / FYI:

Coming back to this after actually reading the docs that state for

Fl_Window * Fl::next_window(const Fl_Window *window)

"param[in] window must be shown and not NULL".

This means that you MUST NOT call Fl::next_window(win) after 'win' has
been hidden because it would no longer be /shown/.

In fact (i.e. in current code) Fl::next_window(win) crashes if 'win' is
currently not shown (segmentation fault under Linux).


Question to devs and users: Should we make this more user friendly?

I found two IMHO useful options:

(1) Call Fl::error() with an error message and continue (returning NULL), or

(2) Call Fl::fatal() with an error message, which terminates the program.

Both options are more fault tolerant and display at least a qualified
error message instead of just crashing (when dereferencing a NULL pointer).

(3) Would be to leave it as-is: crash w/o any error message
(segmentation fault).


Proposal with an example: I modified test/clock.cxx to call a callback
whenever one of its two windows is closed by the user. This callback is
intended to close both windows simultaneously (a similar issue as
described by the OP and by Ian).

Here's my test callback with the WRONG call:

void close_cb(Fl_Widget *w, void *v) {
Fl_Window *win = (Fl_Window *)w;
fprintf(stderr, "close_cb() for window %p : '%s'\n", win, win->label());
Fl_Window *p = Fl::first_window();
while (p) {
fprintf(stderr, "closing window %p : '%s'\n", p, p->label());
p->hide();
p = Fl::next_window(p); // *** WRONG ***
}
fprintf(stderr, "Done.\n");
return;
}

This callback would crash with a segmentation fault with current FLTK
code - the user would need a debugger and likely a view of the call
stack to find out what happened.

With the modified FLTK code -- using Fl::error(), as in (1) above -- the
program continues with an error message:

(A) User closes the first window:

close_cb() for window 0x7fffffffd850 : 'Fl_Round_Clock'
closing window 0x7fffffffd850 : 'Fl_Round_Clock'
Fl::next_window(window) failed: window (0x7fffffffd850) is not shown.
Done.

Note: this leaves the second window open but continues program execution.

(B) User closes the 2nd window:

close_cb() for window 0x7fffffffd740 : 'Fl_Clock'
closing window 0x7fffffffd740 : 'Fl_Clock'
Fl::next_window(window) failed: window (0x7fffffffd740) is not shown.
Done.

The error message shows which function failed and which window was at
fault. This should help to find the bug.


What do you think? You can vote for solution (1), (2) or (3) == leave as-is.

My favorite is (1).


PS: I'm going to add the *correct* callback as an example to
test/clock.cxx ...

Done: commit a686e6eafd

Albrecht Schlosser

unread,
Jun 1, 2020, 1:17:47 PM6/1/20
to fltkg...@googlegroups.com
On 6/1/20 12:47 AM Greg Ercolano wrote:
> On 2020-05-31 14:18, Ian MacArthur wrote:
>> This particular code analyses some logged data and does (potentially) open *a lot*
>> of sample windows. FWIW, it does seem to reliably close them all, too, so I believe this works!
>
> So Ian, Albrecht, should we add an Fl::close_all()?

I wouldn't object, but since it's such a simple [1] function we could
also just let it up to the user.

However, *if* we added a method, its name should IMHO be more specific,
for instance Fl::hide_all_windows() or Fl::hide_all(). It's IMHO better
to use 'hide' rather than 'close' because that's what would be done
under the hood, although closing a window is a common term. Personally
I'd prefer the longer version 'Fl::hide_all_windows()'.

> I think it would satisfy the OP's initial request, and would show the proper way
> to loop through closing all windows by example, which could be shown as \code
> in the doxygen docs.

[1] As we've seen in the discussion in this thread it is maybe not that
simple because the list of shown windows is being manipulated when a
window is hidden.

My proposal would be to add some comments and example code to methods
Fl::first_window() and Fl::next_window(), but as I said above, a method
that does this all internally would be fine as well.

Note also that I did just commit an addition to test/clock.cxx that
includes the correct way to close all open windows when the user closes
one window, using the proper loop with Fl::first_window().

rich little

unread,
Jun 1, 2020, 1:25:43 PM6/1/20
to fltk.general
I vote for Number 1.

Greg Ercolano

unread,
Jun 1, 2020, 1:30:49 PM6/1/20
to fltkg...@googlegroups.com
On 2020-06-01 10:17, Albrecht Schlosser wrote:
> On 6/1/20 12:47 AM Greg Ercolano wrote:
>> On 2020-05-31 14:18, Ian MacArthur wrote:
>>> This particular code analyses some logged data and does (potentially) open *a lot*
>>> of sample windows. FWIW, it does seem to reliably close them all, too, so I believe this works!
>>
>> So Ian, Albrecht, should we add an Fl::close_all()?
>
> I wouldn't object, but since it's such a simple [1] function we could
> also just let it up to the user.
>
> However, *if* we added a method, its name should IMHO be more specific,
> for instance Fl::hide_all_windows() or Fl::hide_all(). It's IMHO better
> to use 'hide' rather than 'close' because that's what would be done
> under the hood, although closing a window is a common term. Personally
> I'd prefer the longer version 'Fl::hide_all_windows()'.

Ya, I'd be +1 for such names.

But definitely +1 for adding a function, so that the user /doesn't/
have to try to figure out the right way to do this, as I think most
people would attempt to use my first mentioned technqiue, using
first_window() and next_window().. so the new function would not
only be useful for the OP, but would also demonstrate the correct
way to do it.

> My proposal would be to add some comments and example code to methods
> Fl::first_window() and Fl::next_window(), but as I said above, a method
> that does this all internally would be fine as well.

Yes, that'd be good place for a \code example, certainly.

> Note also that I did just commit an addition to test/clock.cxx that
> includes the correct way to close all open windows when the user closes
> one window, using the proper loop with Fl::first_window().

That's good too, but I think in the docs for e.g. first_window()
and next_window() would be best, since it's not obvious to the
outside user why using first_window/next_window is bad.

There might actually be a way to use next_window(), by
calling next_window() to get the pointer to the next window
/before/ calling hide.

Albrecht Schlosser

unread,
Jun 1, 2020, 1:57:23 PM6/1/20
to fltkg...@googlegroups.com
Note: I'll reply to / summarize the other points of your post later...

On 6/1/20 7:30 PM Greg Ercolano wrote:

> There might actually be a way to use next_window(), by
> calling next_window() to get the pointer to the next window
> /before/ calling hide.

Sure, I thought of this too. But, again, that would not work under
certain circumstances - which are currently probably not possible [1],
but anyway:

Imagine two windows, window A being a normal window, and window B being
a window "modal to" window A, for instance an open menu window or
another modal window.

Now, when you close window A, some systems (may) close the modal window
B too [1]. If, by accident, window B is Fl::next_window(A), then you
would get this and still be wrong. To make this clear:

Fl_Window *win = Fl::fist_window(); // returns any window
Fl_Window *nextwin = 0; // stores the "next" window

while (win) {
// assume win == A
nextwin = Fl::next_window(win); // assume nextwin == B
win->hide(); // closes A *and* B
win = nextwin; // now win == B is a closed window
}

I'm pretty sure that this is unrealistic in the current state of FLTK,
but you can never know. Be defensive, don't rely on "undefined"
behavior. That's all I can say to this...

-----

[1] AFAICT this is currently an open issue because FLTK wouldn't work
well with this scenario and let window B in the list of open windows not
"knowing" that the system closed window B as well -- but let's put this
issue aside for now.

Ian MacArthur

unread,
Jun 1, 2020, 5:13:35 PM6/1/20
to fltkg...@googlegroups.com


> On 1 Jun 2020, at 18:10, Albrecht Schlosser wrote:
>
>
> Question to devs and users: Should we make this more user friendly?


Yes, it seems to me that we should...

>
> I found two IMHO useful options:
>
> (1) Call Fl::error() with an error message and continue (returning NULL), or
>
> (2) Call Fl::fatal() with an error message, which terminates the program.
>
> Both options are more fault tolerant and display at least a qualified error message instead of just crashing (when dereferencing a NULL pointer).
>
> (3) Would be to leave it as-is: crash w/o any error message (segmentation fault).


Hmm, OK. How about this more complicated option:

(4) Given some call like...

win2 = Fl::next_window(win1);

Then

IF (win1 == NULL) OR (win2 == NULL)
return Fl::first_window(); // Rather than segfault. Might still be NULL if actually at the last window

ELSE
return win2;


Does that make sense?

I don’t *think* that changes the behaviour for the “normal” case, but would avoid the segfault in the failing case, and likely produce something like “expected” behaviour for most users?

I think. Maybe...



Ian MacArthur

unread,
Jun 1, 2020, 5:30:56 PM6/1/20
to fltkg...@googlegroups.com


> On 1 Jun 2020, at 22:13, Ian MacArthur wrote:
>
> Hmm, OK. How about this more complicated option:
>
> (4) Given some call like...
>
> win2 = Fl::next_window(win1);
>
> Then
>
> IF (win1 == NULL) OR (win2 == NULL)
> return Fl::first_window(); // Rather than segfault. Might still be NULL if actually at the last window
>
> ELSE
> return win2;
>
>
> Does that make sense?
>
> I don’t *think* that changes the behaviour for the “normal” case, but would avoid the segfault in the failing case, and likely produce something like “expected” behaviour for most users?
>
> I think. Maybe...

Here’s a possible implementation for same - I haven’t tested this, need to go (child care issue...)

We currently have, in Fl.cxx:
/**
Returns the next top-level window in the list of shown() windows.
You can use this call to iterate through all the windows that are shown().
\param[in] window must be shown and not NULL
*/
Fl_Window* Fl::next_window(const Fl_Window* window) {
Fl_X* i = Fl_X::i(window)->next;
return i ? i->w : 0;
}


So we could change that to something like...


Fl_Window* Fl::next_window(const Fl_Window* window) {
if (!window || !window->shown()) return Fl::first_window(); // This’ll get some “next” shown window, if any...
Fl_X* i = Fl_X::i(window)->next;
return i ? i->w : Fl::first_window(); // Again, this’ll get some “next” shown window, if any exist...
}

And I *think* that would just about get it...?



Albrecht Schlosser

unread,
Jun 1, 2020, 5:31:16 PM6/1/20
to fltkg...@googlegroups.com
On 6/1/20 11:13 PM Ian MacArthur wrote:
>
>> On 1 Jun 2020, at 18:10, Albrecht Schlosser wrote:
>>
>>
>> Question to devs and users: Should we make this more user friendly?
>
>
> Yes, it seems to me that we should...

OK.

>> I found two IMHO useful options:
>>
>> (1) Call Fl::error() with an error message and continue (returning NULL), or
>>
>> (2) Call Fl::fatal() with an error message, which terminates the program.
>>
>> Both options are more fault tolerant and display at least a qualified error message instead of just crashing (when dereferencing a NULL pointer).
>>
>> (3) Would be to leave it as-is: crash w/o any error message (segmentation fault).
>
>
> Hmm, OK. How about this more complicated option:
>
> (4) Given some call like...
>
> win2 = Fl::next_window(win1);
>
> Then
>
> IF (win1 == NULL) OR (win2 == NULL)
> return Fl::first_window(); // Rather than segfault. Might still be NULL if actually at the last window
>
> ELSE
> return win2;
>
>
> Does that make sense?
>
> I don’t *think* that changes the behaviour for the “normal” case, but would avoid the segfault in the failing case, and likely produce something like “expected” behaviour for most users?

This "sounds" like "goto top" which I'd really like to avoid. This *may*
create an infinite loop if something really goes awry. Admitted, this
needs some serious coding errors, but my gut feeling is that this would
ask for trouble.

Anyway, everything that tries to "repair" a user program error can be
problematic. I don't know. Just returning NULL would at least terminate
the loop. We're not talking only about someone calling Fl_Window::hide()
in the case we're thinking of - it can be something totally different!

Albrecht Schlosser

unread,
Jun 1, 2020, 5:39:23 PM6/1/20
to fltkg...@googlegroups.com
Summarizing, trying to come to a quick end of this topic...

On 6/1/20 7:30 PM Greg Ercolano wrote:
> On 2020-06-01 10:17, Albrecht Schlosser wrote:
>>
>> However, *if* we added a method, its name should IMHO be more specific,
>> for instance Fl::hide_all_windows() or Fl::hide_all(). It's IMHO better
>> to use 'hide' rather than 'close' because that's what would be done
>> under the hood, although closing a window is a common term. Personally
>> I'd prefer the longer version 'Fl::hide_all_windows()'.
>
> Ya, I'd be +1 for such names.

...

> But definitely +1 for adding a function, so that the user /doesn't/
> have to try to figure out the right way to do this, ...

OK, let's add a function and call it Fl::hide_all_windows(). I can do
this if we agree.

>> My proposal would be to add some comments and example code to methods
>> Fl::first_window() and Fl::next_window(), but as I said above, a method
>> that does this all internally would be fine as well.
>
> Yes, that'd be good place for a \code example, certainly.

OK, I'll do this as well.

>> Note also that I did just commit an addition to test/clock.cxx that
>> includes the correct way to close all open windows when the user closes
>> one window, using the proper loop with Fl::first_window().
>
> That's good too, but I think in the docs for e.g. first_window()
> and next_window() would be best, since it's not obvious to the
> outside user why using first_window/next_window is bad.

Yep, see above.

Anything else we need to think of?

imm

unread,
Jun 2, 2020, 5:30:33 AM6/2/20
to general fltk
On Mon, 1 Jun 2020 at 22:31, Albrecht Schlosser wrote:
>
> This "sounds" like "goto top" which I'd really like to avoid. This *may*
> create an infinite loop if something really goes awry. Admitted, this
> needs some serious coding errors, but my gut feeling is that this would
> ask for trouble.
>
> Anyway, everything that tries to "repair" a user program error can be
> problematic. I don't know. Just returning NULL would at least terminate
> the loop. We're not talking only about someone calling Fl_Window::hide()
> in the case we're thinking of - it can be something totally different!

Yeah, fair enough.

However, I do think we could safely "harden" this code so it less
likley to segfault, e.g.:


Fl_Window* Fl::next_window(const Fl_Window* window) {
if (!window || !window->shown()) return 0; // Do not try to
dereference a non-shown window
Fl_X* i = Fl_X::i(window)->next;
return i ? i->w : 0;
}

Just to make sure that next_window doesn't choke and die if passed a
NULL or hidden window entry...

Albrecht Schlosser

unread,
Jun 2, 2020, 7:10:25 AM6/2/20
to fltkg...@googlegroups.com
On 6/2/20 11:30 AM imm wrote:
>
> However, I do think we could safely "harden" this code so it less
> likley to segfault, e.g.:
>
>
> Fl_Window* Fl::next_window(const Fl_Window* window) {
> if (!window || !window->shown()) return 0; // Do not try to
> dereference a non-shown window
> Fl_X* i = Fl_X::i(window)->next;
> return i ? i->w : 0;
> }
>
> Just to make sure that next_window doesn't choke and die if passed a
> NULL or hidden window entry...

Yep, something similar like that was my plan, and I proposed to add an
error message with Fl::error() and returning NULL or Fl::fatal() which
would terminate the program. See my post from Mon Jun 1 17:10:51 2020 UTC.

I think your proposal should replace my posted option "leave as-is" so
that we have these options:

(1) Call Fl::error() with an error message and continue (returning NULL)

(2) Call Fl::fatal() with an error message, which terminates the program

(3) *withdrawn* (was: leave as-is and crash)

(4) Fix the crash as Ian proposed above and continue silently (returning
NULL).

I think I'd still vote for (1), as another userd (Rich Little) did as well.

Anybody else? Please vote (in this case users and developers, please). TIA

PS: the reason why I prefer (1) is that the user can see the error
message and notice that something went awry. In my test case
(test/clock.cxx, described previously in this thread) the wrong call
would /not/ close all windows (as intended) and return to the caller,
thus leaving the program in an unexpected state.

imm

unread,
Jun 2, 2020, 7:32:26 AM6/2/20
to general fltk
On Tue, 2 Jun 2020, 12:10 Albrecht Schlosser, wrote:


I think I'd still vote for (1), as another userd (Rich Little) did as well.

Anybody else? Please vote (in this case users and developers, please). TIA


I'm caught between 1 & 4. Here's why:

In the lifetime of the program, most of the time will be with end users who can't do anything about the error, so they get a dialog popping up telling them that some window is null, and...?

For the developer the popup dialog is useful though.

On balance, possibly option (1) because if it happens a lot in the field, feedback will reach the developer and they'll have a chance to fix it...

And better than a segfault! 

Manolo

unread,
Jun 2, 2020, 9:18:46 AM6/2/20
to fltk.general


On Tuesday, 2 June 2020 13:10:25 UTC+2, Albrecht Schlosser wrote:


(1) Call Fl::error() with an error message and continue (returning NULL)

(2) Call Fl::fatal() with an error message, which terminates the program

(3) *withdrawn* (was: leave as-is and crash)

(4) Fix the crash as Ian proposed above and continue silently (returning
NULL).

My vote is for (3) because it's also possible that what happens to the window
returned by Fl::next_window(win) or by Fl::first_window()
is to be deleted rather than hidden. In that case, there's nothing we can do
to avoid Fl::next_window(win) from being an error.

Albrecht Schlosser

unread,
Jun 2, 2020, 9:25:06 AM6/2/20
to fltkg...@googlegroups.com
On 6/2/20 1:32 PM imm wrote:
> On Tue, 2 Jun 2020, 12:10 Albrecht Schlosser, wrote:
>
> I think I'd still vote for (1), as another userd (Rich Little) did
> as well.
>
> I'm caught between 1 & 4. Here's why:
>
> In the lifetime of the program, most of the time will be with end users
> who can't do anything about the error, so they get a dialog popping up
> telling them that some window is null, and...?

Docs say about Fl::error() : "The default version on Windows displays
the error message in a MessageBox window. The default version on all
other platforms prints the error message to stderr."

Hence only Windows would show a popup window, but IMHO this would be
acceptable. See below.

> For the developer the popup dialog is useful though.

Yep.

> On balance, possibly option (1) because if it happens a lot in the
> field, feedback will reach the developer and they'll have a chance to
> fix it...

Yes, that was my thought about it.

Note that current programs with this coding error (calling
Fl::next_window() with a window that is not shown) would crash - hence I
believe that such programs are very, very rare.

New programs would (hopefully) be tested by developers and the error
message would give a valuable hint, and, as you said:

> And better than a segfault!

I'm attaching my proposal in file 'next_window.patch'. This is still
only a suggestion though and can be tested with FLTK 1.4 (Git) by
changing close_cb() appropriately.

I'm aware that the error message is not 100% clear if window is NULL but
I didn't want to make it more complicated than necessary. Linux writes
"(nil)" for the NULL pointer with '%p', but if this is an issue on other
platforms (currently not tested) we can change this bit.

Well, I just ran the demo on Linux under Wine and I'm attaching the
error popup image. Not nice, admittedly, but we can change the pointer
value to 'NULL' or something else or leave it away at all.

Looking forward to more comments and votes. TIA.
next_window.patch
next_window_error_wine.png

Albrecht Schlosser

unread,
Jun 2, 2020, 11:36:27 AM6/2/20
to fltkg...@googlegroups.com
Do I understand you correctly? You'd rather let it crash than trying to
"fix" a probably common (as seen in the discussion of this thread) user
error? Or did you mean 4?

Sure, if what we get as argument (Fl_Window *) is not a valid window at
all (a stale pointer to a deleted window or any other stale pointer),
then we can't do anything, that's true. It will crash in one or the
other unforeseeable way.

However, in the likely case that Fl::next_window() is used *as usual in
/other/ cases* when a window gets hidden, then I think a better handling
would be appropriate. Our CMP states at
https://www.fltk.org/cmp.php#CS_METHODOLOGY_ALGORITHMS

"Input validation should be performed only when the function or method
is able to return an error to the caller."

In this case I'd say that returning NULL (and optionally calling
Fl::error()) *is* a way to "return an error to the caller", even if this
has the same meaning as "end of list".

Manolo

unread,
Jun 2, 2020, 12:13:01 PM6/2/20
to fltk.general
My point is that the important thing here is to discourage the user to call
Fl::next_window(win) without considering in what state is win.
What I see important is to add a note about this theme in the doc of
function Fl::next_window().

Modified vote:  option 4.
Reply all
Reply to author
Forward
0 new messages