Java Solaris Communities Sun Store Join SDN My Profile Why Join?
 
Bug Database
Bug Detail
Quick Lists
Top 25 Bugs
Top 25 RFE's
Recently Closed Bugs
Printable Page Printable Page


Bug Database
Bug ID: 4968008
Votes 1
Synopsis Canvas retains previous screen pixels when moved to new location
Category java:classes_awt
Reported Against 1.4.2 , tiger-beta2
Release Fixed
State 11-Closed, Not Reproducible, bug
Priority: 3-Medium
Related Bugs 5013045
Submit Date 12-DEC-2003
Description


FULL PRODUCT VERSION :
Java(TM) Plug-in: Version 1.4.1_01
Using JRE version 1.4.1_01 Java HotSpot(TM) Client VM

FULL OPERATING SYSTEM VERSION : Windows 98 SE


EXTRA RELEVANT SYSTEM CONFIGURATION :
Same behavior when JRE used in Netscape 7.01 or Internet
Explorer 6.0 SP1

A DESCRIPTION OF THE PROBLEM :
The canvas used for a ToolTip usually gets painted with the
proper background and text.  However, about 5% of the time
this does not happen.  Instead, the pixels that should have
been under the canvas in its previous location are used
instead of the proper display.



STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1.  Run the applet with source code below or see it in
action at http://segal.org/java/tooltip2/index.html

2.  Hold the mouse pointer over one checkbox until the
  ToolTip appears.  Then move to the other checkbox.  Keep
doing this.

EXPECTED VERSUS ACTUAL BEHAVIOR :
Expected:  The ToolTip should always display the pale
yellow background and the Tooltip text.

Actual: About 5% of the time the new ToolTip canvas does
not paint the ToolTip text.  Instead, the pixels that were
covered by the ToolTip canvas in its previous location are
displayed.

Behavior is always OK in other JREs, including Apple's MRJ
3.3 and  customer 's JRE version 3809.

Adding update and validate/invalidate code does not
overcome the bug.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.applet.*;
import java.awt.*;
import java.awt.event.*;

public class Tooltip2 extends Applet  {

public void init()
{
	setBackground(new Color(225,225, 255));
	try
	{
		FrameForApplet frameForApplet = new FrameForApplet("A frame");
	}
	catch (Throwable e)
	{
		System.out.println("Can't use Frame: " + e);
		return;
	}
}

public void paint(Graphics g)
{
	g.drawString("A Frame appears with two Checkboxes with ToolTips", 10 ,
25);
}
}  // END OF Class Tooltip2


final class FrameForApplet extends Frame implements WindowListener,
ItemListener  {

TipCheck checkbox1, checkbox2;
static Frame frame;
static Insets insets;
static TipCanvas tipCanvas;
static Point pointOnFrame;

FrameForApplet(String s)
{
	super(s);
	setLayout(new GridBagLayout());
	frame = this;
	addNotify();
	insets = getInsets();
	setBounds( 100, 100, 300, 300);
	checkbox1 = new TipCheck("My checkbox 1", new Color(255, 200, 255),
Color.black,
		new Font("Serif", Font.BOLD, 25), "Tooltip 1 text", this);
	constrain(this, checkbox1, 0, 0, 1, 1,
		GridBagConstraints.NORTH, 0, 0, 0, 0, GridBagConstraints.NONE,
0, 0);
	checkbox2 = new TipCheck("My checkbox 2", new Color(200, 255, 255),
Color.black,
		new Font("Serif", Font.BOLD, 25), "Tooltip 2 text", this);
	constrain(this, checkbox2, 0, 1, 1, 1,
		GridBagConstraints.NORTH, 10, 0, 0, 0, GridBagConstraints.NONE,
0, 0);
	show();
	addWindowListener(this);
	tipCanvas = new TipCanvas();
}

static final void addTip(String tip)
{
	tipCanvas.setTip(tip);
	frame.add(tipCanvas, 0);
	tipCanvas.setSize(tipCanvas.getPreferredSize());
	Point tipLocation = tipCanvas.modifyLocation(pointOnFrame);
	tipCanvas.setLocation(tipLocation.x, tipLocation.y);
}

static final void removeTip()
{
	if (tipCanvas.isShowing()) frame.remove(tipCanvas);
}

static final void constrain(Container container, Component component, int
grid_x, int grid_y,
		int grid_width, int grid_height, int anchor, int topPad, int
leftPad, int bottomPad,
		int rightPad, int fill, double weightx, double weighty)
{
	GridBagConstraints c = new GridBagConstraints();
	c.gridx = grid_x;
	c.gridy = grid_y;
	c.gridwidth = grid_width;
	c.gridheight = grid_height;
	c.anchor = anchor;
	c.insets.top   = topPad;
	c.insets.left  = leftPad;
	c.insets.bottom= bottomPad;
	c.insets.right = rightPad;
	c.fill = fill;
	c.weightx = weightx;
	c.weighty = weighty;
	((GridBagLayout)container.getLayout()).setConstraints(component, c);
	container.add(component);
}
public void windowOpened(WindowEvent we){}

public void windowClosed(WindowEvent we){}

public void windowIconified(WindowEvent we){}

public void windowDeiconified(WindowEvent we){}

public void windowActivated(WindowEvent we){}

public void windowDeactivated(WindowEvent we){}

public void windowClosing(WindowEvent we)
{
	if (we.getSource() == this)
	{
		setVisible(false);
		dispose();
	}
}
public void itemStateChanged(ItemEvent ie){}
}  // END OF Class FrameForApplet



