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]

[gui][patch] change to single-threaded event loop


hi,

this patch, which I just committed to the java-gui-branch, reduces the number of threads needed to support the AWT event loop from 2 to 1. it does this by having the dispatch thread move back and forth between iterating the native event loop (on the native side of JNI) and iterating the AWT event loop, up in java. events are transferred in batches, so that the JNI traffic isn't too heavy.

in addition, I added support for the precise shutdown condition of AWT applications. when all windows are destroyed, and both the native and java event queues are empty, the (single) event dispatch thread exits, allowing the program to terminate along with its main thread.

I tried to hack support for this scheme into the X toolkit as well, but I'm not certain how successful I was -- it runs, but behaves oddly -- since much of the X toolkit seems broken at the moment.

-graydon


2005-01-04 Graydon Hoare <graydon@redhat.com>


	* Makefile.am
	(jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMainThread.c)
	(gnu/java/awt/peer/gtk/GtkMainThread.java) : Remove.
	* Makefile.in: Regenerate.
	* gnu/awt/xlib/XEventLoop.java: Fix to match thread model.
	* gnu/awt/xlib/XFramePeer.java: Likewise.
	* gnu/awt/xlib/XToolkit.java: Likewise.
	* gnu/gcj/xlib/XAnyEvent.java: Likewise.
	* gnu/gcj/xlib/natXAnyEvent.cc: Likewise.
	* gnu/java/awt/ClasspathToolkit.java
	(nativeQueueEmpty)
	(wakeNativeQueue)
	(iterateNativeQueue): New methods.
	* gnu/java/awt/peer/gtk/GtkMainThread.java: Remove.
	* gnu/java/awt/peer/gtk/GtkToolkit.java
	(gtkInit): Absorb from defunct GtkMainThread class.
	(static): Run gtkInit in static startup block.
	(GtkToolkit): Remove construction of GtkMainThread and queue.
	(getSystemEventQueueImpl): Construct queue when requested.
	(nativeQueueEmpty)
	(wakeNativeQueue)
	(iterateNativeQueue): New methods.
	* java/awt/Component.java (removeNotify): Remove race.
	* java/awt/EventDispatchThread.java
	(EventDispatchThread): Don't start on construction.
	(run): Remove isInterrupted check.
	* java/awt/EventQueue.java (shutdown): New flag.
	(isShutdown): New method checking J2SE shutdown condition.
	(setShutdown): New method.
	(getNextEvent): Restructure to use ClasspathToolkit.
	(postEvent): Activate new thread on posting, wake thread on
	post of possible shutdown condition event.
	* java/awt/Frame.java
	(Frame): Call noteFrame in all constructors.
	(fireDummyEvent): New helper method.
	(addNotify): Fire a dummy event to wake up queue.
	(removeNotify): Fire a dummy event to wake up queue.
	(noteFrame): New method.
	(weakFrames): New static field.
	(getFrames): Implement.
	* jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMainThread.c:
	Remove.
	* jni/gtk-peer/gnu_java_awt_peer_gtk_GtkToolkit.c:
	Move everything from GtkMainThread into this file
	(Java_gnu_java_awt_peer_gtk_GtkToolkit_iterateNativeQueue)
	(Java_gnu_java_awt_peer_gtk_GtkToolkit_wakeNativeQueue)
	(Java_gnu_java_awt_peer_gtk_GtkToolkit_nativeQueueEmpty):
	New functions to implement single-threaded queue semantics.
--- Makefile.am	31 Dec 2004 18:36:02 -0000	1.361.2.68
+++ Makefile.am	4 Jan 2005 18:40:03 -0000
@@ -211,7 +211,6 @@
 jni/gtk-peer/gnu_java_awt_peer_gtk_GtkImagePainter.c \
 jni/gtk-peer/gnu_java_awt_peer_gtk_GtkLabelPeer.c \
 jni/gtk-peer/gnu_java_awt_peer_gtk_GtkListPeer.c \
-jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMainThread.c \
 jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMenuBarPeer.c \
 jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMenuComponentPeer.c \
 jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMenuItemPeer.c \
@@ -262,7 +261,6 @@
 gnu/java/awt/peer/gtk/GtkImagePainter.java \
 gnu/java/awt/peer/gtk/GtkLabelPeer.java	\
 gnu/java/awt/peer/gtk/GtkListPeer.java \
-gnu/java/awt/peer/gtk/GtkMainThread.java \
 gnu/java/awt/peer/gtk/GtkMenuBarPeer.java \
 gnu/java/awt/peer/gtk/GtkMenuComponentPeer.java	\
 gnu/java/awt/peer/gtk/GtkMenuItemPeer.java \
@@ -308,7 +306,6 @@
 jniinclude/gnu_java_awt_peer_gtk_GtkImagePainter.h: gnu/java/awt/peer/gtk/GtkImagePainter.java
 jniinclude/gnu_java_awt_peer_gtk_GtkLabelPeer.h: gnu/java/awt/peer/gtk/GtkLabelPeer.java
 jniinclude/gnu_java_awt_peer_gtk_GtkListPeer.h: gnu/java/awt/peer/gtk/GtkListPeer.java
-jniinclude/gnu_java_awt_peer_gtk_GtkMainThread.h: gnu/java/awt/peer/gtk/GtkMainThread.java
 jniinclude/gnu_java_awt_peer_gtk_GtkMenuBarPeer.h: gnu/java/awt/peer/gtk/GtkMenuBarPeer.java
 jniinclude/gnu_java_awt_peer_gtk_GtkMenuComponentPeer.h: gnu/java/awt/peer/gtk/GtkMenuComponentPeer.java
 jniinclude/gnu_java_awt_peer_gtk_GtkMenuItemPeer.h: gnu/java/awt/peer/gtk/GtkMenuItemPeer.java
@@ -580,7 +577,6 @@
 jni/gtk-peer/gnu_java_awt_peer_gtk_GtkImagePainter.lo: $(top_builddir)/jniinclude/gnu_java_awt_peer_gtk_GtkImagePainter.h
 jni/gtk-peer/gnu_java_awt_peer_gtk_GtkLabelPeer.lo: $(top_builddir)/jniinclude/gnu_java_awt_peer_gtk_GtkLabelPeer.h
 jni/gtk-peer/gnu_java_awt_peer_gtk_GtkListPeer.lo: $(top_builddir)/jniinclude/gnu_java_awt_peer_gtk_GtkListPeer.h
-jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMainThread.lo: $(top_builddir)/jniinclude/gnu_java_awt_peer_gtk_GtkMainThread.h
 jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMenuBarPeer.lo: $(top_builddir)/jniinclude/gnu_java_awt_peer_gtk_GtkMenuBarPeer.h
 jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMenuComponentPeer.lo: $(top_builddir)/jniinclude/gnu_java_awt_peer_gtk_GtkMenuComponentPeer.h
 jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMenuItemPeer.lo: $(top_builddir)/jniinclude/gnu_java_awt_peer_gtk_GtkMenuItemPeer.h
--- Makefile.in	31 Dec 2004 18:36:02 -0000	1.385.2.68
+++ Makefile.in	4 Jan 2005 18:40:03 -0000
@@ -302,7 +302,6 @@
 jni/gtk-peer/gnu_java_awt_peer_gtk_GtkImagePainter.c \
 jni/gtk-peer/gnu_java_awt_peer_gtk_GtkLabelPeer.c \
 jni/gtk-peer/gnu_java_awt_peer_gtk_GtkListPeer.c \
-jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMainThread.c \
 jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMenuBarPeer.c \
 jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMenuComponentPeer.c \
 jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMenuItemPeer.c \
@@ -353,7 +352,6 @@
 gnu/java/awt/peer/gtk/GtkImagePainter.java \
 gnu/java/awt/peer/gtk/GtkLabelPeer.java	\
 gnu/java/awt/peer/gtk/GtkListPeer.java \
-gnu/java/awt/peer/gtk/GtkMainThread.java \
 gnu/java/awt/peer/gtk/GtkMenuBarPeer.java \
 gnu/java/awt/peer/gtk/GtkMenuComponentPeer.java	\
 gnu/java/awt/peer/gtk/GtkMenuItemPeer.java \
@@ -3717,7 +3715,6 @@
 @GTK_CAIRO_FALSE@gnu/java/awt/peer/gtk/GtkImagePainter.lo \
 @GTK_CAIRO_FALSE@gnu/java/awt/peer/gtk/GtkLabelPeer.lo \
 @GTK_CAIRO_FALSE@gnu/java/awt/peer/gtk/GtkListPeer.lo \
-@GTK_CAIRO_FALSE@gnu/java/awt/peer/gtk/GtkMainThread.lo \
 @GTK_CAIRO_FALSE@gnu/java/awt/peer/gtk/GtkMenuBarPeer.lo \
 @GTK_CAIRO_FALSE@gnu/java/awt/peer/gtk/GtkMenuComponentPeer.lo \
 @GTK_CAIRO_FALSE@gnu/java/awt/peer/gtk/GtkMenuItemPeer.lo \
@@ -3758,7 +3755,6 @@
 @GTK_CAIRO_FALSE@jni/gtk-peer/gnu_java_awt_peer_gtk_GtkImagePainter.lo \
 @GTK_CAIRO_FALSE@jni/gtk-peer/gnu_java_awt_peer_gtk_GtkLabelPeer.lo \
 @GTK_CAIRO_FALSE@jni/gtk-peer/gnu_java_awt_peer_gtk_GtkListPeer.lo \
-@GTK_CAIRO_FALSE@jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMainThread.lo \
 @GTK_CAIRO_FALSE@jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMenuBarPeer.lo \
 @GTK_CAIRO_FALSE@jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMenuComponentPeer.lo \
 @GTK_CAIRO_FALSE@jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMenuItemPeer.lo \
@@ -3805,7 +3801,6 @@
 @GTK_CAIRO_TRUE@gnu/java/awt/peer/gtk/GtkImagePainter.lo \
 @GTK_CAIRO_TRUE@gnu/java/awt/peer/gtk/GtkLabelPeer.lo \
 @GTK_CAIRO_TRUE@gnu/java/awt/peer/gtk/GtkListPeer.lo \
-@GTK_CAIRO_TRUE@gnu/java/awt/peer/gtk/GtkMainThread.lo \
 @GTK_CAIRO_TRUE@gnu/java/awt/peer/gtk/GtkMenuBarPeer.lo \
 @GTK_CAIRO_TRUE@gnu/java/awt/peer/gtk/GtkMenuComponentPeer.lo \
 @GTK_CAIRO_TRUE@gnu/java/awt/peer/gtk/GtkMenuItemPeer.lo \
@@ -3847,7 +3842,6 @@
 @GTK_CAIRO_TRUE@jni/gtk-peer/gnu_java_awt_peer_gtk_GtkImagePainter.lo \
 @GTK_CAIRO_TRUE@jni/gtk-peer/gnu_java_awt_peer_gtk_GtkLabelPeer.lo \
 @GTK_CAIRO_TRUE@jni/gtk-peer/gnu_java_awt_peer_gtk_GtkListPeer.lo \
-@GTK_CAIRO_TRUE@jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMainThread.lo \
 @GTK_CAIRO_TRUE@jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMenuBarPeer.lo \
 @GTK_CAIRO_TRUE@jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMenuComponentPeer.lo \
 @GTK_CAIRO_TRUE@jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMenuItemPeer.lo \
@@ -3908,7 +3902,7 @@
 
 DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
 
-TAR = gtar
+TAR = tar
 GZIP_ENV = --best
 DIST_SUBDIRS =  @DIRLTDL@ testsuite gcj include @DIRLTDL@ gcj include
 DEP_FILES =  .deps/$(srcdir)/$(CONVERT_DIR)/gen-from-JIS.P \
@@ -4049,7 +4043,6 @@
 .deps/gnu/java/awt/peer/gtk/GtkImagePainter.P \
 .deps/gnu/java/awt/peer/gtk/GtkLabelPeer.P \
 .deps/gnu/java/awt/peer/gtk/GtkListPeer.P \
-.deps/gnu/java/awt/peer/gtk/GtkMainThread.P \
 .deps/gnu/java/awt/peer/gtk/GtkMenuBarPeer.P \
 .deps/gnu/java/awt/peer/gtk/GtkMenuComponentPeer.P \
 .deps/gnu/java/awt/peer/gtk/GtkMenuItemPeer.P \
@@ -5746,7 +5739,6 @@
 .deps/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkImagePainter.P \
 .deps/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkLabelPeer.P \
 .deps/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkListPeer.P \
