Re: GtkComponentPeer realization
Hi Tom, On Mon, 2006-02-20 at 15:50 -0500, Thomas Fitzsimmons wrote: On Mon, 2006-02-20 at 17:47 +0100, Mark Wielaard wrote: Yes. We do defer creation of the gtk-peer till the actual addNotify() call. But then we also defer the parent/bound-setting if the container is already visible till the container is actually made valid. This is at least a problem with (sub) classes of Panel and Canvas where applications seem to expect to be able to paint on them before they are actually validated. This is a very strange requirement; they require the ability to draw on a Component that may not be sized correctly? Presumably that component will not be in that invalid state for long, and so will be repainted correctly when it is validated? I am not sure it is an requirement. But there are clearly programs that call paint(), update() and/or getGraphics() on Components at the weirdest times. Tom, do you have an example where the delayed realization of the gtk+ component was necessary/gave strange visual effects? I like to get to the bottom of this one. Yes, the problem was with adding a container of widgets to another already-showing container. Since we were force-realizing widgets on creation, each widget would be shown at 0,0 with size 1x1, then moved into place and resized upon validation. At the time there were bugs in the peer resizing code that made some widgets, including buttons, show up at their natural sizes. The result was that for a big button box in vte, you'd see all the buttons stack up at 0,0 at their natural sizes, then be moved and resized into the right spot. In an attempt to solve this, and to be more in-line with GTK conventions, I eliminated forced realization. The idea is that widgets should be realized when GTK decides to realize them: after they've been properly sized and parented. So with the current code, I leave realization up to GTK and I only set bounds and parents on the peer side after all the AWT validation has occurred, in GtkContainerPeer.endValidate(). Unfortunately, as Mark points out, this approach relies on validation to occur before getGraphics is called on a component, which (apparently) isn't guaranteed. In fact, getGraphics must work as soon as addNotify has been called on it which can happen anytime independent of validation. So delayed realization is unworkable and we should change back to forced-realization. OK, I did this and I tried to make the impact as little as possible. This patch handles the most common case where the Component bounds have not been set yet (so are zero) then it wont show the component yet. This doesn't solve all the cases as seen with vte when a Component is set to a particular size before being added though. (It does solve one other issue I was seeing where my app was hiding an Component by setting its size to 0, previously we would ignore such a reshape call...) I think this is the best we can do for now without doing something much more tricky such hooking into things like endLayout which we don't currently seem to support anyway. And it solve the crashes I was seeing which I think is a more important issue then the jumping components problem. 2006-03-03 Mark Wielaard [EMAIL PROTECTED] * gnu/java/awt/peer/gtk/GtkComponentPeer.java (GtkComponentPeer): Always call setParentAndBounds(). (setComponentBounds): Always call setBounds(). (setBounds): Call setVisible(). (setVisible): If no pixels are showing then don't make it visible. * gnu/java/awt/peer/gtk/GtkContainerPeer.java (endValidate): No need to call setParentAndBounds() anymore. What do you think? The hsqldb, VTE and MegaMek seem to look and work fine with this. Cheers, Mark Index: gnu/java/awt/peer/gtk/GtkComponentPeer.java === RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/GtkComponentPeer.java,v retrieving revision 1.106 diff -u -r1.106 GtkComponentPeer.java --- gnu/java/awt/peer/gtk/GtkComponentPeer.java 23 Feb 2006 19:22:24 - 1.106 +++ gnu/java/awt/peer/gtk/GtkComponentPeer.java 3 Mar 2006 20:05:56 - @@ -145,12 +145,7 @@ Component parent = awtComponent.getParent (); -// Only set our parent on the GTK side if our parent on the AWT -// side is not showing. Otherwise the gtk peer will be shown -// before we've had a chance to position and size it properly. -if (awtComponent instanceof Window -|| (parent != null ! parent.isShowing ())) - setParentAndBounds (); +setParentAndBounds (); setNativeEventMask (); @@ -201,11 +196,6 @@ void setComponentBounds () { Rectangle bounds = awtComponent.getBounds (); - -if (bounds.x == 0 bounds.y == 0 - bounds.width == 0 bounds.height == 0) - return; - setBounds (bounds.x, bounds.y, bounds.width, bounds.height); } @@ -487,6 +477,10 @@ } setNativeBounds (new_x, new_y, width,
Re: GtkComponentPeer realization
On Fri, 2006-03-03 at 21:30 +0100, Mark Wielaard wrote: Hi Tom, On Mon, 2006-02-20 at 15:50 -0500, Thomas Fitzsimmons wrote: On Mon, 2006-02-20 at 17:47 +0100, Mark Wielaard wrote: Yes. We do defer creation of the gtk-peer till the actual addNotify() call. But then we also defer the parent/bound-setting if the container is already visible till the container is actually made valid. This is at least a problem with (sub) classes of Panel and Canvas where applications seem to expect to be able to paint on them before they are actually validated. This is a very strange requirement; they require the ability to draw on a Component that may not be sized correctly? Presumably that component will not be in that invalid state for long, and so will be repainted correctly when it is validated? I am not sure it is an requirement. But there are clearly programs that call paint(), update() and/or getGraphics() on Components at the weirdest times. Tom, do you have an example where the delayed realization of the gtk+ component was necessary/gave strange visual effects? I like to get to the bottom of this one. Yes, the problem was with adding a container of widgets to another already-showing container. Since we were force-realizing widgets on creation, each widget would be shown at 0,0 with size 1x1, then moved into place and resized upon validation. At the time there were bugs in the peer resizing code that made some widgets, including buttons, show up at their natural sizes. The result was that for a big button box in vte, you'd see all the buttons stack up at 0,0 at their natural sizes, then be moved and resized into the right spot. In an attempt to solve this, and to be more in-line with GTK conventions, I eliminated forced realization. The idea is that widgets should be realized when GTK decides to realize them: after they've been properly sized and parented. So with the current code, I leave realization up to GTK and I only set bounds and parents on the peer side after all the AWT validation has occurred, in GtkContainerPeer.endValidate(). Unfortunately, as Mark points out, this approach relies on validation to occur before getGraphics is called on a component, which (apparently) isn't guaranteed. In fact, getGraphics must work as soon as addNotify has been called on it which can happen anytime independent of validation. So delayed realization is unworkable and we should change back to forced-realization. OK, I did this and I tried to make the impact as little as possible. This patch handles the most common case where the Component bounds have not been set yet (so are zero) then it wont show the component yet. This doesn't solve all the cases as seen with vte when a Component is set to a particular size before being added though. (It does solve one other issue I was seeing where my app was hiding an Component by setting its size to 0, previously we would ignore such a reshape call...) I think this is the best we can do for now without doing something much more tricky such hooking into things like endLayout which we don't currently seem to support anyway. And it solve the crashes I was seeing which I think is a more important issue then the jumping components problem. Yes, I agree. This patch looks good, and even avoids force-realizing widgets with gtk_widget_realize, while satisfying the AWT's peer-creation criteria. One small request: that you expand Component.addNotify's javadocs to describe these criteria clearly for future reference. Thanks, Tom
Re: GtkComponentPeer realization
On Fri, 2006-03-03 at 16:05 -0500, Thomas Fitzsimmons wrote: Yes, I agree. This patch looks good, and even avoids force-realizing widgets with gtk_widget_realize, while satisfying the AWT's peer-creation criteria. One small request: that you expand Component.addNotify's javadocs to describe these criteria clearly for future reference. Good point. I only realized (pun intended) that setParent() would do the right thing in the constructor because of the way addNotify() is called. Documented as follows: 2006-03-03 Mark Wielaard [EMAIL PROTECTED] * java/awt/Component.java (addNotify): Expand documentation. diff -u -r1.104 Component.java --- java/awt/Component.java 28 Feb 2006 11:27:15 - 1.104 +++ java/awt/Component.java 3 Mar 2006 21:10:40 - @@ -3426,10 +3426,11 @@ } /** - * Called to inform this component it has been added to a container. - * A native peer - if any - is created at this time. This method is - * called automatically by the AWT system and should not be called by - * user level code. + * Called when the parent of this Component is made visible or when + * the Component is added to an already visible Container and needs + * to be shown. A native peer - if any - is created at this + * time. This method is called automatically by the AWT system and + * should not be called by user level code. * * @see #isDisplayable() * @see #removeNotify() signature.asc Description: This is a digitally signed message part
Re: GtkComponentPeer realization
Hi Mark, OK, I see now where the trouble comes from. In GtkComponentPeer constructor we have: // Only set our parent on the GTK side if our parent on the AWT // side is not showing. Otherwise the gtk peer will be shown // before we've had a chance to position and size it properly. if (awtComponent instanceof Window || (parent != null ! parent.isShowing ())) setParentAndBounds (); So if a component is added to an already showing Container its parent is not set. We try to set it later in GtkContainerPeer.endValidate(). But 1) There is a small bug that doesn't setParentAndBounds for direct children of a Window. (That is easily fixed though.) 2) That only works if the Container is actually revalidated before the just added Component is painted. I don't see how/if 2) ever happens/should happen after a Container.add(Component). The Container is invalid after an add(). When/Where should validate[Tree]() be called after a new component has been added? This is not performed automatically in AWT. We used to do it in setVisible somewhere if I remember correctly, but tests have shown that this is not right (and in fact leads to strange interactions with Swing, where we _do_ perform validation automatically). The GTK peers therefore should initialize correctly even when the component is not validated and not try to defer things. (Actually, the AWT itself defers initialization of the peers themselves, they are not created with the component, but instead when the component receives a call to addNotify). I hope this helps, Roman signature.asc Description: Dies ist ein digital signierter Nachrichtenteil
Re: GtkComponentPeer realization
On Mon, 2006-02-20 at 15:20 +0100, Roman Kennke wrote: I don't see how/if 2) ever happens/should happen after a Container.add(Component). The Container is invalid after an add(). When/Where should validate[Tree]() be called after a new component has been added? This is not performed automatically in AWT. We used to do it in setVisible somewhere if I remember correctly, but tests have shown that this is not right (and in fact leads to strange interactions with Swing, where we _do_ perform validation automatically). OK. Thanks. That probably explains why the delayed parenting and realization did work in the past. The GTK peers therefore should initialize correctly even when the component is not validated and not try to defer things. (Actually, the AWT itself defers initialization of the peers themselves, they are not created with the component, but instead when the component receives a call to addNotify). Yes. We do defer creation of the gtk-peer till the actual addNotify() call. But then we also defer the parent/bound-setting if the container is already visible till the container is actually made valid. This is at least a problem with (sub) classes of Panel and Canvas where applications seem to expect to be able to paint on them before they are actually validated. Tom, do you have an example where the delayed realization of the gtk+ component was necessary/gave strange visual effects? I like to get to the bottom of this one. Cheers, Mark signature.asc Description: This is a digitally signed message part
Re: GtkComponentPeer realization
On Mon, 2006-02-20 at 17:47 +0100, Mark Wielaard wrote: On Mon, 2006-02-20 at 15:20 +0100, Roman Kennke wrote: I don't see how/if 2) ever happens/should happen after a Container.add(Component). The Container is invalid after an add(). When/Where should validate[Tree]() be called after a new component has been added? This is not performed automatically in AWT. We used to do it in setVisible somewhere if I remember correctly, but tests have shown that this is not right (and in fact leads to strange interactions with Swing, where we _do_ perform validation automatically). OK. Thanks. That probably explains why the delayed parenting and realization did work in the past. The GTK peers therefore should initialize correctly even when the component is not validated and not try to defer things. (Actually, the AWT itself defers initialization of the peers themselves, they are not created with the component, but instead when the component receives a call to addNotify). Yes. We do defer creation of the gtk-peer till the actual addNotify() call. But then we also defer the parent/bound-setting if the container is already visible till the container is actually made valid. This is at least a problem with (sub) classes of Panel and Canvas where applications seem to expect to be able to paint on them before they are actually validated. This is a very strange requirement; they require the ability to draw on a Component that may not be sized correctly? Presumably that component will not be in that invalid state for long, and so will be repainted correctly when it is validated? Tom, do you have an example where the delayed realization of the gtk+ component was necessary/gave strange visual effects? I like to get to the bottom of this one. Yes, the problem was with adding a container of widgets to another already-showing container. Since we were force-realizing widgets on creation, each widget would be shown at 0,0 with size 1x1, then moved into place and resized upon validation. At the time there were bugs in the peer resizing code that made some widgets, including buttons, show up at their natural sizes. The result was that for a big button box in vte, you'd see all the buttons stack up at 0,0 at their natural sizes, then be moved and resized into the right spot. There are two cases that we must handle when adding a container to another container and both cases must be handled differently when implementing them on GTK: 1) the parent container is not showing 2) the parent container is showing For case 1 forced realization works fine because the widgets aren't actually shown until the parent is showing, and at that point everything is correctly laid out. For case 2 forced realization causes child components to be shown as soon as their peers are created. Container.validateTree does this: ContainerPeer.beginValidate() create peers for all child components in this container do layout, setting the child component and child component peers' bounds validate child containers recursively ContainerPeer.endValidate() With forced realization all the peers are shown when they are first created, *then* moved and resized when layout is called. This was a bug in our AWT implementation which didn't affect other peer sets. In an attempt to solve this, and to be more in-line with GTK conventions, I eliminated forced realization. The idea is that widgets should be realized when GTK decides to realize them: after they've been properly sized and parented. So with the current code, I leave realization up to GTK and I only set bounds and parents on the peer side after all the AWT validation has occurred, in GtkContainerPeer.endValidate(). Unfortunately, as Mark points out, this approach relies on validation to occur before getGraphics is called on a component, which (apparently) isn't guaranteed. In fact, getGraphics must work as soon as addNotify has been called on it which can happen anytime independent of validation. So delayed realization is unworkable and we should change back to forced-realization. Maybe we can solve the original problem using gtk_widget_show/gtk_widget_hide. I wonder how other toolkits (e.g. Sun's Motif implementation) solve this validate-while-showing problem. Perhaps it's not worth worrying about at this point (though I find validating-while-showing very sloppy). Tom
Re: GtkComponentPeer realization
Hi Tom, On Thu, 2006-02-09 at 09:53 -0500, Thomas Fitzsimmons wrote: An unrealized GtkComponentPeer is a GtkWidget that has an uninitialized X window field. In other words, the X server doesn't have a window data structure allocated for it. We used to force-realize every GtkComponentPeer on construction but that lead to strange behaviour when adding a hierarchy of components to an already-shown container (all components would be shown first at 0,0-1x1 then layed out after that). Delayed realization makes the peer code much more complicated, and adding a container to an already-shown container is rare so I'm thinking it may be acceptable to switch the code back to force-realization-on-creation. It would simplify GtkComponentPeer GdkGraphics and would probably eliminate these situations that you're describing. OK, I see now where the trouble comes from. In GtkComponentPeer constructor we have: // Only set our parent on the GTK side if our parent on the AWT // side is not showing. Otherwise the gtk peer will be shown // before we've had a chance to position and size it properly. if (awtComponent instanceof Window || (parent != null ! parent.isShowing ())) setParentAndBounds (); So if a component is added to an already showing Container its parent is not set. We try to set it later in GtkContainerPeer.endValidate(). But 1) There is a small bug that doesn't setParentAndBounds for direct children of a Window. (That is easily fixed though.) 2) That only works if the Container is actually revalidated before the just added Component is painted. I don't see how/if 2) ever happens/should happen after a Container.add(Component). The Container is invalid after an add(). When/Where should validate[Tree]() be called after a new component has been added? Cheers, Mark signature.asc Description: This is a digitally signed message part
Re: GtkComponentPeer realization
On Sat, 2006-02-11 at 23:00 +0100, Mark Wielaard wrote: Hi Tom, On Fri, 2006-02-10 at 12:52 -0500, Thomas Fitzsimmons wrote: I am not sure how/where the GtkPanelPeer should have been realized and/or whether this comes from the delayed realization you talked about (I don't actually see where this Panel will ever get realized to be honest). Yes, panels are never realized because they do not have an X window associated with them. Instead they draw on their parent's X window. So GtkPanelPeer should probably override isRealized to check if it has a parent and if so, if that parent is realized. But then they should also override getGraphics() I presume to return the (translated) Graphics of their parent Container? And currently they don't. Also we seem to explicitly request an X window to be associated since in GtkPanelPeer.c we say: gtk_fixed_set_has_window (GTK_FIXED (widget), TRUE); We also request GTK_CAN_FOCUS. Is that possible on widgets without a window? And in fact Graphics object returned from a Panel is often correct. Only in situations like the example I gave does it never get realized correctly it seems. Puzzling... You're right. GtkPanelPeers do have their own windows now; I was thinking of a previous revision of this code. To answer your original question, GtkPanelPeers are realized when they are shown. Tom Cheers, Mark
Re: GtkComponentPeer realization
On Fri, 2006-02-10 at 00:26 +0100, Mark Wielaard wrote: Hi Tom, On Thu, 2006-02-09 at 09:53 -0500, Thomas Fitzsimmons wrote: On Thu, 2006-02-09 at 13:52 +0100, Mark Wielaard wrote: I like to debug this a bit further, but I couldn't find good documentation on the handling of (un)realized GtkComponentPeers. Does anybody have a link or an explanation of whether or not the above should ever happen? Yes it can happen especially in multi-threaded applications. You're probably right to just ignore the paint event. It'd be useful to know if hsqldb makes calls into the peers from different threads. For example if it shows a window in one thread and another thread handles painting on that window. No it doesn't. I was finally able to extract a simple testcase that shows the problem which also doesn't use any extra application Threads. See attached. If you update the tree to include Lillian's latest patches for GtkPanelPeer (which made the logic more clear to me, thanks Lillian) and add the following hack, you can see that the Panel doesn't seem to get realized. And always will print out the following after clicking on the button: getGraphics() called on unrealized: java.awt.Panel[panel0,4,25,66x25,invalid,parent=frame0,layout=java.awt.FlowLayout] I am not sure how/where the GtkPanelPeer should have been realized and/or whether this comes from the delayed realization you talked about (I don't actually see where this Panel will ever get realized to be honest). Yes, panels are never realized because they do not have an X window associated with them. Instead they draw on their parent's X window. So GtkPanelPeer should probably override isRealized to check if it has a parent and if so, if that parent is realized. Tom The Hack (or workaround, if you only put in the return null, and not the dumpStack): --- gnu/java/awt/peer/gtk/GtkComponentPeer.java 9 Feb 2006 17:44:30 - 1.101 +++ gnu/java/awt/peer/gtk/GtkComponentPeer.java 9 Feb 2006 23:14:19 - @@ -263,6 +262,13 @@ public Graphics getGraphics () { +if (! isRealized()) + { + System.err.println(getGraphics() called on unrealized: + awtWidget); + Thread.dumpStack(); + return null; + } + if (GtkToolkit.useGraphics2D ()) return new GdkGraphics2D (this); else Any insights on how to properly debug this welcome. I do have one patch to make the event passing a bit simpler. That doesn't change the result of this test case, but it makes it a little easier to follow the events that are fired. I'll post it in a second to the patches list. Cheers, Mark
Re: GtkComponentPeer realization
On Thu, 2006-02-09 at 13:52 +0100, Mark Wielaard wrote: Hi, After a lot of debugging I finally found out why a program I was testing was crashing sometimes. (The hsqldb AWT frontend - try the org.hsqldb.util.DatabaseManager class from the hsqldb.jar as distributed with OpenOffice for example.) A GtkGraphics object is created differently for realized and un-realized components. When we get a paint event for an unrealized component and try to use the associated GdkGraphics object bad things happen (because NSA_GET_G_PTR returns null in such cases). The attached hack makes things work for now by just ignoring such paint events for such objects. It gives: NOT handling java.awt.event.PaintEvent[UPDATE,updateRect=java.awt.Rectangle[x=0,y=0,width=360,height=300]] on org.hsqldb.util.Grid[panel0,0,0,360x300,invalid,parent=panel1] for UNREALIZED org.hsqldb.util.Grid[panel0,0,0,360x300,invalid,parent=panel1] I like to debug this a bit further, but I couldn't find good documentation on the handling of (un)realized GtkComponentPeers. Does anybody have a link or an explanation of whether or not the above should ever happen? Yes it can happen especially in multi-threaded applications. You're probably right to just ignore the paint event. It'd be useful to know if hsqldb makes calls into the peers from different threads. For example if it shows a window in one thread and another thread handles painting on that window. An unrealized GtkComponentPeer is a GtkWidget that has an uninitialized X window field. In other words, the X server doesn't have a window data structure allocated for it. We used to force-realize every GtkComponentPeer on construction but that lead to strange behaviour when adding a hierarchy of components to an already-shown container (all components would be shown first at 0,0-1x1 then layed out after that). Delayed realization makes the peer code much more complicated, and adding a container to an already-shown container is rare so I'm thinking it may be acceptable to switch the code back to force-realization-on-creation. It would simplify GtkComponentPeer GdkGraphics and would probably eliminate these situations that you're describing. Tom
Re: GtkComponentPeer realization
Hi Tom, On Thu, 2006-02-09 at 09:53 -0500, Thomas Fitzsimmons wrote: On Thu, 2006-02-09 at 13:52 +0100, Mark Wielaard wrote: I like to debug this a bit further, but I couldn't find good documentation on the handling of (un)realized GtkComponentPeers. Does anybody have a link or an explanation of whether or not the above should ever happen? Yes it can happen especially in multi-threaded applications. You're probably right to just ignore the paint event. It'd be useful to know if hsqldb makes calls into the peers from different threads. For example if it shows a window in one thread and another thread handles painting on that window. No it doesn't. I was finally able to extract a simple testcase that shows the problem which also doesn't use any extra application Threads. See attached. If you update the tree to include Lillian's latest patches for GtkPanelPeer (which made the logic more clear to me, thanks Lillian) and add the following hack, you can see that the Panel doesn't seem to get realized. And always will print out the following after clicking on the button: getGraphics() called on unrealized: java.awt.Panel[panel0,4,25,66x25,invalid,parent=frame0,layout=java.awt.FlowLayout] I am not sure how/where the GtkPanelPeer should have been realized and/or whether this comes from the delayed realization you talked about (I don't actually see where this Panel will ever get realized to be honest). The Hack (or workaround, if you only put in the return null, and not the dumpStack): --- gnu/java/awt/peer/gtk/GtkComponentPeer.java 9 Feb 2006 17:44:30 - 1.101 +++ gnu/java/awt/peer/gtk/GtkComponentPeer.java 9 Feb 2006 23:14:19 - @@ -263,6 +262,13 @@ public Graphics getGraphics () { +if (! isRealized()) + { + System.err.println(getGraphics() called on unrealized: + awtWidget); + Thread.dumpStack(); + return null; + } + if (GtkToolkit.useGraphics2D ()) return new GdkGraphics2D (this); else Any insights on how to properly debug this welcome. I do have one patch to make the event passing a bit simpler. That doesn't change the result of this test case, but it makes it a little easier to follow the events that are fired. I'll post it in a second to the patches list. Cheers, Mark import java.awt.*; import java.awt.event.*; public class PanelPanel extends Frame implements ActionListener { public static void main(String[] args) { new PanelPanel(); } PanelPanel() { Button b = new Button(Click me!); b.addActionListener(this); add(b); pack(); show(); } public void actionPerformed(ActionEvent ae) { Panel p = new Panel(); p.setBounds(new Rectangle(20, 20)); removeAll(); add(p); doLayout(); p.repaint(); } } signature.asc Description: This is a digitally signed message part