interface TipComponent {

String getTip();

}  //  END OF Interface TipComponent



final class TipThread extends Thread  {

TipComponent tipComponent;
long timeEntered;
boolean stopThreadSoon;
boolean added;
static final long DELAY = 900;

TipThread(TipComponent tipComponent)
{
	this.tipComponent = tipComponent;
	timeEntered = System.currentTimeMillis();
	stopThreadSoon = false;
	added = false;
}

final void endTheThread()
{
	stopThreadSoon = true;
}

public void run()
{
	while (true)
	{
		try
		{
			sleep(50);
		}
		catch (Exception e)
		{
			System.out.println("Couldn't sleep");
		}
		if (stopThreadSoon) break; // stops the thread
		if (!added && (System.currentTimeMillis() - timeEntered >
DELAY))
		{
			FrameForApplet.addTip(tipComponent.getTip());
			added = true;
		}
	}
}
}  // END OF Class TipThread
	


class TipCanvas extends Canvas  {
	
String tip;
	
TipCanvas()
{
	setFont(new Font("Dialog", Font.PLAIN, 14));
	setBackground(new Color(255, 255, 224));  // light yellow
}

final void setTip(String tip)
{
	this.tip = tip;
}

final Point modifyLocation(Point p)
{
	try
	{
		Dimension toolTipDimension = getSize();
		Dimension frameDimension = FrameForApplet.frame.getSize();
		p.x += 4;
		if (p.x > frameDimension.width - toolTipDimension.width -
FrameForApplet.insets.right)
			p.x = frameDimension.width - toolTipDimension.width -
FrameForApplet.insets.right;
		if (p.x < FrameForApplet.insets.left) p.x =
FrameForApplet.insets.left;
		int origY = p.y;
		p.y = p.y + 20;
		if (p.y > frameDimension.height - toolTipDimension.height -
FrameForApplet.insets.bottom)
			p.y = origY - toolTipDimension.height - 1;
		return(p);
	}
	catch (Exception e)
	{
		System.out.println("Error modifying location");
		return (p);
	}
}

public Dimension getPreferredSize()
{
	Graphics g = getGraphics();
	if (g == null) return (new Dimension(0, 0));
	FontMetrics fm = g.getFontMetrics();
	Dimension d = new Dimension(fm.stringWidth(tip) + 8, fm.getHeight() +
4);
	g.dispose();
	return (d);
}

public void paint(Graphics g)
{
	Dimension toolTip = getSize();
	g.drawRect(0, 0, toolTip.width-1, toolTip.height - 1);
	g.drawString(tip, 4, g.getFontMetrics().getAscent() + 2);
}
}  // END OF Class TipCanvas



