Making an Applet repaint in 1.1 compatible

I have no idea how to get an Applet to repaint.

I have added a Canvas with a working paint method.

If I obscure the applet and then uncover it, the applet paints all its bits including the canvas.

If I resize the applet, ditto.

But none of the following methods when invoked on the Applet instance have any effect:

  • doLayout()
  • validate()
  • invalidate()
  • repaint()

although I’ve only tried them individually, not together. And I haven’t touched the paint/repaint/update methods on Applet, so they are all vanilla.

Presumably there’s something obvious I’m missing, but I’ve spent 1 hour searching the API docs, trying things, and failing (took a while to prove that all the other bits were working and isolate it down to this).
Long time since I’ve needed to do this, and it reminds me how much I detest the AWT and how insanely confusing its paint system is :(.

:o
:o
:o
:o

OMFG!!!

Somebody call an Ambulance and direct it to blah’s location; STAT!

;D

Applet.repaint is the way to do it so far as I know…

The real question is; why on earth are YOU asking this question?

Edit; errr, I think I didnt’ understand the question; maybe Applet.update?

Because I offered to do a 1.1-compatible client for someone’s game, and everything was going fine until this point, after I’d written all the main rendering code etc. I’d remembered most of my 1.1 coding (although I’m using the 1.4 compiler for testing) without needing to look it up, but this one thing has stumped me, and the docs don’t suggest why it doesn’t work :frowning: :frowning: :(.

I’m touched you thought to order me an ambulance ;D.

appletInstance.repaint();

should work, but if its not I think we would need to see some code.

also canvasInstance.repaint(); will work, but may not be what you want to do.

Are you saying you want to repaint manually? The update()/repaint() methods are called automatically whenever the Applet deems necessary. If you call appletInstance.getGraphics() you’ll get the Graphics object tied to the screen, and if you manage your own buffer, just toss it on there when necessary, and it’ll INSTANTLY go to the screen :slight_smile: In my applets I always override repaint() and update() to do nothing, and just manually modify the Graphics context, getGraphics(), when I want to paint.

Standard way to paint 1.1-class applets:

  1. Wait until the applet is laoded AND visible on the screen, or else the next step will silently fail, and generate an Image that is illegal according to Sun’s own spec, with undefined width and height (I filed a bug on this more than a year ago for JApplet which has inherited this bug and though it was accepted it looks like the AWT bit is still broken today on 1.4.2_05)

  2. Create a back-buffer (prior to 1.4 you had to do this manually).

  3. Your class with paint logic (NOT the applet - separating logic from startup, you see?) paints to an Image, a buffer, somewhere.

  4. Your logic (usually the end of the loop in 1 above) that realises “oh my! something has changed! must repaint the applet!” invokes repaint on the applet.

  5. Repaint on the applet flips the buffer from part 1 onto the screen.

For crappy browsers (e.g. MSIE 4, and probably 5 and 6 too) the browser issues lots of pointless repaint requests of it’s own to the applet, so you used to have to have a second buffer as well, and only flip the buffer from 1 above to this second buffer when each frame was complete - whilst allowing MSIE-triggered repaints to always flip this buffer. Otherwise you got tearing (because you were seeing partially complete paints all the time).

Points to note:

  • I’ve had this problem before. I remember having it many times when I was doing 1.1-code years ago. I can’t for the life of me remember what I’ve done wrong, but I’m 90% sure it’s that I’ve got to call one additional / different method.

  • the applet has no idea when to paint. How would it know that I’ve just rendered a new frame? Sun didn’t write my client, they don’t even know that a new frame has been rendered, they have no WAY of knowing, hence they added a public repaint() method :). Or so I thought :(. Can’t remember what you actually have to call :(.

  • if you just leave applets to repaint themselves, and you have a good jvm, you will NEVER see a repaint if you stare at the screen and don’t move the mouse nor any windows. Sure, MSIE has this bug where it gets really scared and issues hundreds of repaints on very little pretence, but that’s a bug in their JVM, not behaviour to rely upon.

  • maybe I’m being completely stupid here, and talking BS. Am I? I don’t know, my confidence is being seriously eroded by my failure for the last 3+ hours to get something as simple as a damn applet to repaint!

  • here’s the smallest bit of code I’ve managed that exhibits the problem. As you can see, I’ve not done any overriding of critical Applet methods. Sob. The canvas has a known-working paint method simply because (as noted in OP) if you resize applet then it repaints correctly.


public class DisplayApplet extends Applet implements ActionListener, CanvasListener
{
      MyCanvas canvas;
      TextArea userInput;
      Button sendButton;
      
      Logger logger;
      
      public void init()
      {
            // configure the logging system with default settings
            BasicConfigurator.configure();
            logger = Logger.getLogger( getClass().getName() );
            
            canvas = new MyCanvas();
            canvas.addCanvasListener(this);
            
            userInput = new TextArea();
            sendButton = new Button( "Send Text");
            
            sendButton.addActionListener( this );
            
            setSize(400, 400);
            setLayout( new BorderLayout());
            
            add( canvas, BorderLayout.CENTER );
            add( userInput, BorderLayout.SOUTH );
            add( sendButton, BorderLayout.NORTH );
      }
      
      public void start()
      {
            canvas.createBuffer();
            canvas.start();
      }
      
      public void stop()
      {
            canvas.thread.stop();
      }
      
      public void actionPerformed( ActionEvent e )
      {
            String text = userInput.getText();
            userInput.setText("");
            canvas.receive(text);
      }
      
      public void bufferChanged()
      {
            logger.debug("buffer has changed, so repaint...");
            
            logger.error("repaint on Applet appears not to work !?!");
            repaint();
      }
}

as Malohkan said, using the applet’s repaint() method is not so good, as the repaint() method is actually just a suggestion to the AWT that it should update the screen or component, and it might not do it immediately…

instead, use canvas.getGraphics() to get the canvas’s graphics context, then just draw your buffer straight onto it… i cant test if this works with your code cause you didn’t provide the MyCanvas class you are using.

something along the lines of:

public void bufferChanged()
{
logger.debug(“buffer has changed, so repaint…”);

canvas.getGraphics().drawImage(myBackBuffer);
}

and then override the canvas’s paintComponent(Graphics g) method with

g.drawImage(myBackBuffer);

so that when the JVM wants to repaint, it will still work.

and i have never really noticed tearing in my applets doing this but i would say in a game with a lot of moving things, and a JVM that likes to repaint a lot, you might get it… i wouldn’t, however, override the repaint method to do nothing unless you are constantly updating your graphics, as it’ll cause the graphics to vanish until the next update when you minimize or move another window over the applet

Hmm. People still misunderstanding. I’m obviously not doing well at explaining myself :(. Sorry.

[quote]the repaint() method is actually just a suggestion to the AWT that it should update the screen or component, and it might not do it immediately…
[/quote]
It does it soon enough as makes no difference. Not a problem.

I need to get the applet to repaint, not merely one of it’s components. Because the applet could have any of many different things inside it, and I need a decoupled way of getting the entire applet to repaint.

This is not rocket-science: the design of AWT and Swing has this concept that you usually just repaint the top-level container, and that doing so will automatically re-paint all the things inside it, walking down the container tree as necessary.

This is horribly hacky. But…it’s given me a cunning idea. A light bulb just went on, and I have a vague memory that you should never extend java.awt.Canvas because it’s buggered as an AWT component - it’s missing some of the core behaviours (or disables them) and hence you should always extend Panel or similar instead.

When I first learnt java (1.0/1.1) we were always taught to extend Canvas, so I did it this time without thinking. But maybe this is the problem!

Hmm. I will go experiment with changing to extending a different class (and check the javadocs for Canvas again!)

There is no such method ;D. Please read the title of my post! If it weren’t for this, I would be doing things very differently and wouldn’t have a problem (I know exactly how to paint things in Swing…)

Um. I didn’t think anyone mentioned overriding the repaint method to do nothing?

[quote]This is horribly hacky. But…it’s given me a cunning idea. A light bulb just went on, and I have a vague memory that you should never extend java.awt.Canvas because it’s buggered as an AWT component - it’s missing some of the core behaviours (or disables them) and hence you should always extend Panel or similar instead.
[/quote]
No improvement.

FYI, “canvas.repaint();” works fine. As you pointed out, this is definitely a way of getting it to paint, it’s just that I really wanted something cleaner. But having wasted many hours on this it looks like I’ll just have to stick with writing my own proprietary Container-painting system (which is part of the core of the AWT). Grr. I feel very stupid ending up doing this :(.

I did think of trying “super.super.repaint()” on the Applet subclass, to bypass Applet (which I’m beginning to think is, perhaps, broken: perhaps it silently overrides and destroys the Container paint methods?) and get the Container to paint itself. But that threw up the interesting problem that “super.super” is illegal syntax, and I’m not sure how you can express that concept syntactically ;D. ???

I used a JPanel to implement my own applet one time just to see if it was faster; needless to say it wasn’t. :wink:

Your canvas implements runnable? It looks like your are stopping a running thread in the canvas, canvas.thread.stop();. Maybe there is a weird thread thing going on.

I could come up with a bunch of suggestions for tracking it down, but many of them would sound rather silly. Sometimes a silly bug leads to silly testing. Like disabling the canvas thread and making a test method that alters the buffer and sleeps a bit, so both classes are using the same thread.

The way you did it does look like it should work. I’m assuming you got the log messages, so its being called. The only time I ever used canvas was when I called it directly to repaint. I usually just draw on the base component these days, like a Frame. I also made my own set of components that take images.

[quote]Your canvas implements runnable?
[/quote]
That’s just a thread that paints to the backbuffer. The problem is the backbuffer not being flipped to the screen, i.e. Applet.repaint() does nothing (it ought to paint its children, as per Container (its superclass).

!!! That’s not going to be good when you want to add multiple things to your Container…

i.e. you do what lots of people used to do - completely ignore AWT’s architecture because it’s a confusing pile of crap, designed by people who either think “design” is something you spend five seconds thining about, or were told they weren’t allowed to design it first they had to just implement it NOW, or simply had no idea how to write usable library code (or any other excuse that explains why this “architecture” has clearly had no real design phase).

Yes, I am (and always have been) bitter about the pointlessly obtuse and complex rendering architecture of AWT. It didn’t have to be this way :(.

I made my own Component, Container, Panel, Button and Label classes that can take images. Mostly because if you want an irregularly shaped button, like a bitmasked circle, regular heavyweight AWT would blank out the background with a rect of background color not the panel image. I also wanted images on panels so I could put other components on the image. Its also active rendering and draws on a Frame’s bufferstrategy.

Maybe its overkill, but I had fun doing it and learned a few things.

I should also add that I programmed a small tile graphic strategy game in C on the AtariST way back in 1991. Compiled to 180k, 100k graphics and needed to fit on a 512k machine with room to run my A* pathfinding. Back then you just managed it all yourself. I had buttons, unit info and a world map on the right side. A text bar for info across the bottom and the main game area. I also had and economic phase screen. So I’m accustomed to managing it manually.

If I was doing a business program I would use Swing, there is no way I would go and make all those other component types :slight_smile:

AWT repaint and update 101:

the function repaint in class Component will be called by system when some part of the peer needs redraw ( use a window to rub over your component you will see repaint will be called ) NEVER NEVER NEVER call it by your code.

the function update will be called by java whenever your component triggered repaint ( multiple calls to repaint maybe combined to one call to update for perfomance reasons)

i hope this helps blah * 3

[quote]AWT repaint and update 101:

the function repaint in class Component will be called by system when some part of the peer needs redraw ( use a window to rub over your component you will see repaint will be called ) NEVER NEVER NEVER call it by your code.
[/quote]
Why not?