FLTK logo

STR #3124

FLTK matrix user chat room
(using Element browser app)   FLTK gitter user chat room   GitHub FLTK Project   FLTK News RSS Feed  
  FLTK Apps      FLTK Library      Forums      Links     Login 
 Home  |  Articles & FAQs  |  Bugs & Features  |  Documentation  |  Download  |  Screenshots  ]
 

Return to Bugs & Features | Roadmap 1.3 | SVN ⇄ GIT ]

STR #3124

Application:FLTK Library
Status:1 - Closed w/Resolution
Priority:3 - Moderate, e.g. unable to compile the software
Scope:2 - Specific to an operating system
Subsystem:X11
Summary:Cross-window take_focus not fully working
Version:1.3-current
Created By:cand
Assigned To:AlbrechtS
Fix Version:1.3.3 (SVN: v10339)
Update Notification:

Receive EMails Don't Receive EMails

Trouble Report Files:


Name/Time/Date Filename/Size  
 
#1 cand
12:44 Sep 02, 2014
windowfocus.cxx
1k
 
 
#2 ianmacarthur
07:54 Sep 04, 2014
windowfocus2.cxx
1k
 
 
#3 ianmacarthur
08:59 Sep 04, 2014
windowfocus3.cxx
2k
 
 
#4 ianmacarthur
09:02 Sep 04, 2014
windowfocus4.cxx
1k
 
 
#5 cand
06:59 Sep 11, 2014
fltk-show-sync.patch
4k
 
 
#6 cand
00:18 Sep 12, 2014
fltk-show-sync-v2.patch
4k
 
 
#7 cand
23:46 Sep 16, 2014
fltk-show-sync-v3.patch
5k
 
 
#8 AlbrechtS
09:24 Sep 17, 2014
wait_for_expose.patch
2k
 
 
#9 AlbrechtS
09:37 Sep 25, 2014
wait_for_expose_v2.patch
2k
 
 
#10 cand
00:39 Sep 26, 2014
windowtest.patch
2k
 
     

Trouble Report Comments:


Name/Time/Date Text  
 
#1 cand
12:44 Sep 02, 2014
On Linux, take_focus across windows seems to work only partially. If you have existing windows, you can pass the focus between them fine, but if you need to pop a new window and restore focus to the spawning window, it doesn't work.

Attaching sample code. I'd appreciate if someone could confirm it's working on other platforms (win/mac).
 
 
#2 ianmacarthur
07:54 Sep 04, 2014
I had initially thought this problem was *not* restricted to X11, but was in fact widespread.
However, testing Lauri's example windowfocus.cxx on Win7 I *do not* see the failure, so the fault may be X11 specific after all...

I attach an slightly extended windowfocus2.cxx example by way of seeing something of where the focus actually is, when it goes awry...
 
 
#3 AlbrechtS
08:36 Sep 04, 2014
FWIW, I don't know if this is related to the issue in this STR, however it seems, so I just want to let you know. There are indeed platform specific differences when showing windows, because X needs at least two or three messages (client-server-turnaround) to show a new window. This can be seen in callbacks when you show a window, or maybe in splash-screens when showing a window before Fl::run() is called.

The effect is something like this:

win->show(); // pop up window
Fl::wait(); // wait for window to show [1]
while (win->shown()) {
  do something;
  ...
}

This works on non-X platforms, but the call to Fl::wait() at [1] is not sufficient to show the window. Under Linux you may need two or more Fl::wait() calls to succeed.

That or something similar...

This *may* explain the different focus behavior (or not). Hope this helps, but I can't contribute more right now. Sorry.
 
 
#4 chris
08:37 Sep 04, 2014
Setting

   win2->set_menu_window()

or

   win2->set_override()

does the trick here.
 
 
#5 ianmacarthur
08:59 Sep 04, 2014
OK, some more testing, here's what I see.
Basically, there's a race of sorts going on here. As Albrecht notes, actually showing a window under X takes more "server trips" than it does on OSX or WIN32.

The net result, for this use case, is that if you show() a window then immediatley try to pass the focus *away* from that window to some other widget, that *may* not work, since under X the window is not even mapped yet... it gets mapped "later" when the server gets around to it - at which point it gains the foucs. But this is *after* we have executed the code to "give away" the focus to the input widget in Lauri's test code...

See windowfocus3.cxx above, to which I have added some debug to see what's going on, including a timeout so I could see the focus moving from one widget to another "after the fact".

