This is the mail archive of the java-patches@gcc.gnu.org mailing list for the Java project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Fixes to AWT PaintEvent handling


Hello all,

Here is a patch that reworks and fixes PaintEvent handling in Component
and Container classes.  This patch eliminates redundant calls to
paint()/update(), and will make sure that heavyweight components are
repainted properly along with the lightweight peers.

Firstly, it consolidates the PaintEvent handling between the two classes
so that only one paint()/update() method call results from a single
PaintEvent.  This is done by relocating PaintEvent processing for both
Component and Container objects to GtkComponentPeer's handleEvent().

Secondly, it adds a mechanism to explicitly repaint a heavyweight
component by calling native GTK methods to invalidate and update the
corresponding widget.

I originally had problems with repainting heavyweight peers, where
invalidating a widget caused an expose event from GTK, which caused a
PaintEvent in AWT, which then caused AWT to invalidate the same widget
again, resulting in an infinite loop.  So I added mechanisms to filter
out these unwanted expose events from GTK to break the loop.

Any comments and are welcome.

-David Jee


2004-01-23  David Jee  <djee@redhat.com>

        * gnu/java/awt/peer/gtk/GtkComponentPeer.java
        (handleEvent): Implemented. Handles PaintEvents.
        (paint): Implemented. Use GTK native methods to queue updates
        for this heavyweight peer.
        * gnu/java/awt/peer/gtk/GtkContainerPeer.java
        (handleEvent): Moved the code to GtkComponentPeer.handleEvent().
        Just call super.handleEvent()
        * java/awt/Component.java
        (paint): Implemented. Explictly paint the heavyweight peer.
        (update): Clear the background for heavyweight components.
        (paintAll): No need to call peer.paint() anymore.
        (processEvent): Don't process PaintEvents here. It's now done in
        the peer's handleEvent().
        (processPaintEvent): Removed.
        * java/awt/Container.java
        (paint): No need to call super.paint(). Visit heavyweight
        children as well.
        (update): Don't clear the background here.  It's done in
        Component.update().
        (visitChildren): Added check to not recurse into Containers.
        * jni/gtk-peer/gnu_java_awt_peer_gtk_GtkComponentPeer.c
        (filter_expose_event_handler): New method.  Filter unwanted
        expose events while painting heavyweight peers.
        (Java_gnu_java_awt_peer_gtk_GtkComponentPeer_addExposeFilter):
        New method. Connect filter and block pre_event_handler.
        (Java_gnu_java_awt_peer_gtk_GtkComponentPeer_removeExposeFilter):
        New method. Disconnect filter and unblock pre_event_handler.
        (Java_gnu_java_awt_peer_gtk_GtkComponentPeer_gtkWidgetQueueDrawArea):
        New method. Invalidate and update given area.
        * jni/gtk-peer/gnu_java_awt_peer_gtk_GtkEvents.c
        (pre_event_handler): Add checks for unwanted expose events.


Index: gnu/java/awt/peer/gtk/GtkComponentPeer.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/gnu/java/awt/peer/gtk/GtkComponentPeer.java,v
retrieving revision 1.15
diff -u -r1.15 GtkComponentPeer.java
--- gnu/java/awt/peer/gtk/GtkComponentPeer.java	20 Jan 2004 21:24:09 -0000	1.15
+++ gnu/java/awt/peer/gtk/GtkComponentPeer.java	23 Jan 2004 21:01:19 -0000
@@ -91,6 +91,9 @@
   native void gtkWidgetSetCursor (int type);
   native void gtkWidgetSetBackground (int red, int green, int blue);
   native void gtkWidgetSetForeground (int red, int green, int blue);
