Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

xlibe and Tcl/Tk, static wish produced but with mixed results #2

Open
undroidwish opened this issue Dec 24, 2021 · 21 comments
Open

xlibe and Tcl/Tk, static wish produced but with mixed results #2

undroidwish opened this issue Dec 24, 2021 · 21 comments
Labels

Comments

@undroidwish
Copy link

I've tried the commit 25e94d5 as basis for a Tcl/Tk wish using the source tree from www.androwish.org. The following steps were carried out:

  • install the xorgproto package for missing header files (X.h and friends)
  • change the CMakeLists.txt in xlib and xext to produce static libraries
  • use gcc-x86 and g++-x86 for building libX11.a and libXext.a
  • configure and build tcl with --disable-shared
  • configure and build tk with --disable-shared and --x-includes/--x-libraries to point to the xlibe headers and libraries, LIBS set to "-lbe -ltranslation -lstdc++ -liconv" in the configure step to pick up missing stuff when linking wish
  • alternatively, linking wish was performed both with gcc-x86 and g++-x86 without observing different results

Finally I've got a wish which when started creates a very small black window with about 55x55 pixels. When the wish is started with the TK_CONSOLE=1 environment variable (this is an AndroWish feature), a separate toplevel with the console known from Windows is created. This toplevel is initially too small and black. Later, on moving and/or resizing, parts of the toplevels are filled in gray and sometimes the console's menu bar is drawn. The menu bar is not sensitive to mouse events.

Similar procedure on x86_64 yields a very early crash of wish in strdup/XGetAtomName... where I suspect some 32/64 bit type issues with X11 Atom types. I did not dig deeper on x86_64,

@waddlesplash
Copy link
Owner

waddlesplash commented Dec 24, 2021

Similar procedure on x86_64 yields a very early crash of wish in strdup/XGetAtomName... where I suspect some 32/64 bit type issues with X11 Atom types. I did not dig deeper on x86_64

I saw the same with Tk 8.6 stable release. It seems the 32/64 bit type mixup for Atoms was corrected in the interim in Tk; current trunk gets much farther.

I haven't tested this code at all on 32-bit, it's entirely possible I have some serious bug there. I will investigate after the holiday weekend.

@waddlesplash
Copy link
Owner

At least with current Tk trunk on x86_64, I get this when running git gui and <TAB>-ing through the window:

image

