From 3db4e89cb2c36f63c6d0a8f3450705d1ef3694b0 Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Fri, 18 Nov 2011 09:14:08 +0100 Subject: NEWT/AWT Focus Traversal / Deadlock Fix (Windows) ; Harmonized NEWT KeyListener handling (Bug 526) NativeWindow: - expose 'hasFocus()' Window: - 'protected enqueueRequestFocus(..)' -> 'public requestFocus(boolean wait)' - New: 'setKeyboardFocusHandler(KeyListener)' allowing focus traversal co-op w/ covered TK (AWT) WindowImpl: - Impl Window changes (see above) - Impl 'consumedTag' see commit 3b38957f36d4f89b85730755a41c00892ac70591 NewtCanvasAWT: - FocusAction only removes the global AWT focus owner. This fixes a deadlock on the Windows platform of AWT's native peer requestFocus impl, since it's no more called at this point. - NEW FocusTraversalKeyListener is set as the newtChild's KeyboardFocusHandler, allowing traversal to the next/previous AWT component. AWTParentWindowAdapter: - focusGained(..) clears AWT focus and propagates focus to Newt child, non blocking w/ 'requestFocus(false)' (see above) KeyEvent: - Document limitations of getKeyChar() (Bug 526) MacWindow: - only deliver keyChar on key Typed events, harmonizing platform behavior (Bug 526) WindowsWindow: - regenerate the keyCode for EVENT_KEY_TYPED (Bug 526) X11Windows: - complete keyCode mapping X11 -> Newt - X11KeySym2NewtVKey() - only deliver keyChar on key Typed events, harmonizing platform behavior (Bug 526) Tests: - GearsES2: Make focus visible - TestParentingFocusTraversal01AWT: unit test for keyboard focus traversal w/ NewtCanvasAWT --- src/newt/classes/com/jogamp/newt/Window.java | 38 +++++++++- .../classes/com/jogamp/newt/awt/NewtCanvasAWT.java | 81 ++++++++++++++++++---- .../classes/com/jogamp/newt/event/KeyEvent.java | 4 ++ .../classes/com/jogamp/newt/opengl/GLWindow.java | 9 +++ 4 files changed, 114 insertions(+), 18 deletions(-) (limited to 'src/newt/classes/com') diff --git a/src/newt/classes/com/jogamp/newt/Window.java b/src/newt/classes/com/jogamp/newt/Window.java index ae6bd2b8c..b0df7a28a 100644 --- a/src/newt/classes/com/jogamp/newt/Window.java +++ b/src/newt/classes/com/jogamp/newt/Window.java @@ -30,6 +30,8 @@ package com.jogamp.newt; import com.jogamp.newt.event.WindowListener; import com.jogamp.newt.event.KeyListener; +import com.jogamp.newt.event.KeyEvent; +import com.jogamp.newt.event.InputEvent; import com.jogamp.newt.event.MouseListener; import jogamp.newt.Debug; import javax.media.nativewindow.CapabilitiesChooser; @@ -309,16 +311,46 @@ public interface Window extends NativeWindow, WindowClosingProtocol { } /** - * May set to a {@link FocusRunnable}, {@link FocusRunnable#run()} before Newt requests the native focus. + * Sets a {@link FocusRunnable}, + * which {@link FocusRunnable#run()} method is executed before the native focus is requested. + *

* This allows notifying a covered window toolkit like AWT that the focus is requested, * hence focus traversal can be made transparent. + *