+  native void gtkWidgetQueueDrawArea(int x, int y, int width, int height);
+  native void addExposeFilter();
+  native void removeExposeFilter();
 
   void create ()
   {
@@ -217,6 +220,37 @@
   
   public void handleEvent (AWTEvent event)
   {
+    int id = event.getID();
+
+    switch (id)
+      {
+      case PaintEvent.PAINT:
+      case PaintEvent.UPDATE:
+        {
+          try 
+            {
+              Graphics g = getGraphics ();
+          
+              // Some peers like GtkFileDialogPeer are repainted by Gtk itself
+              if (g == null)
+                break;
+
+              g.setClip (((PaintEvent)event).getUpdateRect());
+
+              if (id == PaintEvent.PAINT)
+                awtComponent.paint (g);
+              else
+                awtComponent.update (g);
+
+              g.dispose ();
+            }
+          catch (InternalError e)
+            {
+              System.err.println (e);
+            }
+        }
+        break;
+      }
   }
   
   public boolean isFocusTraversable () 
@@ -235,7 +269,21 @@
 
   public void paint (Graphics g)
   {
-    awtComponent.paint (g);
+    Component parent = awtComponent.getParent();
+    GtkComponentPeer parentPeer = null;
+    if ((parent instanceof Container) && !parent.isLightweight())
+      parentPeer = (GtkComponentPeer) parent.getPeer();
+
+    addExposeFilter();
+    if (parentPeer != null)
+      parentPeer.addExposeFilter();
+
+    Rectangle clip = g.getClipBounds();
+    gtkWidgetQueueDrawArea(clip.x, clip.y, clip.width, clip.height);
+
+    removeExposeFilter();
+    if (parentPeer != null)
+      parentPeer.removeExposeFilter();
   }
 
   public Dimension preferredSize ()
Index: gnu/java/awt/peer/gtk/GtkContainerPeer.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/gnu/java/awt/peer/gtk/GtkContainerPeer.java,v
retrieving revision 1.7
diff -u -r1.7 GtkContainerPeer.java
--- gnu/java/awt/peer/gtk/GtkContainerPeer.java	13 Jan 2004 17:55:20 -0000	1.7
+++ gnu/java/awt/peer/gtk/GtkContainerPeer.java	23 Jan 2004 21:01:19 -0000
@@ -102,37 +102,7 @@
 
   public void handleEvent (AWTEvent event)
   {
-    int id = event.getID();
-      
-    switch (id)
-      {
-      case PaintEvent.PAINT:
-      case PaintEvent.UPDATE:
-	{
-	  try 
-	    {
-	      Graphics g = getGraphics ();
-
-	      // Some peers like GtkFileDialogPeer are repainted by Gtk itself
-	      if (g == null)
-	        break;
-
-	      g.setClip (((PaintEvent)event).getUpdateRect());
-
-	      if (id == PaintEvent.PAINT)
-		awtComponent.paint (g);
-	      else
-		awtComponent.update (g);
-	      
-	      g.dispose ();
-	    } 
-	  catch (InternalError e)
-	    { 
-	      System.err.println (e);
-	    }
-	}
-	break;
-      }
+    super.handleEvent(event);
   }
 
   public void beginLayout () { }
Index: java/awt/Component.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/awt/Component.java,v
retrieving revision 1.33
diff -u -r1.33 Component.java
--- java/awt/Component.java	22 Jan 2004 09:54:15 -0000	1.33
+++ java/awt/Component.java	23 Jan 2004 21:01:21 -0000
@@ -1702,6 +1702,9 @@
    */
   public void paint(Graphics g)
   {
+    // Paint the heavyweight peer
+    if (!isLightweight() && peer != null)
+      peer.paint(g);
   }
 
   /**
@@ -1719,6 +1722,15 @@
    */
   public void update(Graphics g)
   {
+    if (!isLightweight())
+      {
+        Rectangle clip = g.getClipBounds();
+        if (clip == null)
+          g.clearRect(0, 0, width, height);
+        else
+          g.clearRect(clip.x, clip.y, clip.width, clip.height);
+      }
+
     paint(g);
   }
 
@@ -1732,8 +1744,6 @@
   {
     if (! visible)
       return;
-    if (peer != null)
-      peer.paint(g);
     paint(g);
   }
 
@@ -2787,8 +2797,6 @@
 
     if (e instanceof FocusEvent)
       processFocusEvent((FocusEvent) e);
-    else if (e instanceof PaintEvent)
-      processPaintEvent((PaintEvent) e);
     else if (e instanceof MouseWheelEvent)
       processMouseWheelEvent((MouseWheelEvent) e);
     else if (e instanceof MouseEvent)
@@ -4222,42 +4230,6 @@
 
     newEvent.setUpdateRect(union);
     return newEvent;
-  }
-
-  /**
-   * Does the work for a paint event.
-   *
-   * @param event the event to process
-   */
-  private void processPaintEvent(PaintEvent event)
-  {
-    // Can't do graphics without peer
-    if (peer == null)
-      return;
-
-    Graphics gfx = getGraphics();
-    try
-      {
-	Shape clip = event.getUpdateRect();
-	gfx.setClip(clip);
-
-	switch (event.id)
-	  {
-	  case PaintEvent.PAINT:
-	    paint(gfx);
-	    break;
-	  case PaintEvent.UPDATE:
-	    update(gfx);
-	    break;
-	  default:
-	    throw new IllegalArgumentException("unknown paint event");
-	  }
-	event.consume ();
-      }
-    finally
-      {
-	gfx.dispose();
-      }
   }
 
   /**
Index: java/awt/Container.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/awt/Container.java,v
retrieving revision 1.31
diff -u -r1.31 Container.java
--- java/awt/Container.java	21 Jan 2004 14:39:15 -0000	1.31
+++ java/awt/Container.java	23 Jan 2004 21:01:21 -0000
@@ -663,8 +663,9 @@
   {
     if (!isShowing())
       return;
-    super.paint(g);
-    visitChildren(g, GfxPaintVisitor.INSTANCE, true);
+    // Visit heavyweights as well, in case they were
+    // erased when we cleared the background for this container.
+    visitChildren(g, GfxPaintVisitor.INSTANCE, false);
   }
 
   /**
@@ -678,11 +679,6 @@
    */
   public void update(Graphics g)
   {
-    Rectangle clip = g.getClipBounds();
-    if (clip == null)
-      g.clearRect(0, 0, width, height);
-    else
-      g.clearRect(clip.x, clip.y, clip.width, clip.height);
     super.update(g);
   }
 