-.deps/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMainThread.P \
 .deps/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMenuBarPeer.P \
 .deps/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMenuComponentPeer.P \
 .deps/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMenuItemPeer.P \
@@ -6401,7 +6393,6 @@
 jniinclude/gnu_java_awt_peer_gtk_GtkImagePainter.h: gnu/java/awt/peer/gtk/GtkImagePainter.java
 jniinclude/gnu_java_awt_peer_gtk_GtkLabelPeer.h: gnu/java/awt/peer/gtk/GtkLabelPeer.java
 jniinclude/gnu_java_awt_peer_gtk_GtkListPeer.h: gnu/java/awt/peer/gtk/GtkListPeer.java
-jniinclude/gnu_java_awt_peer_gtk_GtkMainThread.h: gnu/java/awt/peer/gtk/GtkMainThread.java
 jniinclude/gnu_java_awt_peer_gtk_GtkMenuBarPeer.h: gnu/java/awt/peer/gtk/GtkMenuBarPeer.java
 jniinclude/gnu_java_awt_peer_gtk_GtkMenuComponentPeer.h: gnu/java/awt/peer/gtk/GtkMenuComponentPeer.java
 jniinclude/gnu_java_awt_peer_gtk_GtkMenuItemPeer.h: gnu/java/awt/peer/gtk/GtkMenuItemPeer.java
@@ -6520,7 +6511,6 @@
 jni/gtk-peer/gnu_java_awt_peer_gtk_GtkImagePainter.lo: $(top_builddir)/jniinclude/gnu_java_awt_peer_gtk_GtkImagePainter.h
 jni/gtk-peer/gnu_java_awt_peer_gtk_GtkLabelPeer.lo: $(top_builddir)/jniinclude/gnu_java_awt_peer_gtk_GtkLabelPeer.h
 jni/gtk-peer/gnu_java_awt_peer_gtk_GtkListPeer.lo: $(top_builddir)/jniinclude/gnu_java_awt_peer_gtk_GtkListPeer.h
-jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMainThread.lo: $(top_builddir)/jniinclude/gnu_java_awt_peer_gtk_GtkMainThread.h
 jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMenuBarPeer.lo: $(top_builddir)/jniinclude/gnu_java_awt_peer_gtk_GtkMenuBarPeer.h
 jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMenuComponentPeer.lo: $(top_builddir)/jniinclude/gnu_java_awt_peer_gtk_GtkMenuComponentPeer.h
 jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMenuItemPeer.lo: $(top_builddir)/jniinclude/gnu_java_awt_peer_gtk_GtkMenuItemPeer.h
--- gnu/awt/xlib/XEventLoop.java	30 Jul 2004 19:48:31 -0000	1.3.14.1
+++ gnu/awt/xlib/XEventLoop.java	4 Jan 2005 18:40:04 -0000
@@ -21,12 +21,11 @@
 import java.awt.event.MouseEvent;
 import java.util.Vector;
 
-public class XEventLoop implements Runnable
+public class XEventLoop
 {
   Display display;
   EventQueue queue;
   XAnyEvent anyEvent;
-  Thread eventLoopThread;
 
   LightweightRedirector lightweightRedirector = new LightweightRedirector();
     
@@ -36,20 +35,18 @@
     this.queue = queue;
     
     anyEvent = new XAnyEvent(display);
-    eventLoopThread = new Thread(this, "AWT thread for XEventLoop");
-    eventLoopThread.start();
   }
 
-  public void run()
+  void interrupt()
   {
-    while (true) 
-      postNextEvent();
+    anyEvent.interrupt();
   }
 
   void postNextEvent()
   {
     AWTEvent evt = getNextEvent();
-    queue.postEvent(evt);
+    if (evt != null)
+      queue.postEvent(evt);
   }
     
   /** get next event. Will block until events become available. */
@@ -61,19 +58,17 @@
       throw new Error("should not be idle");
     
     AWTEvent event = null;
-    while (event == null)
+    if (loadNextEvent())
       {
-	loadNextEvent();
-	event = createEvent();
-      }
-
-    event = lightweightRedirector.redirect(event);
-
+        event = createEvent();        
+        event = lightweightRedirector.redirect(event);
+      }    
     return event;
   }
 