However, windowfocus3.cxx "works correctly" as I've added a workaround to allow the callback to wait until the window is mapped before giving away the focus.

This may be "good enough" for Lauri's desired use-case in the short term.

The real fix is harder, as under X we are always at the mercy of the server in terms of *when* a window will actually be mapped...

Thoughts?
 
 
#6 ianmacarthur
09:03 Sep 04, 2014
The file windowfocus4.cxx is just windowfocus3 with the debug stuff stripped out, so it is my proposed "working example" for the workaround...  
 
#7 cand
11:37 Sep 04, 2014
I'd really like to get a proper fix.

I tested adding this to the end of Fl_Window::show() in Fl_x.cxx:

while (!Fl_X::i(this) || Fl_X::i(this)->wait_for_expose) {
  Fl::wait(0);
}

and it correctly blocks until we have a window. I would think show() blocking until the window is up is quite ok, even expected behavior, but I'm not sure if this wouldn't break a corner use case.

The above code alone is not enough to fully fix the bug, though. While it prevents the initial focus steal, passing focus between windows doesn't correctly pass the focus to the new window, causing the focus to break when I move the mouse over either window. It would seem that a focus change should check if the window changed, and if so, call XSetInputFocus on the new window. I'll open a new STR for this part.
 
 
#8 ianmacarthur
13:35 Sep 04, 2014
I'm uneasy about show() *always* waiting for the window to be mapped, since in a networked environ that might be a long time; or never...

The current asynchronous nature is part of the way X works, but it is a nuisance. This is why I think a "proper" fix is hard.

The issue with not setting the focus correctly between windows once mapped does look like it may be amenable to a bit of tweaking. That said I spent some time staring at the code where we interact with Fl::focus(), widget::take_focus(), fl_fix_focus(), fl_throw_focus()... and so on, and it is a bit of a rabbit warren, so I lost my way a bit...!
 
 
#9 cand
00:03 Sep 05, 2014
How about making it a global option then, default to current behavior?  
 
#10 cand
07:00 Sep 11, 2014
Attaching proposed patch. It also includes a modified version of windowfocus.cxx under test/.  
 
#11 manolo
23:52 Sep 11, 2014
The proposed patch states
"Note that this will block threads that use Fl::lock() for
syncronization for a short time."
In fact, the Fl::wait() call within show_sync() unlocks, if anything.

But it may be desirable to have show_sync truely block threads until
the window is mapped. This would be

void Fl_Window::show_sync() {
  Fl::lock();
  show();
  while (!i || i->wait_for_expose) {
    Fl::wait();
  }
  Fl::unlock();
}

may be?

Also, just use i instead of Fl_X::i(this) inside the Fl_Window class.
 
 
#12 cand
00:18 Sep 12, 2014
Posted v2 with those modifications.  
 
#13 ianmacarthur
15:08 Sep 13, 2014
Apropos of nothing much - in the patch we say that OSX and Win32 are both "show synchronous" by default...

I recall that Manolo said that OSX was, but do we know that Win32 is?

I have a vague recollection of getting caught out in the past with windows showing (or rather not showing) on Win32, though I don't recall the circumstances...
 
 
#14 cand
04:37 Sep 14, 2014
I relied on comment #2 saying the test case works on Windows.  
 
#15 cand
00:23 Sep 16, 2014
Checking Fl_win32.cxx, it calls ShowWindow for new windows and BringWindowToTop for old windows. Both of these are blocking calls according to documentation.

If there are no objections, I'll commit tomorrow.
 
 
#16 AlbrechtS
17:47 Sep 16, 2014
Patch (v2) looks good so far, but I'd like to test the Windows case more thoroughly, probably tomorrow.

WRT blocking threads: true, v1 didn't block threads (my fault), because Fl::wait() calls Fl::unlock() and Fl::lock(), patch v2 does. But do we really want this, or should we remove the comment about blocking threads. Maybe blocking the main thread (that calls show_sync()) with Fl::wait() until the window is shown would be enough.

But there's another point: we should really also have

  [virtual] show_sync(int argc, char **argv)

because that would be the typical call for using splash screens (the first window shown in such a scenario). Maybe not trivial to do however w/o a little code refactoring.
 
 
#17 cand
23:47 Sep 16, 2014
Posted v3 with show_sync(int argc, char **argv).  
 