*/ void setFocusAction(FocusRunnable focusAction); + + /** + * Sets a {@link KeyListener} allowing focus traversal with a covered window toolkit like AWT. + *

+ * The {@link KeyListener} methods are invoked prior to all other {@link KeyListener}'s + * allowing to suppress the {@link KeyEvent} via the {@link InputEvent#consumedTag}. + *

+ * @param l + */ + void setKeyboardFocusHandler(KeyListener l); + /** + * Request focus for this native window + *

+ * The request is handled on this Window EDT and blocked until finished. + *

+ * + * @see #requestFocus(boolean) + */ void requestFocus(); - boolean hasFocus(); - + /** + * Request focus for this native window + *

+ * The request is handled on this Window EDT. + *

+ * + * @param wait true if waiting until the request is executed, otherwise false + * @see #requestFocus() + */ + void requestFocus(boolean wait); + void windowRepaint(int x, int y, int width, int height); void enqueueEvent(boolean wait, com.jogamp.newt.event.NEWTEvent event); diff --git a/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java b/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java index 8a0cb8d6c..18ecdf772 100644 --- a/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java +++ b/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java @@ -29,6 +29,7 @@ package com.jogamp.newt.awt; +import java.awt.AWTKeyStroke; import java.awt.Canvas; import java.awt.EventQueue; import java.awt.Graphics; @@ -37,6 +38,7 @@ import java.awt.KeyboardFocusManager; import java.lang.reflect.Method; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.Set; import javax.media.nativewindow.Capabilities; import javax.media.nativewindow.CapabilitiesImmutable; @@ -54,6 +56,9 @@ import jogamp.newt.awt.event.NewtFactoryAWT; import com.jogamp.newt.Display; import com.jogamp.newt.Window; +import com.jogamp.newt.event.InputEvent; +import com.jogamp.newt.event.KeyEvent; +import com.jogamp.newt.event.KeyListener; import com.jogamp.newt.event.WindowAdapter; import com.jogamp.newt.event.WindowEvent; import com.jogamp.newt.event.WindowListener; @@ -167,6 +172,9 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto class FocusAction implements Window.FocusRunnable { public boolean run() { + if(!isNewtChildOnscreen) { + throw new InternalError("focusAction() shall not be invoked for offscreen windows by native code"); + } if ( EventQueue.isDispatchThread() ) { focusActionImpl.run(); } else { @@ -175,11 +183,6 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto } catch (Exception e) { throw new NativeWindowException(e); } - /** - // wait for AWT focus ! - for(long sleep = Window.TIMEOUT_NATIVEWINDOW; 0 fwdKeys = kfm.getDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); + final Set bwdKeys = kfm.getDefaultFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); + if(fwdKeys.contains(ks)) { + if(DEBUG) { + System.err.println("FTKL.handleKey (fwd): "+ks); + } + kfm.focusNextComponent(NewtCanvasAWT.this); + suppress = true; + } else if(bwdKeys.contains(ks)) { + if(DEBUG) { + System.err.println("FTKL.handleKey (bwd): "+ks); + } + kfm.focusPreviousComponent(NewtCanvasAWT.this); + suppress = true; + } else if(DEBUG) { + System.err.println("FTKL.handleKey (nop): "+ks); + } + } else if(DEBUG) { + System.err.println("FTKL.handleKey: null"); + } + if(suppress) { + e.setAttachment(InputEvent.consumedTag); + } + } + } + FocusTraversalKeyListener newtFocusTraversalKeyListener = null; + /** sets a new NEWT child, provoking reparenting. */ private NewtCanvasAWT setNEWTChild(Window child) { if(newtChild!=child) { @@ -240,8 +287,12 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto private final void configureNewtChildInputEventHandler() { if(null==awtMouseAdapter && null != newtChild.getGraphicsConfiguration()) { isNewtChildOnscreen = newtChild.getGraphicsConfiguration().getChosenCapabilities().isOnscreen(); - if(!isNewtChildOnscreen) { - // offscreen childs require AWT event fwd for key/mouse + if(isNewtChildOnscreen) { + // onscreen child needs to fwd focus traversal + newtFocusTraversalKeyListener = new FocusTraversalKeyListener(); + newtChild.setKeyboardFocusHandler(newtFocusTraversalKeyListener); + } else { + // offscreen child require AWT event fwd for key/mouse awtMouseAdapter = new AWTMouseAdapter(newtChild).addTo(this); awtKeyAdapter = new AWTKeyAdapter(newtChild).addTo(this); } @@ -261,6 +312,10 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto awtKeyAdapter.removeFrom(this); awtKeyAdapter = null; } + if(null!=newtFocusTraversalKeyListener) { + newtChild.setKeyboardFocusHandler(null); + newtFocusTraversalKeyListener = null; + } if( null != newtChild ) { if(attach) { @@ -401,10 +456,6 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto } } - private final void requestFocusAWTParent() { - super.requestFocusInWindow(); - } - private final void requestFocusNEWTChild() { if(null!=newtChild) { newtChild.setFocusAction(null); diff --git a/src/newt/classes/com/jogamp/newt/event/KeyEvent.java b/src/newt/classes/com/jogamp/newt/event/KeyEvent.java index 9e4fe372b..44fcea49c 100644 --- a/src/newt/classes/com/jogamp/newt/event/KeyEvent.java +++ b/src/newt/classes/com/jogamp/newt/event/KeyEvent.java @@ -34,6 +34,7 @@ package com.jogamp.newt.event; +@SuppressWarnings("serial") public class KeyEvent extends InputEvent { public KeyEvent(int eventType, Object source, long when, int modifiers, int keyCode, char keyChar) { @@ -42,9 +43,12 @@ public class KeyEvent extends InputEvent this.keyChar=keyChar; } + /** Only valid if delivered via {@link KeyListener#keyPressed(KeyEvent)} */ public char getKeyChar() { return keyChar; } + + /** Always valid. */ public int getKeyCode() { return keyCode; } diff --git a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java index e9ac272c8..fd216bfda 100644 --- a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java +++ b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java @@ -41,6 +41,7 @@ import com.jogamp.common.GlueGenVersion; import com.jogamp.common.util.VersionUtil; import com.jogamp.newt.*; import com.jogamp.newt.event.*; + import jogamp.newt.WindowImpl; import javax.media.nativewindow.*; @@ -254,10 +255,18 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC window.setFocusAction(focusAction); } + public void setKeyboardFocusHandler(KeyListener l) { + window.setKeyboardFocusHandler(l); + } + public final void requestFocus() { window.requestFocus(); } + public final void requestFocus(boolean wait) { + window.requestFocus(wait); + } + public boolean hasFocus() { return window.hasFocus(); } -- cgit v1.2.3