Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[platform-swt-dev] Cocoa SWT_AWT issues, and a new API proposal

I apologize up front for the length of this message, but this collects  
a lot of the issues I have run into over the past months trying to get  
this to work.

Here are the main problems I have found so far. I'm going to need some  
consulting/advice on how to resolve them. In no particular order...

- Creating a Display from the AWT event dispatch thread causes the EDT  
to be associated with the Display. But, when we subclass the AWT's  
NSApplication, the Display will now be called into from the AppKit  
thread. This will eventually lead to a hang because checkDevice() will  
start throwing exceptions and prevent normal event dispatch. I can  
work around that by adding a check for NSThread.isMainThread() in  
Display.checkDevice(), but it violates the spirit of the method  
because the design assumes one Display object per thread.

- Creating any SWT control from the AWT thread generates a spew of  
"__NSAutoreleaseNoPool(): Object xxxx of class yyyyy autoreleased with  
no pool in place - just leaking" because there's no autorelease pool  
in effect, even after we create the Display.  It looks like  
Display.addPool() and Display.removePool() will do what I want, but  
I'm trying to understand why we add an autorelease pool to the current  
thread's threadDictionary. Is this for printing?

- There are some operations that must happen on the AppKit thread,  
like resizing.  I wrote an embedding/threading-aware resize method for  
Shell, but there are other places where we call setFrame that can lead  
to deadlock if done from the AWT thread.

Along those lines, WebKit does NOT like to be used anywhere other than  
the AppKit thread.  Right now we crash if we try to make an SWT  
Browser from the AWT thread.  WebViews are the only AppKit-based  
control I'm aware of that has this problem, so maybe we can deal with  
that as a special case.

-----

I think my overriding concern here is that the API as it is today  
isn't really compatible with the architecture of the AWT on the Mac.  
SWT_AWT.new_Shell(Display d, Canvas c) assumes that you have created a  
Display.  As we see above, this is a problem because the Display is  
created from the AWT event thread, but needs to be 'moved' to the  
AppKit thread.  The obvious answer is 'create it on the AppKit  
thread', but in the Mac AWT you never get that opportunity because the  
AppKit thread is always hidden from you.

I'm starting to think that the right solution for this would be to  
come up with a new API whose Mac implementation is based on Apple's  
CocoaComponent, which was written and designed for exactly this  
situation.  A brief introduction to it is available at <http://developer.apple.com/mac/library/technotes/tn2005/tn2147.html 
 >, which also documents the requirements for AWT and native component  
interaction.

CocoaComponent is a subclass of java.awt.Canvas.  It has an abstract  
method createNSView() that we would implement to create the Display  
and the top-level NSView that would map to the Shell.  That NSView is  
the view we would return to the Mac AWT.  After creating those  
objects, we would define our own abstract method for clients to  
implement that would let them create all of their SWT controls safely  
from the AppKit thread.

I think this could be crafted into a cross-platform API that just  
calls through to the existing SWT_AWT.new_Shell on other platforms.  
Without it I'm not seeing a good way to get this to work without a  
significant rewrite of the SWT.

Conceptually, here's what it would do to developer code.  Today, to  
use SWT_AWT.new_Shell you would do this:

....
Panel panel = new Panel();
frame.add(panel);

/* Create AWT and Swing Widgets */
java.awt.Button awtButton = new java.awt.Button("AWT Button");
panel.add(awtButton);

final Canvas canvas = new Canvas();
panel.add(canvas);

final Display display = new Display ();
final Shell shell = SWT_AWT.new_Shell (display, canvas);

shell.setLayout(new FillLayout());

  /* Create SWT Widgets */
final org.eclipse.swt.widgets.Button swtButton = new  
org.eclipse.swt.widgets.Button(shell, SWT.PUSH);
swtButton.setText("SWT Button");
swtButton.addListener(SWT.Selection, new Listener() {
     public void handleEvent(Event event){
         label.setText("SWT Button selected");
         frame.validate();
     }
});
....

My new proposal would change the code to look like this.  Assume the  
new base class is called SWT_AWT_Shell:

....
Panel panel = new Panel();
frame.add(panel);

/* Create AWT and Swing Widgets */
java.awt.Button awtButton = new java.awt.Button("AWT Button");
panel.add(awtButton);

My_SWT_AWT_Shell canvas = new My_SWT_AWT_Shell();
panel.add(canvas);
...

Then, in your subclass of SWT_AWT_Shell:

public void createSWTComponents() {

Display display = getDisplay();  // Method in SWT_AWT_Shell
Shell shell = getShell();  // Method in SWT_AWT_Shell
shell.setLayout(new FillLayout());

/* Create SWT Widgets */
final org.eclipse.swt.widgets.Button swtButton = new
org.eclipse.swt.widgets.Button(shell, SWT.PUSH);
swtButton.setText("SWT Button");
swtButton.addListener(SWT.Selection, new Listener() {
     public void handleEvent(Event event){
         label.setText("SWT Button selected");
         EventQueue.invokeLater(new Runnable() {
         public void run() {
             frame.validate();
	}
     });

...

createSWTComponents is guaranteed to be called from the AppKit  
thread.  All SWT event handlers are guaranteed to do that as well --  
that's already baked into the implementation of the SWT.

If you have read this far, I hope I have convinced you that  
SWT_AWT.new_Shell looks simple at first, but introduces a lot of  
complexities that need to be addressed.  Feedback is most definitely  
welcome.

Thanks,
Scott K.



Back to the top