@@ -1204,8 +1200,12 @@
         for (int i = ncomponents - 1; i >= 0; --i)
           {
             Component comp = component[i];
+            // If we're visiting heavyweights as well,
+            // don't recurse into Containers here. This avoids
+            // painting the same nested child multiple times.
             boolean applicable = comp.isVisible()
-              && (comp.isLightweight() || !lightweightOnly);
+              && (comp.isLightweight()
+                  || !lightweightOnly && ! (comp instanceof Container));
 
             if (applicable)
               visitChild(gfx, visitor, comp);
Index: jni/gtk-peer/gnu_java_awt_peer_gtk_GtkComponentPeer.c
===================================================================
RCS file: /cvs/gcc/gcc/libjava/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkComponentPeer.c,v
retrieving revision 1.12
diff -u -r1.12 gnu_java_awt_peer_gtk_GtkComponentPeer.c
--- jni/gtk-peer/gnu_java_awt_peer_gtk_GtkComponentPeer.c	5 Jan 2004 21:13:46 -0000	1.12
+++ jni/gtk-peer/gnu_java_awt_peer_gtk_GtkComponentPeer.c	23 Jan 2004 21:01:22 -0000
@@ -575,6 +575,80 @@
   (*env)->ReleaseStringUTFChars (env, jname, name);
 }
 