class TipCheck extends Checkbox implements MouseListener, MouseMotionListener,
TipComponent  {

TipThread tipThread;
String tip;

	
TipCheck(String label, Color backgroundColor, Color foregroundColor, Font font,
String tip,
	Container container)
{
	setLabel(label);
	setBackground(backgroundColor);
	setForeground(foregroundColor);
	setFont(font);
	initTip(tip);
	addItemListener((ItemListener)container);
}

final void initTip(String tip)
{
	this.tip = tip;
	addMouseListener(this);
	addMouseMotionListener(this);
}

public String getTip()
{
	return(tip);
}

final void setPointOnFrame(int x, int y)
{
	Point frameLocation = FrameForApplet.frame.location();
	Point screenLocation = getLocationOnScreen();
	FrameForApplet.pointOnFrame = new Point(screenLocation.x + x -
frameLocation.x,
										
	 screenLocation.y + y - frameLocation.y);
}

final void removeTip()
{
	FrameForApplet.removeTip();
	tipThread.endTheThread();
}

public synchronized void mouseEntered(MouseEvent me)
{
	setPointOnFrame(me.getX(), me.getY());
	tipThread = new TipThread(this);
	tipThread.start();
}

public synchronized void mouseExited(MouseEvent me){ removeTip();}

public synchronized void mouseReleased(MouseEvent me){}

public synchronized void mousePressed(MouseEvent me) {	removeTip();}

public synchronized void mouseClicked(MouseEvent me) {}

public synchronized void mouseMoved(MouseEvent me){	setPointOnFrame(me.getX
(), me.getY());}

public synchronized void mouseDragged(MouseEvent me) {}

}  // END OF Class TipCheck
---------- END SOURCE ----------
(Incident Review ID: 181756) 
======================================================================
Work Around


<FrameForApplet.addTip> method should be modified this way:

*** Tooltip2.java Wed Dec 24 11:19:42 2003
--- _Tooltip2.java Wed Dec 24 11:20:36 2003
***************
*** 58,72 ****
--- 58,74 ----
              tipCanvas = new TipCanvas();
          }

      static final void addTip(String tip)
          {
+             tipCanvas.setVisible(false);
              tipCanvas.setTip(tip);
              frame.add(tipCanvas, 0);
              tipCanvas.setSize(tipCanvas.getPreferredSize());
              Point tipLocation = tipCanvas.modifyLocation(pointOnFrame);
              tipCanvas.setLocation(tipLocation.x, tipLocation.y);
+             tipCanvas.setVisible(true);
          }

      static final void removeTip()
          {
              if (tipCanvas.isShowing()) frame.remove(tipCanvas);
--------------

This will allow to:
1. avoid the bug reported;
2. improve visibility of showing TipCanvas:
   - now it jumps before it is completely shown because its location is
     changed after it's already shown of the screen.
     
  xxxxx@xxxxx   2003-12-24


======================================================================
Evaluation


The bug is reproducible on Windows 98/2000/XP upto JDK 1.5.0-b29.
The bug is not reproducible on Windows 98/2000/XP since JDK 1.5.0-b30.

I was able to put some debugging trases in JDK 1.5.0-b29 classes and found out
that the bug was caused by a thread race. Probably it was fixed by some timings
in JDK 1.5.0-b30. Here is a part of thread dump I got in b29:

Thread[AWT-EventQueue-0,6,main]
 0: java.lang.Thread.dumpThreads(Native Method)
 1: java.lang.Thread.getAllStackTraces(Thread.java:1386)
 2: java.awt.EventQueue.postEvent(EventQueue.java:244)
 3: java.awt.EventQueue.postEventPrivate(EventQueue.java:204)
 4: java.awt.EventQueue.postEvent(EventQueue.java:175)
 5: sun.awt.PostEventQueue.flush(SunToolkit.java:993)
 6: sun.awt.SunToolkit.flushPendingEvents(SunToolkit.java:510)
 7: java.awt.EventQueue.getNextEvent(EventQueue.java:371)
 8: java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:189)
 9: java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:163)
 10: java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:157)
 11: java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:149)
 12: java.awt.EventDispatchThread.run(EventDispatchThread.java:110)

Thread[Thread-7,6,main]
 0: sun.awt.windows.WComponentPeer.start(Native Method)
 1: sun.awt.windows.WComponentPeer.<init>(WComponentPeer.java:523)
 2: sun.awt.windows.WCanvasPeer.<init>(WCanvasPeer.java:29)
 3: sun.awt.windows.WToolkit.createCanvas(WToolkit.java:366)
 4: java.awt.Canvas.addNotify(Canvas.java:74)
 5: java.awt.Container.addImpl(Container.java:1050)
 6: java.awt.Container.add(Container.java:388)
 7: FrameForApplet.addTip(Tooltip2.java:76)
 8: TipThread.run(Tooltip2.java:180)