-  void loadNextEvent()
+  boolean loadNextEvent()
   {
+    boolean gotEvent = false;
     try
       {
 	setIdle(true);
@@ -100,7 +95,7 @@
 	   of events. */
 	
 	//display.flush(); // implicit?
-	anyEvent.loadNext();
+	gotEvent = anyEvent.loadNext();
       }
     catch (RuntimeException re)
       {
@@ -110,6 +105,7 @@
       {
 	setIdle(false);
       }
+    return gotEvent;
   }
     
   /**
--- gnu/awt/xlib/XFramePeer.java	1 Mar 2003 22:14:20 -0000	1.3
+++ gnu/awt/xlib/XFramePeer.java	4 Jan 2005 18:40:04 -0000
@@ -78,7 +78,7 @@
      bounds.  */
   public void setBounds(int x, int y, int width, int height)
   {
-    if (Thread.currentThread() == getXToolkit().eventLoop.eventLoopThread)
+    if (EventQueue.isDispatchThread())
       return;
     
     super.setBounds(x, y, width, height);
--- gnu/awt/xlib/XToolkit.java	31 Dec 2004 18:36:04 -0000	1.7.2.2
+++ gnu/awt/xlib/XToolkit.java	4 Jan 2005 18:40:04 -0000
@@ -443,4 +443,24 @@
   {
     throw new java.lang.UnsupportedOperationException ();
   }
+
+  boolean interrupted;
+
+  public boolean nativeQueueEmpty() 
+  { 
+    return eventLoop.isIdle(); 
+  }
+
+  public void wakeNativeQueue() 
+  {
+    interrupted = true;
+    eventLoop.interrupt();
+  }
+
+  public void iterateNativeQueue(java.awt.EventQueue locked) 
+  {
+    interrupted = false;
+    while (!interrupted)
+      eventLoop.postNextEvent();
+  }; 
 }
--- gnu/gcj/xlib/XAnyEvent.java	8 Nov 2003 01:23:47 -0000	1.3
+++ gnu/gcj/xlib/XAnyEvent.java	4 Jan 2005 18:40:04 -0000
@@ -70,7 +70,8 @@
   /**
    * Load next event into the event structure.
    */
-  public native void loadNext();
+  public native boolean loadNext();
+  public native void interrupt();
 
   public native int getType();
   public native void setType(int type);
@@ -86,6 +87,7 @@
   public native void send(Window destination, boolean propagate,
 			  long mask);
 
+  RawData pipefds;
   RawData structure;
   Display display;
 
--- gnu/gcj/xlib/natXAnyEvent.cc	22 Oct 2000 17:46:09 -0000	1.1
+++ gnu/gcj/xlib/natXAnyEvent.cc	4 Jan 2005 18:40:04 -0000
@@ -6,6 +6,12 @@
 Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
 details.  */
 
+#include <config.h>
+#include <platform.h>
+
+#include <gcj/javaprims.h>
+#include <jvm.h>
+
 #include <X11/Xlib.h>
 
 #include <gcj/cni.h>
@@ -22,24 +28,69 @@
 #include <gnu/gcj/xlib/XExposeEvent.h>
 #include <gnu/gcj/xlib/XException.h>
 
+#include <unistd.h>
+#include <posix.h>
+
 void gnu::gcj::xlib::XAnyEvent::init()
 {
   ::XEvent* event = new ::XEvent;
+  int *pipes = new int[2];
+  pipe(pipes);
   structure = reinterpret_cast<gnu::gcj::RawData*>(event);
+  pipefds = reinterpret_cast<gnu::gcj::RawData*>(pipes);
 }
 
 void gnu::gcj::xlib::XAnyEvent::finalize()
 {
   delete structure;
+  int *pipe = reinterpret_cast<int *>(pipefds);
+  close(pipe[0]);
+  close(pipe[1]);
+  delete [] pipefds;
+  pipefds = 0;
   structure = 0;
 }
 
-void gnu::gcj::xlib::XAnyEvent::loadNext()
+jboolean gnu::gcj::xlib::XAnyEvent::loadNext()
 {
   ::Display* dpy = (::Display*) display->display;
   ::XEvent* evt = (::XEvent*) structure;
-  XNextEvent(dpy, evt);
-  // What does XNextEvent return?
+
+  if (XPending(dpy))
+    {
+      XNextEvent(dpy, evt);
+      return true;  
+    }
+
+  int *pipe = reinterpret_cast<int *>(pipefds);
+  int xfd = XConnectionNumber(dpy);
+  int pipefd = pipe[0];
+  int n = (xfd > pipefd ? xfd : pipefd) + 1;
+  fd_set rfds;
+  FD_ZERO(&rfds);
+  FD_SET(xfd, &rfds);
+  FD_SET(pipefd, &rfds);  
+  int sel = _Jv_select (n, &rfds, NULL, NULL, NULL);
+  if (sel > 0)
+    {
+      if (FD_ISSET(xfd, &rfds))
+	{
+	  XNextEvent(dpy, evt);
+	  return true;  
+	}
+      if (FD_ISSET(pipefd, &rfds))
+	{
+	  char c;
+	  read(pipefd, &c, 1);
+	}
+    }
+  return false;
+}
+
+void gnu::gcj::xlib::XAnyEvent::interrupt()
+{
+  int *pipe = reinterpret_cast<int *>(pipefds);
+  write(pipe[1], "W", 1);
 }
 
 jint gnu::gcj::xlib::XAnyEvent::getType()
--- gnu/java/awt/ClasspathToolkit.java	1 Jan 2005 19:09:36 -0000	1.1.18.5
+++ gnu/java/awt/ClasspathToolkit.java	4 Jan 2005 18:40:04 -0000
@@ -45,6 +45,7 @@
 import java.awt.Image;
 import java.awt.Dimension;
 import java.awt.DisplayMode;
+import java.awt.EventQueue;
 import java.awt.Font;
 import java.awt.FontMetrics;
 import java.awt.GraphicsEnvironment;
@@ -354,4 +355,8 @@
 
   public abstract RobotPeer createRobot (GraphicsDevice screen)
     throws AWTException;
+
+  public abstract boolean nativeQueueEmpty();
+  public abstract void wakeNativeQueue();  
+  public abstract void iterateNativeQueue(EventQueue locked);
 }
--- gnu/java/awt/peer/gtk/GtkMainThread.java	31 Jul 2004 13:28:11 -0000	1.1.32.1
+++ gnu/java/awt/peer/gtk/GtkMainThread.java	4 Jan 2005 18:40:04 -0000
@@ -55,7 +55,21 @@
    * set to "false".  -1 if unset.
    */
   static native void gtkInit(int portableNativeSync);
-  native void gtkMain();
+
+  public native void wakeup();
+  native void iterate();
+
+  boolean running = true;
+  public synchronized boolean stillRunning()
+  {
+    return running;
+  }
+
+  public synchronized void quit() 
+  { 
+    running = false;
+    wakeup();
+  }
   
   public GtkMainThread() 
   {
@@ -63,7 +77,8 @@
     synchronized (mainThreadLock) 
       {
 	if (mainThread != null)
-	  throw new IllegalStateException();
+          return;
+        //throw new IllegalStateException();
 	mainThread = new Thread(this, "GtkMain");
       }
     
@@ -103,7 +118,8 @@
 	gtkInitCalled = true;
 	notifyAll();
       }
-    gtkMain();
+    while(stillRunning())
+      iterate();
   }
 }
 
--- gnu/java/awt/peer/gtk/GtkToolkit.java	31 Dec 2004 18:36:05 -0000	1.8.2.15
+++ gnu/java/awt/peer/gtk/GtkToolkit.java	4 Jan 2005 18:40:04 -0000
@@ -70,6 +70,7 @@
 import java.util.Map;
 import java.util.MissingResourceException;
 import java.util.Properties;
+import javax.imageio.spi.IIORegistry;
 
 /* This class uses a deprecated method java.awt.peer.ComponentPeer.getPeer().
    This merits comment.  We are basically calling Sun's bluff on this one.
@@ -88,9 +89,8 @@
 public class GtkToolkit extends gnu.java.awt.ClasspathToolkit
   implements EmbeddedWindowSupport
 {
-  GtkMainThread main;
   Hashtable containers = new Hashtable();
-  static EventQueue q = new EventQueue();
+  static EventQueue q;
   static Clipboard systemClipboard;
   static boolean useGraphics2dSet;
   static boolean useGraphics2d;
@@ -105,19 +105,32 @@
     return useGraphics2d;
   }
 
+  static native void gtkInit(int portableNativeSync);
+
   static
   {
     if (Configuration.INIT_LOAD_LIBRARY)
       System.loadLibrary("gtkpeer");
+
+    int portableNativeSync;     
+    String portNatSyncProp = 
+      System.getProperty("gnu.classpath.awt.gtk.portable.native.sync");
+    
+    if (portNatSyncProp == null)
+      portableNativeSync = -1;  // unset
+    else if (Boolean.valueOf(portNatSyncProp).booleanValue())
+      portableNativeSync = 1;   // true
+    else
+      portableNativeSync = 0;   // false
+
+    gtkInit(portableNativeSync);
   }
 
   public GtkToolkit ()
   {
-    main = new GtkMainThread ();
     systemClipboard = new GtkClipboard ();
-    GtkGenericPeer.enableQueue (q);
   }
-  
+
   native public void beep ();
   native private void getScreenSizeDimensions (int[] xy);
   
@@ -596,6 +609,14 @@
 
   protected EventQueue getSystemEventQueueImpl() 
   {
+    synchronized (GtkToolkit.class)
+      {
+        if (q == null)
+          {
+            q = new EventQueue();
+            GtkGenericPeer.enableQueue (q);
+          }
+      }    
     return q;
   }
 
@@ -627,4 +648,9 @@
   {
     return new GdkRobotPeer (screen);
   }
-}
+
+  public native boolean nativeQueueEmpty();
+  public native void wakeNativeQueue();  
+  public native void iterateNativeQueue(EventQueue locked);
+
+} // class GtkToolkit
--- java/awt/Component.java	9 Dec 2004 21:36:46 -0000	1.37.2.30
+++ java/awt/Component.java	4 Jan 2005 18:40:04 -0000
@@ -3408,9 +3408,15 @@
    */
   public void removeNotify()
   {
-    if (peer != null)
-      peer.dispose();
+    // We null our peer field before disposing of it, such that if we're
+    // not the event dispatch thread and the dispatch thread is awoken by
+    // the dispose call, there will be no race checking the peer's null
+    // status.
+
+    ComponentPeer tmp = peer;
     peer = null;
+    if (tmp != null)
+      tmp.dispose();
   }
 
   /**
--- java/awt/EventDispatchThread.java	26 Dec 2004 09:53:45 -0000	1.5.6.6
+++ java/awt/EventDispatchThread.java	4 Jan 2005 18:40:04 -0000
@@ -53,7 +53,6 @@
     setName("AWT-EventQueue-" + ++dispatchThreadNum);
     this.queue = queue;
     setPriority(NORM_PRIORITY + 1);
-    start();
   }
 
   public void run()
@@ -63,11 +62,6 @@
         try
 	{
 	  AWTEvent evt = queue.getNextEvent();
-	  if (isInterrupted ())
-	    {
-	      // We are interrupted when we should finish executing
-	      return;
-	    }
 
           KeyboardFocusManager manager;
           manager = KeyboardFocusManager.getCurrentKeyboardFocusManager ();
--- java/awt/EventQueue.java	16 Nov 2004 09:55:31 -0000	1.17.4.2
+++ java/awt/EventQueue.java	4 Jan 2005 18:40:04 -0000
@@ -40,9 +40,12 @@
 import java.awt.event.ActionEvent;
 import java.awt.event.InputEvent;
 import java.awt.event.InvocationEvent;
+import java.awt.event.WindowEvent;
 import java.lang.reflect.InvocationTargetException;
 import java.util.EmptyStackException;
 
+import gnu.java.awt.ClasspathToolkit;
+
 /* Written using on-line Java 2 Platform Standard Edition v1.3 API 
  * Specification, as well as "The Java Class Libraries", 2nd edition 
  * (Addison-Wesley, 1998).
@@ -71,6 +74,32 @@
   private long lastWhen = System.currentTimeMillis();
 
   private EventDispatchThread dispatchThread = new EventDispatchThread(this);
+  private boolean shutdown = false;
+
+  synchronized void setShutdown (boolean b) 
+  {
+    shutdown = b;
+  }
+
+  synchronized boolean isShutdown ()
+  {
+    if (shutdown)
+      return true;
+
+    // This is the exact self-shutdown condition specified in J2SE:
+    // http://java.sun.com/j2se/1.4.2/docs/api/java/awt/doc-files/AWTThreadIssues.html
+    
+    if (peekEvent() == null
+        && ((ClasspathToolkit) Toolkit.getDefaultToolkit()).nativeQueueEmpty())
+      {
+        Frame[] frames = Frame.getFrames();
+        for (int i = 0; i < frames.length; ++i)
+          if (frames[i].isDisplayable())
+            return false;
+        return true;
+      }
+    return false;
+  }
 
   /**
    * Initializes a new instance of <code>EventQueue</code>.
@@ -95,7 +124,38 @@
       return next.getNextEvent();
 
     while (next_in == next_out)
-      wait();
+      {
+        // Only the EventDispatchThread associated with the top of the stack is
+        // allowed to get events from the native source; everyone else just
+        // waits on the head of the queue.
+
+        if (isDispatchThread())
+          {
+            // We are not allowed to return null from this method, yet it
+            // is possible that we actually have run out of native events
+            // in the enclosing while() loop, and none of the native events
+            // happened to cause AWT events. We therefore ought to check
+            // the isShutdown() condition here, before risking a "native
+            // wait". If we check it before entering this function we may
+            // wait forever for events after the shutdown condition has
+            // arisen.
+
+            if (isShutdown())
+              throw new InterruptedException();
+
+            ((ClasspathToolkit) Toolkit.getDefaultToolkit()).iterateNativeQueue(this);
+          }
+        else
+          {
+            try
+              {
+                wait();
+              }
+            catch (InterruptedException ie)
+              {
+              }
+          }
+      }
 
     AWTEvent res = queue[next_out];
 
@@ -215,6 +275,22 @@
         next_out = 0;
         next_in = oldQueue.length;
       }
+    
+    if (dispatchThread == null || !dispatchThread.isAlive())
+      {
+        dispatchThread = new EventDispatchThread(this);
+        dispatchThread.start();
+      }
+
+    // Window events might represent the closing of a window, which
+    // might cause the end of the dispatch thread's life, so we'll wake
+    // it up here to give it a chance to check for shutdown.
+
+    if (!isDispatchThread() 
+        || (evt.getID() == WindowEvent.WINDOW_CLOSED)
+        || (evt.getID() == WindowEvent.WINDOW_CLOSING))
+      ((ClasspathToolkit) Toolkit.getDefaultToolkit()).wakeNativeQueue();
+
     notify();
   }
 
@@ -386,9 +462,10 @@
 	    next_in = 0;
 	    next_out = 0;
 
-            // Tell our EventDispatchThread that it can end execution
-            dispatchThread.interrupt ();
+            ((ClasspathToolkit) Toolkit.getDefaultToolkit()).wakeNativeQueue();
+            setShutdown(true);
 	    dispatchThread = null;
+            this.notifyAll();
           }
       }
   }
--- java/awt/Frame.java	29 Dec 2004 17:22:53 -0000	1.20.2.6
+++ java/awt/Frame.java	4 Jan 2005 18:40:04 -0000
@@ -39,6 +39,9 @@
 package java.awt;
 
 import java.awt.peer.FramePeer;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.Vector;
 
 import javax.accessibility.AccessibleContext;
@@ -214,6 +217,7 @@
 Frame()
 {
   this("");
+  noteFrame(this);
 }
 
 /**
@@ -229,6 +233,7 @@
   this.title = title;
   // Top-level frames are initially invisible.
   visible = false;
+  noteFrame(this);
 }
 
 public
@@ -236,6 +241,7 @@
 {
   super(gc);
   visible = false;
+  noteFrame(this);
 }
 
 public
@@ -244,6 +250,7 @@
   super(gc);
   setTitle(title);
   visible = false;
+  noteFrame(this);
 }
 
 /**
@@ -396,6 +403,12 @@
 /**
   * Notifies this frame that it should create its native peer.
   */