#18 AlbrechtS
05:21 Sep 17, 2014
Thanks for fltk-show-sync-v3.patch. It was late yesterday, and I was distracted by all that argc/argv stuff I saw. Of course it was simple to implement show_sync(argc,argv).  
 
#19 AlbrechtS
05:39 Sep 17, 2014
I apologize for the confusion in advance, but I thought about it and maybe my own proposal of show_sync() was not the best way.

We would have to add two overloaded virtual methods, and the questions about blocking threads are not resolved yet (I believe that both would be okay, but I'd tend to use a non-locking variant now).

I want to come back to Ian's proposal somewhere in fltk.coredev. Maybe this is the better way to do it, because we need only one new and very simple method, and this could probably be non-virtual.

Something like this would probably suffice:

/**
  Waits for a window to be fully shown after calling show().

  [ ... more from the docs as proposed earlier, but w/o the comment
    about blocking threads, because it's non-blocking when calling
    Fl::wait() as Manolo wrote earlier. ]
*/
void Fl_Window::wait_for_expose() {
  while (!i || i->wait_for_expose) {
    Fl::wait();
  }
}

If we'd really want to block threads we could make that optional, as in:

FL/Fl_Window.H:

void Fl_Window::wait_for_expose(int blocking=0);

Implementation, as proposed by Manolo with optional internal locking:

void Fl_Window::wait_for_expose(int blocking) {
  if (blocking) Fl::lock();
  while (!i || i->wait_for_expose) {
    Fl::wait();
  }
  if (blocking) Fl::unlock();
}

Note that this would utilize the recursive locking of Fl::lock() which is (AFAICT) not fully documented, but I've verified it in Windows and Unix (pthread) code versions. I don't know about the Mac code, unless we use pthreads on OS X as well. Do we?

But that's probably moot, since wait_for_expose() would be a no-op on Windows and OS X anyway.

[Note: I did not test Windows yet. Need more time, sorry.]
 
 
#20 AlbrechtS
09:24 Sep 17, 2014
See attached file wait_for_expose.patch for my proposal with

  Fl_Window::wait_for_expose(int blocking=0);

Personally I would prefer not to have the blocking option at all, and this can be removed easily. I don't think that it is needed, since a programmer could easily wrap his code in Fl::lock() ... Fl::unlock() as well.

The doxygen comment is somewhat long, maybe this is not all needed.

Comments, please, which version to use:

(a1) show_sync() or
(a2) wait_for_expose()

(b1) blocking version or
(b2) non-blocking version

(c) maybe a better name for "wait_for_expose()" ?

My vote would be (a2) and (b2), i.e. blocking argument removed from wait_for_expose.patch.
 
 
#21 AlbrechtS
09:26 Sep 17, 2014
Note: wait_for_expose() is neither virtual nor platform-dependent in this patch. Tested on Windows and Linux.  
 
#22 cand
11:49 Sep 17, 2014
a2 & b2 is ok by me. Note that the test app should still be committed, otherwise there is no test for this functionality included, and so it might break in the future.  
 
#23 AlbrechtS
05:13 Sep 18, 2014
Yep, agreed (test app).  
 
#24 AlbrechtS
09:37 Sep 25, 2014
Please review and test wait_for_expose_v2.patch. This has been compiled w/o problems on Windows (MinGW) and Linux. I intend to commit this patch tomorrow, if there are no objections.

Changes:

(1) No "blocking" options, i.e. no Fl::lock() and Fl::unlock().
(2) Documentation is in source file.

Todo: Fix test program and test.

Lauri, if you can adapt your test program and post it, feel free to do it. I won't have time to work on this until tomorrow afternoon (MET).
 
 
#25 cand
00:40 Sep 26, 2014
Posted updated test program as windowtest.patch. Working fine here.  
 
#26 AlbrechtS
17:14 Sep 26, 2014
Fixed in Subversion repository.

Note: there are some small additions, mainly svn attributes and $Id$ tags in following commits after 10339, but 10339 is the real fix version.
 
 
#27 AlbrechtS
17:15 Sep 26, 2014
PS: Lauri, thanks for the updated test program.  
 
#28 cand
01:52 Sep 27, 2014
All good, closing.  
     

Return to Bugs & Features ]

 
 

Comments are owned by the poster. All other content is copyright 1998-2024 by Bill Spitzak and others. This project is hosted by The FLTK Team. Please report site problems to 'erco@seriss.com'.