+gboolean
+filter_expose_event_handler (GtkWidget *widget, GdkEvent *event, jobject peer)
+{
+  // Prevent the default event handler from getting this signal if applicable
+  // FIXME: I came up with these filters by looking for patterns in the unwanted
+  //        expose events that are fed back to us from gtk/X. Perhaps there is
+  //        a way to prevent them from occuring in the first place.
+  if (event->type == GDK_EXPOSE && (!GTK_IS_LAYOUT(widget)
+                                    || event->any.window != widget->window))
+    {
+      g_signal_stop_emission_by_name(GTK_OBJECT(widget), "event");
+      return FALSE;
+    }
+  else
+    {
+      // There may be non-expose events that are triggered while we're
+      // painting a heavyweight peer.
+      return pre_event_handler(widget, event, peer);
+    }
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkComponentPeer_addExposeFilter
+  (JNIEnv *env, jobject obj)
+{
+  void *ptr = NSA_GET_PTR (env, obj);
+  jobject *gref = NSA_GET_GLOBAL_REF (env, obj);
+  g_assert (gref);
+
+  gdk_threads_enter ();
+
+  g_signal_handlers_block_by_func (GTK_OBJECT(ptr), *pre_event_handler, *gref);
+  g_signal_connect( GTK_OBJECT(ptr), "event",
+                    G_CALLBACK(filter_expose_event_handler), *gref);
+
+  gdk_threads_leave ();
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkComponentPeer_removeExposeFilter
+  (JNIEnv *env, jobject obj)
+{
+  void *ptr = NSA_GET_PTR (env, obj);
+  jobject *gref = NSA_GET_GLOBAL_REF (env, obj);
+  g_assert (gref);
+
+  gdk_threads_enter ();
+
+  g_signal_handlers_disconnect_by_func (GTK_OBJECT(ptr),
+                                        *filter_expose_event_handler, *gref);
+  g_signal_handlers_unblock_by_func (GTK_OBJECT(ptr), *pre_event_handler, *gref);
+
+  gdk_threads_leave ();
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkComponentPeer_gtkWidgetQueueDrawArea
+  (JNIEnv *env, jobject obj, jint x, jint y, jint width, jint height)
+{
+  GdkRectangle rect;
+  void *ptr;
+
+  ptr = NSA_GET_PTR (env, obj);
+
+  rect.x = x + GTK_WIDGET(ptr)->allocation.x;
+  rect.y = y + GTK_WIDGET(ptr)->allocation.y;
+  rect.width = width;
+  rect.height = height;
+
+  gdk_threads_enter ();
+
+  gdk_window_invalidate_rect (GTK_WIDGET (ptr)->window, &rect, 0);
+  gdk_window_process_all_updates();
+
+  gdk_threads_leave ();
+}
+
 JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkComponentPeer_connectJObject
   (JNIEnv *env, jobject obj)
 {
Index: jni/gtk-peer/gnu_java_awt_peer_gtk_GtkEvents.c
===================================================================
RCS file: /cvs/gcc/gcc/libjava/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkEvents.c,v
retrieving revision 1.18
diff -u -r1.18 gnu_java_awt_peer_gtk_GtkEvents.c
--- jni/gtk-peer/gnu_java_awt_peer_gtk_GtkEvents.c	16 Jan 2004 22:30:11 -0000	1.18
+++ jni/gtk-peer/gnu_java_awt_peer_gtk_GtkEvents.c	23 Jan 2004 21:01:22 -0000
@@ -1012,12 +1012,21 @@
       break;
     case GDK_EXPOSE:
       {
-	(*gdk_env)->CallVoidMethod (gdk_env, peer,
-				    postExposeEventID,
-				    (jint)event->expose.area.x,
-				    (jint)event->expose.area.y,
-				    (jint)event->expose.area.width,
-				    (jint)event->expose.area.height);
+        // This filters out unwanted feedback expose events from gtk/X
+        // when we explictly invalidate and update heavyweight components,
+        // thus avoiding an infinite loop.
+        // FIXME: I'm not quite sure why we're getting these expose events. 
+        //        Maybe there is a way to avoid them?
+        if((event->any.window == widget->window && event->any.send_event)
+           || GTK_IS_LAYOUT(widget))
+          {
+	    (*gdk_env)->CallVoidMethod (gdk_env, peer,
+				        postExposeEventID,
+				        (jint)event->expose.area.x,
+				        (jint)event->expose.area.y,
+				        (jint)event->expose.area.width,
+				        (jint)event->expose.area.height);
+          }
       }
       break;
     case GDK_FOCUS_CHANGE:

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]