+
+private static void fireDummyEvent()
+{
+  EventQueue.invokeLater(new Runnable() { public void run() { } });
+}
+
 public void
 addNotify()
 {
@@ -403,6 +416,12 @@
     menuBar.addNotify();
   if (peer == null)
     peer = getToolkit ().createFrame (this);
+
+  // We now know there's a Frame (us) with a live peer, so we can start the
+  // fundamental queue and dispatch thread, by inserting a dummy event.
+  if (parent != null && parent.isDisplayable())
+    fireDummyEvent();
+  
   super.addNotify();
 }
 
@@ -411,6 +430,12 @@
   if (menuBar != null)
     menuBar.removeNotify();
   super.removeNotify();
+
+  // By now we've been disconnected from the peer, and the peer set to
+  // null.  This is formally the same as saying "we just became
+  // un-displayable", so we wake up the event queue with a dummy event to
+  // see if it's time to shut down.
+  fireDummyEvent();
 }
 
   /**
@@ -449,13 +474,41 @@
     return super.paramString () + ",title=" + title + resizable + state;
   }
 
-public static Frame[]
-getFrames()
+private static ArrayList weakFrames = new ArrayList();
+
+private static void noteFrame(Frame f)
+{
+  weakFrames.add(new WeakReference(f));
+}
+
+public static Frame[] getFrames()
 {
-  //Frame[] array = new Frames[frames.size()];
-  //return frames.toArray(array);
-  String msg = "FIXME: can't be implemented without weak references";
-  throw new UnsupportedOperationException(msg);
+  int n = 0;
+  synchronized (weakFrames)
+    {
+      Iterator i = weakFrames.iterator();
+      while (i.hasNext())
+        {
+          WeakReference wr = (WeakReference) i.next();
+          if (wr.get() != null)
+            ++n;
+        }
+      if (n == 0)
+        return new Frame[0];
+      else
+        {
+          Frame[] frames = new Frame[n];
+          n = 0;
+          i = weakFrames.iterator();
+          while (i.hasNext())
+            {
+              WeakReference wr = (WeakReference) i.next();
+              if (wr.get() != null)
+                frames[n++] = (Frame) wr.get();
+            }
+          return frames;
+        }
+    }
 }
 
   public void setState (int state)
--- jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMainThread.c	6 Nov 2004 23:31:21 -0000	1.12.8.7
+++ jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMainThread.c	4 Jan 2005 18:40:04 -0000
@@ -251,17 +251,23 @@
 }
 
 
-
 /*
  * Run gtk_main and block.
  */ 
+
+JNIEXPORT void JNICALL 
+Java_gnu_java_awt_peer_gtk_GtkMainThread_iterate
+  (JNIEnv *env __attribute__((unused)), jobject obj __attribute__((unused)))
+{
+  gtk_main_iteration();
+}
+
 JNIEXPORT void JNICALL 
-Java_gnu_java_awt_peer_gtk_GtkMainThread_gtkMain
+Java_gnu_java_awt_peer_gtk_GtkMainThread_wakeUp
   (JNIEnv *env __attribute__((unused)), jobject obj __attribute__((unused)))
 {
-  gdk_threads_enter ();
-  gtk_main ();
-  gdk_threads_leave ();
+  /* Argument is a GtkContext*. NULL means "default context". */
+  g_main_context_wakeup (NULL);
 }
 
 /* This is a big hack, needed until this pango bug is resolved:
--- jni/gtk-peer/gnu_java_awt_peer_gtk_GtkToolkit.c	12 May 2004 23:40:05 -0000	1.2.16.1
+++ jni/gtk-peer/gnu_java_awt_peer_gtk_GtkToolkit.c	4 Jan 2005 18:40:04 -0000
@@ -38,9 +38,322 @@
 
 #include "gtkpeer.h"
 #include "gnu_java_awt_peer_gtk_GtkToolkit.h"
+#include "gthread-jni.h"
+
+#ifdef JVM_SUN
+  struct state_table *native_state_table;
+  struct state_table *native_global_ref_table;
+#endif
+
+jmethodID setBoundsCallbackID;
+
+jmethodID postActionEventID;
+jmethodID postMenuActionEventID;
+jmethodID postMouseEventID;
+jmethodID postConfigureEventID;
+jmethodID postExposeEventID;
+jmethodID postKeyEventID;
+jmethodID postFocusEventID;
+jmethodID postAdjustmentEventID;
+jmethodID postItemEventID;
+jmethodID choicePostItemEventID;
+jmethodID postListItemEventID;
+jmethodID postTextEventID;
+jmethodID postWindowEventID;
+
+jmethodID beginNativeRepaintID;
+jmethodID endNativeRepaintID;
+
+jmethodID initComponentGraphicsID;
+jmethodID initComponentGraphics2DID;
+jmethodID setCursorID;
+
+JNIEnv *gdk_env;
+
+GtkWindowGroup *global_gtk_window_group;
+
+static void init_glib_threads(JNIEnv *, jint);
+
+double dpi_conversion_factor;
+
+static void init_dpi_conversion_factor (void);
+static void dpi_changed_cb (GtkSettings  *settings,
+                            GParamSpec   *pspec);
+
+/*
+ * Call gtk_init.  It is very important that this happen before any other
+ * gtk calls.
+ *
+ * The portableNativeSync argument may have the values:
+ *   1 if the Java property gnu.classpath.awt.gtk.portable.native.sync
+ *     is set to "true".  
+ *   0 if it is set to "false"
+ *  -1 if unset.
+ */
+
+
+JNIEXPORT void JNICALL 
+Java_gnu_java_awt_peer_gtk_GtkToolkit_gtkInit (JNIEnv *env, 
+					       jclass clazz __attribute__((unused)),
+					       jint portableNativeSync)
+{
+  int argc = 1;
+  char **argv;
+  char *homedir, *rcpath = NULL;
+
+  jclass gtkgenericpeer, gtkcomponentpeer, gtkchoicepeer, gtkwindowpeer, gtkscrollbarpeer, gtklistpeer,
+    gtkmenuitempeer, gtktextcomponentpeer, window, gdkgraphics, gdkgraphics2d;
+
+  gtkgenericpeer = (*env)->FindClass(env, "gnu/java/awt/peer/gtk/GtkGenericPeer");
+
+  NSA_INIT (env, gtkgenericpeer);
+  gdk_env = env;
+
+  /* GTK requires a program's argc and argv variables, and requires that they
+     be valid.   Set it up. */
+  argv = (char **) g_malloc (sizeof (char *) * 2);
+  argv[0] = (char *) g_malloc(1);
+#if 1
+  strcpy(argv[0], "");
+#else  /* The following is a more efficient alternative, but less intuitively
+	* expresses what we are trying to do.   This code is only run once, so
+	* I'm going for intuitive. */
+  argv[0][0] = '\0';
+#endif
+  argv[1] = NULL;
+
+  init_glib_threads(env, portableNativeSync);
+
+  /* From GDK 2.0 onwards we have to explicitly call gdk_threads_init */
+  gdk_threads_init();
+
+  gtk_init (&argc, &argv);
+
+  gdk_rgb_init ();
+  gtk_widget_set_default_colormap (gdk_rgb_get_cmap ());
+  gtk_widget_set_default_visual (gdk_rgb_get_visual ());
+
+  /* Make sure queued calls don't get sent to GTK/GDK while 
+     we're shutting down. */
+  atexit (gdk_threads_enter);
+
+  gdk_event_handler_set ((GdkEventFunc)awt_event_handler, NULL, NULL);
+
+  if ((homedir = getenv ("HOME")))
+    {
+      rcpath = (char *) g_malloc (strlen (homedir) + strlen (RC_FILE) + 2);
+      sprintf (rcpath, "%s/%s", homedir, RC_FILE);
+    }
+  
+  gtk_rc_parse ((rcpath) ? rcpath : RC_FILE);
+
+  g_free (rcpath);
+  g_free (argv[0]);
+  g_free (argv);
+
+  /* setup cached IDs for posting GTK events to Java */
+
+  window = (*env)->FindClass (env, "java/awt/Window");
+
+  gtkcomponentpeer = (*env)->FindClass (env,
+				     "gnu/java/awt/peer/gtk/GtkComponentPeer");
+  gtkchoicepeer = (*env)->FindClass (env,
+				     "gnu/java/awt/peer/gtk/GtkChoicePeer");
+  gtkwindowpeer = (*env)->FindClass (env,
+				     "gnu/java/awt/peer/gtk/GtkWindowPeer");
+  gtkscrollbarpeer = (*env)->FindClass (env, 
+				     "gnu/java/awt/peer/gtk/GtkScrollbarPeer");
+  gtklistpeer = (*env)->FindClass (env, "gnu/java/awt/peer/gtk/GtkListPeer");
+  gtkmenuitempeer = (*env)->FindClass (env,
+                                     "gnu/java/awt/peer/gtk/GtkMenuItemPeer");
+  gtktextcomponentpeer = (*env)->FindClass (env,
+                                     "gnu/java/awt/peer/gtk/GtkTextComponentPeer");
+  gdkgraphics = (*env)->FindClass (env,
+                                   "gnu/java/awt/peer/gtk/GdkGraphics");
+  gdkgraphics2d = (*env)->FindClass (env,
+                                     "gnu/java/awt/peer/gtk/GdkGraphics2D");
+  setBoundsCallbackID = (*env)->GetMethodID (env, window,
+					     "setBoundsCallback",
+					     "(IIII)V");
+
+  postMenuActionEventID = (*env)->GetMethodID (env, gtkmenuitempeer,
+					       "postMenuActionEvent",
+					       "()V");
+  postMouseEventID = (*env)->GetMethodID (env, gtkcomponentpeer, 
+                                          "postMouseEvent", "(IJIIIIZ)V");
+  setCursorID = (*env)->GetMethodID (env, gtkcomponentpeer,
+                                     "setCursor", "()V");
+  beginNativeRepaintID = (*env)->GetMethodID (env, gtkcomponentpeer, 
+                                              "beginNativeRepaint", "()V");
+
+  endNativeRepaintID = (*env)->GetMethodID (env, gtkcomponentpeer, 
+                                            "endNativeRepaint", "()V");
+
+  postConfigureEventID = (*env)->GetMethodID (env, gtkwindowpeer, 
+					      "postConfigureEvent", "(IIII)V");
+  postWindowEventID = (*env)->GetMethodID (env, gtkwindowpeer,
+					   "postWindowEvent",
+					   "(ILjava/awt/Window;I)V");
+  postExposeEventID = (*env)->GetMethodID (env, gtkcomponentpeer, 
+					  "postExposeEvent", "(IIII)V");
+  postKeyEventID = (*env)->GetMethodID (env, gtkcomponentpeer,
+					"postKeyEvent", "(IJIICI)V");
+  postFocusEventID = (*env)->GetMethodID (env, gtkcomponentpeer,
+					  "postFocusEvent", "(IZ)V");
+  postAdjustmentEventID = (*env)->GetMethodID (env, gtkscrollbarpeer,
+					       "postAdjustmentEvent", 
+					       "(II)V");
+  postItemEventID = (*env)->GetMethodID (env, gtkcomponentpeer,
+					 "postItemEvent", 
+					 "(Ljava/lang/Object;I)V");
+  choicePostItemEventID = (*env)->GetMethodID (env, gtkchoicepeer,
+					 "choicePostItemEvent", 
+					 "(Ljava/lang/String;I)V");
+  postListItemEventID = (*env)->GetMethodID (env, gtklistpeer,
+					     "postItemEvent",
+					     "(II)V");
+  postTextEventID = (*env)->GetMethodID (env, gtktextcomponentpeer,
+					     "postTextEvent",
+					     "()V");
+  initComponentGraphicsID = (*env)->GetMethodID (env, gdkgraphics,
+                                                 "initComponentGraphics",
+                                                 "()V");
+  initComponentGraphics2DID = (*env)->GetMethodID (env, gdkgraphics2d,
+                                                   "initComponentGraphics2D",
+                                                   "()V");
+  global_gtk_window_group = gtk_window_group_new ();
+
+  init_dpi_conversion_factor ();
+}
+
+
+/** Initialize GLIB's threads properly, based on the value of the
+    gnu.classpath.awt.gtk.portable.native.sync Java system property.  If
+    that's unset, use the PORTABLE_NATIVE_SYNC config.h macro.  (TODO: 
+    In some release following 0.10, that config.h macro will go away.)
+    */ 
+static void 
+init_glib_threads(JNIEnv *env, jint portableNativeSync)
+{
+  if (portableNativeSync < 0)
+    {
+#ifdef PORTABLE_NATIVE_SYNC /* Default value, if not set by the Java system
+                               property */ 
+      portableNativeSync = 1;
+#else
+      portableNativeSync = 0;
+#endif
+    }
+  
+  (*env)->GetJavaVM( env, &the_vm );
+  if (portableNativeSync)
+    g_thread_init ( &portable_native_sync_jni_functions );
+  else
+    g_thread_init ( NULL );
+
+  /* Debugging progress message; uncomment if needed: */
+  /*   printf("called gthread init\n"); */
+}
+
+
+/* This is a big hack, needed until this pango bug is resolved:
+   http://bugzilla.gnome.org/show_bug.cgi?id=119081.
+   See: http://mail.gnome.org/archives/gtk-i18n-list/2003-August/msg00001.html
+   for details. */
+static void
+init_dpi_conversion_factor ()
+{
+  GtkSettings *settings = gtk_settings_get_default ();
+  GObjectClass *klass;
+
+  klass = G_OBJECT_CLASS (GTK_SETTINGS_GET_CLASS (settings));
+  if (g_object_class_find_property (klass, "gtk-xft-dpi"))
+    {
+      int int_dpi;
+      g_object_get (settings, "gtk-xft-dpi", &int_dpi, NULL);
+      /* If int_dpi == -1 gtk-xft-dpi returns the default value. So we
+	 have to do approximate calculation here.  */
+      if (int_dpi < 0)
+	dpi_conversion_factor = PANGO_SCALE * 72.0 / 96.;
+      else
+	dpi_conversion_factor = PANGO_SCALE * 72.0 / (int_dpi / PANGO_SCALE);
+
+      g_signal_connect (settings, "notify::gtk-xft-dpi",
+			G_CALLBACK (dpi_changed_cb), NULL);
+    }
+  else
+    /* Approximate. */
+    dpi_conversion_factor = PANGO_SCALE * 72.0 / 96.;
+}
+
+static void
+dpi_changed_cb (GtkSettings  *settings,
+		GParamSpec *pspec __attribute__((unused)))
+{
+  int int_dpi;
+  g_object_get (settings, "gtk-xft-dpi", &int_dpi, NULL);
+  if (int_dpi < 0)
+    dpi_conversion_factor = PANGO_SCALE * 72.0 / 96.;
+  else
+    dpi_conversion_factor = PANGO_SCALE * 72.0 / (int_dpi / PANGO_SCALE);
+}
+
+
+JNIEXPORT void JNICALL 
+Java_gnu_java_awt_peer_gtk_GtkToolkit_iterateNativeQueue
+(JNIEnv *env, 
+ jobject self __attribute__((unused)),
+ jobject lockedQueue)
+{
+  /* We're holding an EventQueue lock, and we're about to acquire the GDK
+   * lock before dropping the EventQueue lock. This can deadlock if someone
+   * holds the GDK lock and wants to acquire the EventQueue lock; however
+   * all callbacks from GTK happen with the GDK lock released, so this
+   * would only happen in an odd case such as some JNI helper code
+   * acquiring the GDK lock and calling back into
+   * EventQueue.getNextEvent().
+   */
+  gdk_threads_enter ();
+  (*env)->MonitorExit (env, lockedQueue);
+
+  /* It is quite important that this be a do .. while loop. The first pass
+   * should do an iteration w/o a test so that it sleeps when there really
+   * aren't any events; and the loop should continue for as many events as
+   * there are to avoid pointless thrashing up and down through JNI (it
+   * runs very slowly when this is not a loop).
+   */
+  do 
+    {
+      gtk_main_iteration();
+    }
+  while (gtk_events_pending());
+
+  (*env)->MonitorEnter (env, lockedQueue);
+  gdk_threads_leave ();
+}
+
+JNIEXPORT void JNICALL 
+Java_gnu_java_awt_peer_gtk_GtkToolkit_wakeNativeQueue
+  (JNIEnv *env __attribute__((unused)), jobject obj __attribute__((unused)))
+{
+  g_main_context_wakeup (NULL);
+}
+
+JNIEXPORT jboolean JNICALL 
+Java_gnu_java_awt_peer_gtk_GtkToolkit_nativeQueueEmpty
+  (JNIEnv *env __attribute__((unused)), jobject obj __attribute__((unused)))
+{
+  jboolean empty = FALSE;
+  gdk_threads_enter ();
+  empty = ! gtk_events_pending();
+  gdk_threads_leave ();
+  return empty;
+}
+
 
 static jint gdk_color_to_java_color (GdkColor color);
 
+
 JNIEXPORT void JNICALL 
 Java_gnu_java_awt_peer_gtk_GtkToolkit_beep
   (JNIEnv *env __attribute__((unused)), jobject obj __attribute__((unused)))

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