The menu bar is sensitive to mouse events here, nothing else is (from debugging, it appears a BackgroundElement is being placed in front of all the other controls except the menu bar. I don't really know enough about Tk internals to diagnose why that is though.)

@waddlesplash
Copy link
Owner

use gcc-x86 and g++-x86 for building libX11.a and libXext.a

You can use setarch x86 to start a subshell where these will be just gcc and g++ (and the same for ld etc.), fwiw.

@waddlesplash
Copy link
Owner

Can you test with the little stopwatch example program? In my testing this works just fine. If you see behavioral differences, maybe there is a static/shared incompatibility somehow; I didn't try building as static libraries.

Later, on moving and/or resizing, parts of the toplevels are filled in gray and sometimes the console's menu bar is drawn.

This is the same behavior I see with anything that uses a BackgroundElement. The background is apparently on top of everything else, or maybe the backgrounds for "child" layouts cover the whole parent window? Still not sure how Xlibe is causing this.

@waddlesplash
Copy link
Owner

I noticed that I'd failed to actually implement CWStackMode. I did the basics of that in a63c55c, however, it does not seem to have made much difference here.

I also had a bug where sub-windows would be automatically (and silently) mapped. After fixing that, the menu bars disappear in all the Tk applications I tried. Commenting out the Hide in XCreateWindow gets them back (and they still work in Git GUI) but otherwise the BackgroundElements still obscure everything.

@pulkomandy
Copy link

Does Xlibe (or Tk, really) rely on overlapping BViews? If so, you have to take into account that the stacking is "reversed", that is, if several BViews are overlapping, the first one to be added to the window will get the mouse events.

So if something tries to add a "background view" and then other views as overlapping sibling, this won't work without special care.

This is not officially supported by the BeAPI, but one option is to use the two parameter version of BWindow::AddChild/BView::AddChild, with the previously added view passed as a second parameter. This will put the newly added views on top of the stack, and so reverse the order in which events are dispatched.

This will work for now, but there's no guarantee that we will keep the views in a defined order, we may change how we do this in the future. Unless we decide to document and freeze a specific order...

@waddlesplash
Copy link
Owner

The problem is that the background view is stacked on top of all other views, and thus when redraws occur, is drawn before them.

I am pretty sure, when I last debugged this, that Tk asks X11 for a specific stacking of the windows, and Xlibe obliges. I am pretty sure I tried reversing the addition order and it changed nothing, but, maybe I should test that again.

@waddlesplash
Copy link
Owner

I looked at this a bit again. I think that Tk does not actually create a Window for the background element; instead, it has only some proxy views and the BackgroundElement is merely the last in an internal list of items. I think that's why I had such trouble debugging this, because I couldn't figure out how the BackgroundElement was supposed to get to the bottom (first) of the list and get drawn/handle focus input before anything else.

@X547
Copy link
Contributor

X547 commented Sep 5, 2023

If so, you have to take into account that the stacking is "reversed", that is, if several BViews are overlapping, the first one to be added to the window will get the mouse events.

I believe that it is a Haiku bug and I proposed a patch long time ago: https://review.haiku-os.org/c/haiku/+/2093.

@pulkomandy
Copy link

There is no guarantee anywhere in the API about the ordering of overlapping sibling views. So, it can't be called a bug.

Applications relying on overlapping sibling views are doing something that the API does not give any guarantee about. The applications are not supposed to do that.

Sorry, I know such distinctions are annoying and frustrating, but it changes how I understand and consider things.

From there we have multiple choices we can consider:

Documenting that overlapping sibling views are allowed

In this case, we have to decide what happens in that case in terms of drawing and mouse events, considering also edge cases.

If a view does not handle a key down message, should the key down message propagate to the next sibling view? How does that interact with forwarding the message to the parent view? Or is it just the "topmost" view and all other views "under" it are ignored?

If a view uses B_DRAW_ON_CHILDREN, the sibling views are not children, so what happens exactly? In what order are the sibling overlapping views, and their possibly also overlapping children, drawn?

What about overlays and view bitmaps? In what order are these drawn?

What if the "under" view calls Invalidate()? Does its sibling needs to be redrawn too? How are clipping rectangles calculated?

Do we need an API to change the Z-order after the views are added?

Documenting more explicitly that this isn't allowed

In this case, maybe adding checks in BView/BWindow to call debugger when it happens.

So, yes, it seems overlapping sibling views are a desirable feature, and we should consider allowing it. But making it happen is not just about flipping this one list iteration order. There are many questions to answer here and impacts in both the drawing code and the input handling.

@waddlesplash
Copy link
Owner

I don't think this problem is merely due to that Haiku behavior. Like I said before, the internal list in Tk has the BackgroundElement on top, and even before any mouse clicks occur it is drawn incorrectly. I also think that Tk is using double-buffering here, and the view that receives mouse clicks does not have any children (but instead only an offscreen view used for actual drawing does) but it's been a while since I looked at that.

@waddlesplash
Copy link
Owner

So, after some investigation, I've figured out what's going on. Indeed this is a clip-by-siblings problem: the background elements are "on top" because they are in their own internal hierarchy, and then have views created just for them, and then there's another view above that.

I guess my question is: why does Tk behave this way? The background views are always exactly the same size as the parent views, why not have the parent views be the background views themselves? Is there any way we can add a "hack" to Tk to make this the case?

I see that Tk has custom code on macOS to manually set the clipping state of all views, because I guess macOS (unlike Windows and X11) does not guarantee this either. We could possibly adopt some code like that in a Tk port, but I wonder why this is really necessary in the first place.

@waddlesplash
Copy link
Owner

(FWIW, Windows does not guarantee this by default; instead it has a flag WS_CLIPSIBLINGS that can be set to enable this.)

@waddlesplash
Copy link
Owner

So, the overlapping views aren't the whole story. I added (rather hacky) code to detect and completely hide any view that overlapped with a later sibling, to see what would happen. Mouse events in Git-Gui now are delivered properly, one can click in the views, scroll, etc. but the views are still mostly obscured: mousing over a button, it redraws correctly; clicking it flashes all controls in the window briefly but then they're overwritten with the gray background again.

@waddlesplash
Copy link
Owner

It turns out that the Worker file manager also depends on sibling-clipping behavior for its dialogs, and in a much more straightforward way than what Tk is doing. I worked on the experimental patch I had before and pushed it to a branch (9061873) but it still doesn't behave correctly at all; there's lots of things cut off, and after resizing windows the sibling clipping appears to be totally gone (despite the fact that it always updates on resizes.)

The latter problem may be due to interaction with update sessions, I haven't yet checked. But the problem with things being cut off I don't understand at all; that's exactly what this code should be preventing.

@pulkomandy, I think you have more experience than I dealing with BView clipping behaviors, is there any chance you might be able to poke at this a bit?

@pulkomandy
Copy link

Well I have asked a lot of questions in my previous comment. The current situation is "document that overlapping sibling views are not allowed", for input it sort-of-works if you put the views backwards (the last added view does not receive the events, the one added first does), and the drawing might be in the reverse order (the last added view is drawn last).

There is a patch by X512 on Gerrit reversing the order for the input handling, so, we get consistent behavior:

  • The view added first is drawn first, and its siblings can draw "over" it
  • The view added first is "under" the view added last also for event processing

There likely is a few more bugs (flickering, problems with invalidating the "under" view and not having the "over" view automatically redraw on top of it).

So we need to pick an answer for the questions I asked, and change the implementation to match. The basics of it is just "in what order do we traverse children views", both for drawing and for mouse messages delivery/hit testing. But then there is probably several more problems, so we probably should build a set of test cases with complex view hierarchies (overlapping views each having children that also overlap, etc) to make sure everything is working fine. I think a simple test where clicking a view changes its view color and invalidates it would work fine here? Then just click around and make sure you can change the color of any rectangle, without it hiding/corrupting any other rectangle.

@X547
Copy link
Contributor

X547 commented Nov 21, 2023

There likely is a few more bugs (flickering, problems with invalidating the "under" view and not having the "over" view automatically redraw on top of it).

There are general rule that drawing outside BView::Draw is not allowed when using potentially intersecting views. It is also applicable to many other toolkits. I am not sure, but probably asynchronous drawing is not allowed anymore in GTK 3+.

@pulkomandy
Copy link

I don't see how this is related to what I said in this specific comment. In this example nothing is drawn outside of Draw() calls.

With the following scenario:

  • Views A and B are overlapping siblings
  • There is a defined order between siblings, so A is "under" B
  • Something invalidates A
  • A::Draw is called and draws a few things in the area overlapping with B
  • Now, whatever B had drawn there is not visible anymore

How do we handle this? I can think of these options, maybe there are more:

  • Ignore the problem (not really a solution)
  • Somehow make sure that are a of B is invalidated, and B::Draw is called after A::Draw
  • Use region clipping to prevent A from drawing in the area where B is overlapping it

Do we already have something in place for this?

@X547
Copy link
Contributor

X547 commented Nov 21, 2023

How do we handle this? I can think of these options, maybe there are more:

Invalidation works on window scope, not view. So when invalidating view A with region that also overlaps with view B, both views A and B will be scheduled for drawing in next update session. Invalid region is stored in window object, views do not store invalid region.

It is how app_server currently works, so no new logic is needed to introduce here.

@waddlesplash
Copy link
Owner

Use region clipping to prevent A from drawing in the area where B is overlapping it

This is what I already tried to do in the patch I linked above, and it doesn't seem to be working.

both views A and B will be scheduled for drawing in next update session

Yes, but what if view A is scheduled but view B isn't, as when only a part of A is invalidated and not a part that overlaps B? Then if the application decides to redraw all of A, then B will be invisible; and indeed this is what happens with both Worker and Tk. Hence my attempts to add siblings to the clipping regions in the patch above, but it doesn't seem to be working correctly for some reason.

There are general rule that drawing outside BView::Draw is not allowed when using potentially intersecting views.

All drawing for X11 applications running on Xlibe happens outside BVIew::Draw, and indeed it doesn't work right. So some other solution is needed.

@X547
Copy link
Contributor

X547 commented Nov 22, 2023

Yes, but what if view A is scheduled but view B isn't, as when only a part of A is invalidated and not a part that overlaps B?

Again, it is not supposed to work properly if drawing is done outside of BView::Draw(). BView::Invalidate() call is supposed to used only in pair with BView::Draw(). Doing something else is misuse of API. If drawing is performed outside of BView::Draw(), it should be scheduled manually without using BView::Invalidate().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants