diff options
Diffstat (limited to 'src/newt/classes')
8 files changed, 215 insertions, 62 deletions
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. + * <p> * This allows notifying a covered window toolkit like AWT that the focus is requested, * hence focus traversal can be made transparent. + * </p> */ void setFocusAction(FocusRunnable focusAction); + + /** + * Sets a {@link KeyListener} allowing focus traversal with a covered window toolkit like AWT. + * <p> + * The {@link KeyListener} methods are invoked prior to all other {@link KeyListener}'s + * allowing to suppress the {@link KeyEvent} via the {@link InputEvent#consumedTag}. + * </p> + * @param l + */ + void setKeyboardFocusHandler(KeyListener l); + /** + * Request focus for this native window + * <p> + * The request is handled on this Window EDT and blocked until finished. + * </p> + * + * @see #requestFocus(boolean) + */ void requestFocus(); - boolean hasFocus(); - + /** + * Request focus for this native window + * <p> + * The request is handled on this Window EDT. + * </p> + * + * @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<sleep && !isFocusOwner(); sleep-=10 ) { - try { Thread.sleep(10); } catch (InterruptedException e) { } - } */ } return focusActionImpl.result; } @@ -190,10 +193,7 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto if(DEBUG) { System.err.println("FocusActionImpl.run() "+Display.getThreadName()); } - NewtCanvasAWT.this.requestFocusAWTParent(); - if(isNewtChildOnscreen) { - KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner(); - } + KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner(); } } FocusActionImpl focusActionImpl = new FocusActionImpl(); @@ -207,6 +207,53 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto } }; + class FocusTraversalKeyListener implements KeyListener { + boolean suppress = false; + + public void keyPressed(KeyEvent e) { + handleKey(e, false); + } + public void keyReleased(KeyEvent e) { + handleKey(e, true); + } + public void keyTyped(KeyEvent e) { + if(suppress) { + e.setAttachment(InputEvent.consumedTag); + suppress = false; // reset + } + } + + void handleKey(KeyEvent e, boolean onRelease) { + final AWTKeyStroke ks = AWTKeyStroke.getAWTKeyStroke(e.getKeyCode(), e.getModifiers(), onRelease); + if(null != ks) { + final KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + final Set<AWTKeyStroke> fwdKeys = kfm.getDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); + final Set<AWTKeyStroke> 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(); } diff --git a/src/newt/classes/jogamp/newt/WindowImpl.java b/src/newt/classes/jogamp/newt/WindowImpl.java index 3865cd6c8..ef927ec16 100644 --- a/src/newt/classes/jogamp/newt/WindowImpl.java +++ b/src/newt/classes/jogamp/newt/WindowImpl.java @@ -110,6 +110,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer private RequestFocusAction requestFocusAction = new RequestFocusAction(); private FocusRunnable focusAction = null; + private KeyListener keyboardFocusHandler = null; private SurfaceUpdatedHelper surfaceUpdatedHelper = new SurfaceUpdatedHelper(); @@ -1415,14 +1416,6 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } - public void requestFocus() { - enqueueRequestFocus(true); - } - - public final boolean hasFocus() { - return hasFocus; - } - public final InsetsImmutable getInsets() { if(isUndecorated()) { return Insets.getZero(); @@ -1559,15 +1552,18 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } - protected void enqueueRequestFocus(boolean wait) { + public final boolean hasFocus() { + return hasFocus; + } + + public void requestFocus() { + requestFocus(true); + } + + public void requestFocus(boolean wait) { runOnEDTIfAvail(wait, requestFocusAction); } - /** - * May set to a {@link FocusRunnable}, {@link FocusRunnable#run()} before Newt requests the native focus. - * This allows notifying a covered window toolkit like AWT that the focus is requested, - * hence focus traversal can be made transparent. - */ public void setFocusAction(FocusRunnable focusAction) { this.focusAction = focusAction; } @@ -1588,7 +1584,11 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } return res; } - + + public void setKeyboardFocusHandler(KeyListener l) { + keyboardFocusHandler = l; + } + private class SetPositionActionImpl implements Runnable { int x, y; @@ -1830,8 +1830,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // queue event in case window is locked, ie in operation if( isWindowLocked() ) { if(DEBUG_IMPLEMENTATION) { - // System.err.println("Window.consumeEvent: queued "+e); - // Thread.dumpStack(); // JAU + System.err.println("Window.consumeEvent: queued "+e); + // Thread.dumpStack(); } return false; } @@ -2021,8 +2021,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(DEBUG_MOUSE_EVENT) { System.err.println("consumeMouseEvent: event: "+e); } - - for(int i = 0; i < mouseListeners.size(); i++ ) { + boolean consumed = false; + for(int i = 0; !consumed && i < mouseListeners.size(); i++ ) { MouseListener l = mouseListeners.get(i); switch(e.getEventType()) { case MouseEvent.EVENT_MOUSE_CLICKED: @@ -2052,13 +2052,13 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer default: throw new NativeWindowException("Unexpected mouse event type " + e.getEventType()); } + consumed = InputEvent.consumedTag == e.getAttachment(); } } // // KeyListener/Event Support // - public void sendKeyEvent(int eventType, int modifiers, int keyCode, char keyChar) { consumeKeyEvent(new KeyEvent(eventType, this, System.currentTimeMillis(), modifiers, keyCode, keyChar) ); } @@ -2107,26 +2107,39 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer return (KeyListener[]) keyListeners.toArray(); } - protected void consumeKeyEvent(KeyEvent e) { - if(DEBUG_KEY_EVENT) { - System.err.println("consumeKeyEvent: "+e); + private final boolean propagateKeyEvent(KeyEvent e, KeyListener l) { + switch(e.getEventType()) { + case KeyEvent.EVENT_KEY_PRESSED: + l.keyPressed(e); + break; + case KeyEvent.EVENT_KEY_RELEASED: + l.keyReleased(e); + break; + case KeyEvent.EVENT_KEY_TYPED: + l.keyTyped(e); + break; + default: + throw new NativeWindowException("Unexpected key event type " + e.getEventType()); } - for(int i = 0; i < keyListeners.size(); i++ ) { - KeyListener l = keyListeners.get(i); - switch(e.getEventType()) { - case KeyEvent.EVENT_KEY_PRESSED: - l.keyPressed(e); - break; - case KeyEvent.EVENT_KEY_RELEASED: - l.keyReleased(e); - break; - case KeyEvent.EVENT_KEY_TYPED: - l.keyTyped(e); - break; - default: - throw new NativeWindowException("Unexpected key event type " + e.getEventType()); + return InputEvent.consumedTag == e.getAttachment(); + } + + protected void consumeKeyEvent(KeyEvent e) { + boolean consumed; + if(null != keyboardFocusHandler) { + consumed = propagateKeyEvent(e, keyboardFocusHandler); + if(DEBUG_KEY_EVENT) { + System.err.println("consumeKeyEvent: "+e+", keyboardFocusHandler consumed: "+consumed); + } + } else { + consumed = false; + if(DEBUG_KEY_EVENT) { + System.err.println("consumeKeyEvent: "+e); } } + for(int i = 0; !consumed && i < keyListeners.size(); i++ ) { + consumed = propagateKeyEvent(e, keyListeners.get(i)); + } } // diff --git a/src/newt/classes/jogamp/newt/awt/event/AWTParentWindowAdapter.java b/src/newt/classes/jogamp/newt/awt/event/AWTParentWindowAdapter.java index 358864547..313a5e868 100644 --- a/src/newt/classes/jogamp/newt/awt/event/AWTParentWindowAdapter.java +++ b/src/newt/classes/jogamp/newt/awt/event/AWTParentWindowAdapter.java @@ -28,6 +28,8 @@ package jogamp.newt.awt.event; +import java.awt.KeyboardFocusManager; + import com.jogamp.newt.event.awt.AWTAdapter; import com.jogamp.newt.event.awt.AWTWindowAdapter; @@ -54,9 +56,16 @@ public class AWTParentWindowAdapter } public void focusGained(java.awt.event.FocusEvent e) { + // forward focus to NEWT child + final com.jogamp.newt.Window newtChild = getNewtWindow(); + final boolean isOnscreen = newtChild.isNativeValid() && newtChild.getGraphicsConfiguration().getChosenCapabilities().isOnscreen(); if(DEBUG_IMPLEMENTATION) { - System.err.println("AWT: focusGained: "+ e); + System.err.println("AWT: focusGained: onscreen "+ isOnscreen+", "+e); + } + if(isOnscreen) { + KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner(); } + newtChild.requestFocus(false); } public void focusLost(java.awt.event.FocusEvent e) { diff --git a/src/newt/classes/jogamp/newt/driver/macosx/MacWindow.java b/src/newt/classes/jogamp/newt/driver/macosx/MacWindow.java index 292a9c255..e1b2ef87d 100644 --- a/src/newt/classes/jogamp/newt/driver/macosx/MacWindow.java +++ b/src/newt/classes/jogamp/newt/driver/macosx/MacWindow.java @@ -311,20 +311,24 @@ public class MacWindow extends WindowImpl implements SurfaceChangeable { @Override public void sendKeyEvent(int eventType, int modifiers, int keyCode, char keyChar) { - final int key = convertKeyChar(keyChar); - if(DEBUG_IMPLEMENTATION) System.err.println("MacWindow.sendKeyEvent "+Thread.currentThread().getName()); // Note that we send the key char for the key code on this // platform -- we do not get any useful key codes out of the system - super.sendKeyEvent(eventType, modifiers, key, keyChar); + final int keyCode2 = convertKeyChar(keyChar); + if(DEBUG_IMPLEMENTATION) System.err.println("MacWindow.sendKeyEvent "+Thread.currentThread().getName()+" char: "+keyChar+", code 0x"+Integer.toHexString(keyCode)+" -> 0x"+Integer.toHexString(keyCode2)); + // only deliver keyChar on key Typed events, harmonizing platform behavior + keyChar = KeyEvent.EVENT_KEY_TYPED == eventType ? keyChar : (char)-1; + super.sendKeyEvent(eventType, modifiers, keyCode2, keyChar); } @Override public void enqueueKeyEvent(boolean wait, int eventType, int modifiers, int keyCode, char keyChar) { - final int key = convertKeyChar(keyChar); - if(DEBUG_IMPLEMENTATION) System.err.println("MacWindow.enqueueKeyEvent "+Thread.currentThread().getName()); // Note that we send the key char for the key code on this // platform -- we do not get any useful key codes out of the system - super.enqueueKeyEvent(wait, eventType, modifiers, key, keyChar); + final int keyCode2 = convertKeyChar(keyChar); + if(DEBUG_IMPLEMENTATION) System.err.println("MacWindow.enqueueKeyEvent "+Thread.currentThread().getName()+" char: "+keyChar+", code 0x"+Integer.toHexString(keyCode)+" -> 0x"+Integer.toHexString(keyCode2)); + // only deliver keyChar on key Typed events, harmonizing platform behavior + keyChar = KeyEvent.EVENT_KEY_TYPED == eventType ? keyChar : (char)-1; + super.enqueueKeyEvent(wait, eventType, modifiers, keyCode2, keyChar); } //---------------------------------------------------------------------- diff --git a/src/newt/classes/jogamp/newt/driver/windows/WindowsWindow.java b/src/newt/classes/jogamp/newt/driver/windows/WindowsWindow.java index cd5909d42..28be93acd 100644 --- a/src/newt/classes/jogamp/newt/driver/windows/WindowsWindow.java +++ b/src/newt/classes/jogamp/newt/driver/windows/WindowsWindow.java @@ -42,6 +42,7 @@ import javax.media.nativewindow.util.Insets; import javax.media.nativewindow.util.InsetsImmutable; import javax.media.nativewindow.util.Point; +import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.event.MouseAdapter; import com.jogamp.newt.event.MouseEvent; @@ -244,6 +245,36 @@ public class WindowsWindow extends WindowImpl { // nop - using event driven insetsChange(..) } + private final int validateKeyCode(int eventType, int keyCode) { + switch(eventType) { + case KeyEvent.EVENT_KEY_PRESSED: + lastPressedKeyCode = keyCode; + break; + case KeyEvent.EVENT_KEY_TYPED: + if(-1==keyCode) { + keyCode = lastPressedKeyCode; + } + lastPressedKeyCode = -1; + break; + } + return keyCode; + } + private int lastPressedKeyCode = 0; + + @Override + public void sendKeyEvent(int eventType, int modifiers, int keyCode, char keyChar) { + // Note that we have to regenerate the keyCode for EVENT_KEY_TYPED on this platform + keyCode = validateKeyCode(eventType, keyCode); + super.sendKeyEvent(eventType, modifiers, keyCode, keyChar); + } + + @Override + public void enqueueKeyEvent(boolean wait, int eventType, int modifiers, int keyCode, char keyChar) { + // Note that we have to regenerate the keyCode for EVENT_KEY_TYPED on this platform + keyCode = validateKeyCode(eventType, keyCode); + super.enqueueKeyEvent(wait, eventType, modifiers, keyCode, keyChar); + } + //---------------------------------------------------------------------- // Internals only // |