In EventDispatchThread dump 0-1st lines are calls added by me to get this dump.
3rd line shows that it's EventQueue.postEvent call. It's a PaintEvent and it was posted to the queue
by this call chain:  AwtComponent::WmPaint -> WComponentPeer.handleExpose.
It's the moment when TipCanvas first showed. In EventQueue.postEvent PaintEvent's should
be coalesced. But in this case the first PaintEvent on the TipCanvas isn't coalesced due to
its peer hasn't been created yet and is equal to null. Look at TipThread dump. It's just in
the place of peer creating that is not finished though.
Thus TipCanvas misses its first PaintEvent that has a clip of its full size. Next PaintEvent's contain
a clip of smaller size that results in that TipCanvas is painted incompletely.

In b30 TipCanvas peer is in time for coalescing all its PaintEvent's. I added 10 mls delay into EventQueue.postEvent
to simulate this condition in b29. TipCanvas was painted properly without any artifacts.
Here's TipThread dump I got at the moment of TipCanvas showing:

Thread[Thread-7,6,main]
 0: java.lang.Thread.sleep(Native Method)
 1: TipThread.run(Tooltip2.java:157)

You can see that it passed FrameForApplet.addTip call line where TipCanvas peer is created.
--------------------------

Since the problem is no longer reproducible the bug will be commited to next release.
  xxxxx@xxxxx   2003-12-22

======================================================================




I found out the bug is reproducible up to b29 not only on Windows but
on Linux and Solaris as well. It commits that the problem is "shared".
Since b30 it's not reproducible no matter where.
I put a fix into Suggested. The idea is as follows:
- At the time when creation of a peer class hasn't been finished but the
  native component had been created and events are coming we use (in     
<EventQueue.postEvent>) temporary reference to the peer object (initialized
  in the peer's constructor before the native component's creation).
  And if an event reaches <Component.dispatchEvent> but the peer still equals
  null we wait under TreeLock until it is created.
This approach allow us to avoid modification of native code, organizing some
additional event queues etc. And we keep backward compatibility (not touching
peer creation process).
  xxxxx@xxxxx   2003-12-29

======================================================================




Here's not complicated automated test that can reproduce the problem (up to b30):

import java.awt.*;
import java.awt.event.*;
import java.lang.reflect.InvocationTargetException;

public class RaceTest {
    static Frame frame = new Frame("Test Frame");
    static MyCanvas canvas = new MyCanvas();

    static int counter = 50;
    static int failedCount = 0;

    public static boolean cond;
    public static boolean passed;

    public static void main(String[] args) {
        frame.setBounds(100, 100, 400, 200);
        frame.setLayout(null);        
        frame.setVisible(true);

        test();

        System.err.println("\nFAILED - " + failedCount + "\nPASSED - " + (counter - 
failedCount));
        System.err.println("\nTest " + (failedCount > 0 ? "FAILED!!!" : "PASSED."));

        System.exit(0);
    }

    static void test() {
        canvas.setLocation(150, 50);

        for (int i=0; i<counter; i++) {
            passed = false;

            waitForIdle();
            frame.add(canvas);
            waitForIdle();

            if (!passed) {
                failedCount++;
            }
        
            frame.remove(canvas);
        }
    }

    static void waitForIdle() {
        try {
            Toolkit.getDefaultToolkit().sync();
            sun.awt.SunToolkit.flushPendingEvents();
            EventQueue.invokeAndWait( new Runnable() { 
                    public void run() {} // Dummy implementation
                });
        } catch(InterruptedException ie) {
        } catch(InvocationTargetException ite) {
        }

	try {
            Thread.sleep(100);
        } catch (InterruptedException ie) {
	}
    }
}

class MyCanvas extends Button/*Canvas*/ {
    public static final int width = 50;
    public static final int height = 50;
    
    MyCanvas() {
        setSize(width, height);
        setBackground(Color.lightGray);
    }

    public void paint(Graphics g) {
        g.setColor(Color.green);
        Rectangle r = g.getClipBounds(new Rectangle());
        g.fillRect(r.x, r.y, r.width, r.height);
        g.setColor(Color.red);
        g.drawRect(0, 0, width-1, height-1);

        RaceTest.passed = true;
    }
}
  xxxxx@xxxxx   2003-12-29

======================================================================</TEXTAREA>
		      </td>
                    </tr>

                    <TR>
                      <TD colspan="2" bgcolor="#BFBFBF"> </td>
                    </tr>

                  </table>
                  <!-- SUN TEXTAREA BUG DETAILS TABLE END -->

                     </td>
                   </tr>
                </table>
                <!-- OUTER MEMO TEXT FORM TABLE BEGIN -->
                 
		<BR>

