| Summary: | embedded SWT_AWT frames flicker when the SWT window is resized | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| Product: | [Eclipse Project] Platform | Reporter: | Bruno Haible <haible> | ||||||
| Component: | SWT | Assignee: | Silenio Quarti <Silenio_Quarti> | ||||||
| Status: | CLOSED WORKSFORME | QA Contact: | |||||||
| Severity: | normal | ||||||||
| Priority: | P3 | CC: | ericwill, gordon.hirsch, jan.krakora.cz, snorthov | ||||||
| Version: | 3.1 | Keywords: | triaged | ||||||
| Target Milestone: | --- | ||||||||
| Hardware: | Other | ||||||||
| OS: | Linux-GTK | ||||||||
| Whiteboard: | |||||||||
| Attachments: |
|
||||||||
|
Description
Bruno Haible
Created attachment 19817 [details]
sample application that uses embedded SWT frames
We believe strongly that the flickering is on the AWT/Swing side. SSQ to provide strategies to reduce this. Isn't there one on the SWT snippets page? The strategy from Snippet 154 is already taken into account in the sample. It succeeds to fix the problem only on Windows, not on linux-gtk. This is an AWT problem and I believe there is nothing SWT can do to work around
it. The problem does not happen on because of the AWT system property
"sun.awt.noerasebackground", but this property is only honoured on Windows.
System.setProperty("sun.awt.noerasebackground", "true");
Here is a simple AWT only example that shows the problem:
public static void main(String[] args) {
java.awt.Frame frame = new java.awt.Frame();
java.awt.Panel panel =
new java.awt.Panel(new BorderLayout()) {
public void update(java.awt.Graphics g) {
// Don't erase the background, to decrease flickering.
paint(g);
}
};
JRootPane root = new JRootPane();
panel.add(root);
java.awt.Container contentPane = root.getContentPane();
JPanel blackPanel = new JPanel();
blackPanel.setBackground(java.awt.Color.black);
contentPane.add(blackPanel, BorderLayout.CENTER);
frame.add(panel);
frame.setVisible(true);
}
The AWT sample can be changed so that it doesn't show the problem any more:
public static void main(String[] args) {
java.awt.Frame frame =
new java.awt.Frame() {
public void update(java.awt.Graphics g) {
// Don't erase the background, to decrease flickering.
paint(g);
}
};
JRootPane root = new JRootPane();
frame.add(root);
java.awt.Container contentPane = root.getContentPane();
JPanel blackPanel = new JPanel();
blackPanel.setBackground(java.awt.Color.black);
contentPane.add(blackPanel, BorderLayout.CENTER);
frame.setVisible(true);
}
The two tricks to get rid of the flickering are:
1) Not to use java.awt.Panel,
2) Override the update(Graphics) method of the java.awt.Frame class, to avoid
the clearRect call found in java.awt.Container:update(Graphics).
These two tricks can be transposed to the SWT world. Find attached a sample with a class
FixedXEmbeddedFrame, subclass of sun.awt.X11.XEmbeddedFrame, that overrides the
update(Graphics) method as needed. This shows that the problem can indeed be fixed.
However, this fix is not satisfactorily deployable on our side, because it is platform dependent:
- The class FixedXEmbeddedFrame cannot be compiled on non-X11 platforms.
- The comments in class SWT_AWT say that the constructor should take an 'int' on some
platforms and a 'long' on others.
Therefore I believe this fix should best be integrated into SWT, so that code that uses SWT
can remain platform independent.
Created attachment 19906 [details]
Sample without flickering, through a subclass of sun.awt.X11.XEmbeddedFrame
The extra java.awt.Panel is needed with JDK 1.4.2 (at least). The cursor will not change to the "resize cursor" when you try to resize the columns of the table without the panel (see Bug#58308). Now, If I put the java.awt.Panel back, the example will still flicker even if I subclass XEmbeddedFrame and reimplement update(). sun.awt.X11.XEmbeddedFrame is not public API and there are binary incompatability between JREs for the same platform. So how can I have a subclass that will work on all JREs, since I have to decide between int or long at compile time. Right now, we use reflection to work around this problem. To make a subclass that works on all JREs, you can either
a) Compile the subclass with the 'int' constructor on a platform where
sun.awt.X11.XEmbeddedFrame has 'int'; likewise with 'long' on a different
platform; then choose the appropriate one at runtime and load it through an
extra classloader. Or
b) Construct the appropriate subclass entirely at runtime using the Apache BCEL package,
parametrizing it with the information about sun.awt.X11.XEmbeddedFrame.
The class is converted from a byte[] to a Class object through a custom ClassLoader
whose findClass method just calls defineClass of the byte array.
How to solve the conflict between Bug#58308 and this one regarding the extra java.awt.Panel,
I don't know.
Having different classes at compile time for different platforms was not a option in JDK 1.3, because the same platform (Windows) had JREs with different WEmbeddedFrame classes. So if we compiled for one jre, we would not be able to run in the other jre. This may have change for newer JREs, I would have to check. I believe adding dependence to Apache BCEL package is not a option (over kill). This bug is still (in Eclipse 3.4M4) a major problem with SWT_AWT on Gtk. You can easily reproduce it with Albireo: Install Albireo from http://wiki.eclipse.org/Albireo_Download, then launch the examples plugin, and open the "Resize Flickering" view. At every mouse drag events, it resizes 4 adjacent SWT_AWT instances, and the flickering is *heavy*. You can see the flickering with the white color (#xffffff), although the background is set to grey. You are right in comment #7 that a subclass of sun.awt.X11.XEmbeddedFrame does not fix the flickering, at least not with JDK 1.5. Also, the following do not fix the problem: - Use of the SWT.NO_REDRAW_RESIZE flag in the constructor. - Use of the SWT.TRANSPARENT flag in the constructor. - OS.gtk_widget_set_redraw_on_allocate(composite.handle, false); - OS.gtk_fixed_set_has_window(composite.handle, false); Single-stepping through the code, I found that the flickering occurs in (or as a consequence of) the call OS.gtk_widget_size_allocate (topHandle, allocation); in Control.java, method setBounds (int x, int y, int width, int height, boolean move, boolean resize). I've been looking at the source code of gtk_widget_size_allocate and gtk_fixed_size_allocate (since the handle is an instance of SwtFixed, subclass of GtkFixed), but I don't understand where the area is filled with white color. Is SwtFixed the right class at all for this? The SWT_AWT.new_Frame method takes a Composite as argument. A Composite is meant to contain other widgets and therefore normally paints a background. But here, the entire Composite's area is covered by the Frame's area, therefore it should not paint a background. Possibly this could be achieved by using the GTK_NO_WINDOW flag - but this flag is not used by the implementation of Composite. - Is SwtFixed or GtkFixed the right type to use here at all? There's an old post by Havoc Pennington (you won't find a better expert on Gtk) http://dev.eclipse.org/mhonarc/lists/platform-swt-dev/msg00260.html who explains (AFAIU) that one should use NO_WINDOW in order to avoid flicker. I cannot reproduce this issue on SWT master as of today, GTK3.22, and Fedora 28. |