<a name="bottom"></a>
                    <!-- BUTTON TABLE 2 BEGIN -->
                    <TABLE align="center">
                      <TR>
                        <TD bgcolor="white"><img src="/bugz/images/dot.gif">
                          <INPUT type="submit" name="EditBugPageAction/form.save" value="Save">
		          <img src="/bugz/images/dot.gif" width="5">
                        </td>

                        <TD bgcolor='white'>
                          <img src="/bugz/images/dot.gif" width="10">
			</td>

                        <TD bgcolor="white">
                          <img src="/bugz/images/dot.gif">
                          <INPUT type="submit" name="EditBugPageAction/form.clear" value="Clear"><img src="/bugz/images/dot.gif" width="5">
                        </td>

                        <TD bgcolor='white'>
			  <img src="/bugz/images/dot.gif" width="10">
                        </td>

                        <TD bgcolor="white">
                          <img src="/bugz/images/dot.gif">
                          <INPUT type="submit" name="EditBugPageAction/form.cancel" value="Cancel">
			  <img src="/bugz/images/dot.gif" width="5">
			</td>
                      </tr>
                    </table>
                    <!-- BUTTON TABLE 2 END -->


                   <!-- REQUIRED FIELD LEGEND TABLE BEGIN -->
                   <TABLE border="0" cellspacing="0" cellpadding="2" width="100%">
                    <TR>
                      <TD colspan="5" align="left">
                        <font color="red">*</font> <font size="-1" color="darkblue">indicates Required field </font>
                      </td>
		      <TD align="right">
			<a HREF="#middle"><font size='3' face='Geneva, Arial, Helvetica' color='blue'>Bug Release Data</font></a> 
			<img src="/bugz/images/dot.gif" width="10">
		        <a HREF="#top"><font size='3' face='Geneva, Arial, Helvetica' color='blue'>Top of Page</font></a> 
		      </td>
                    </tr>
                  </table>
                  <!-- REQUIRED FIELD LEGEND TABLE END -->
		    

                  </form>
                </td>
              </tr>
            </table>
            <!-- CONTENT TABLE END -->

            <!-- PAGE FOOTER TABLE BEGIN -->
            <TABLE border="0" cellspacing="0" cellpadding="2" width="100%">
              <!-- WEBTONE ROW BEGIN -->

              <TR>
                <TD bgcolor="#CC0033"><img src="/bugz/images/dot.gif" width=
                "151" height="1" alt="webtone" border="0"></td>

                <TD bgcolor="#CC9900"><img src="/bugz/images/dot.gif" width=
                "151" height="1" alt="webtone" border="0"></td>

                <TD bgcolor="#CCCC33"><img src="/bugz/images/dot.gif" width=
                "151" height="1" alt="webtone" border="0"></td>

                <TD bgcolor="#FF9900"><img src="/bugz/images/dot.gif" width=
                "151" height="1" alt="webtone" border="0"></td>
              </tr>
              <!-- WEBTONE ROW END -->

              <TR>
                <TD bgcolor="#000000" colspan="4">
                  <TABLE border="0" cellspacing="0" cellpadding=
                  "4">
                    <TR>
                      <TD><!-- COPYRIGHT NOTICE BEGIN -->
                       <FONT face="Geneva, sans-regular"
                      color="#FFFFFF" size="2">Copyright 2000-2005 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, CA 95054 USA. <STRONG>All rights
                      reserved.</strong><BR></font>
                       <A href=
                      "http://www.sun.com/share/text/SMICopyright.html"
                       target="_blank"><FONT color="#FFFFFF">Legal
                      Terms</font></a>. <A href=
                      "http://www.sun.com/privacy" target=
                      "_blank"><FONT color="#FFFFFF">Privacy
                      Policy</font></a>. <A href=
                      "mailto:bugtraq_support@Sun.COM"><FONT color=
                      "#FFFFFF">Feedback</font></a>.
                      <!-- COPYRIGHT NOTICE END -->
                      </td>
                    </tr>
                  </table>
                </td>
              </tr>
            </table>
            <!-- PAGE FOOTER TABLE END -->
          </td>
        </tr>
      </table>
      <!-- MASTER TABLE END -->
    </center>
  </body>
</html>

  xxxxx@xxxxx   2004-08-20
Comments
  
  Include a link with my name & email   


PLEASE NOTE: JDK6 is formerly known as Project Mustang