JOGL v2.6.0-rc-20250706
JOGL, High-Performance Graphics Binding for Java™ (public API).
TestNewtKeyEventAutoRepeatAWT.java
Go to the documentation of this file.
1/**
2 * Copyright 2012 JogAmp Community. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification, are
5 * permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 * conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 * of conditions and the following disclaimer in the documentation and/or other materials
12 * provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 *
24 * The views and conclusions contained in the software and documentation are those of the
25 * authors and should not be interpreted as representing official policies, either expressed
26 * or implied, of JogAmp Community.
27 */
28
29package com.jogamp.opengl.test.junit.newt.event;
30
31import org.junit.After;
32import org.junit.Assert;
33import org.junit.AfterClass;
34import org.junit.Assume;
35import org.junit.Before;
36
37import java.awt.AWTException;
38import java.awt.BorderLayout;
39import java.awt.Robot;
40import java.lang.reflect.InvocationTargetException;
41import java.util.Arrays;
42import java.util.EventObject;
43import java.util.List;
44
45import com.jogamp.opengl.GLCapabilities;
46import com.jogamp.opengl.GLEventListener;
47import javax.swing.JFrame;
48
49import java.io.IOException;
50
51import org.junit.BeforeClass;
52import org.junit.Test;
53import org.junit.FixMethodOrder;
54import org.junit.runners.MethodSorters;
55
56import com.jogamp.newt.awt.NewtCanvasAWT;
57import com.jogamp.newt.event.InputEvent;
58import com.jogamp.newt.event.KeyEvent;
59import com.jogamp.newt.opengl.GLWindow;
60import com.jogamp.opengl.util.Animator;
61import com.jogamp.opengl.test.junit.jogl.demos.es2.RedSquareES2;
62import com.jogamp.opengl.test.junit.util.*;
63
64/**
65 * Testing key event order incl. auto-repeat (Bug 601)
66 *
67 * <p>
68 * Note Event order:
69 * <ol>
70 * <li>{@link #EVENT_KEY_PRESSED}</li>
71 * <li>{@link #EVENT_KEY_RELEASED}</li>
72 * </ol>
73 * </p>
74 * <p>
75 * Auto-Repeat shall behave as follow:
76 * <pre>
77 D = pressed, U = released
78 0 = normal, 1 = auto-repeat
79
80 D(0), [ U(1), D(1), U(1), D(1) ..], U(0)
81 * </pre>
82 *
83 * The idea is if you mask out auto-repeat in your event listener
84 * you just get one long pressed key D/U tuple.
85 */
86@FixMethodOrder(MethodSorters.NAME_ASCENDING)
88 static int width, height;
89 static long durationPerTest = 100;
90 static long awtWaitTimeout = 1000;
91
92 static GLCapabilities glCaps;
93
94 @BeforeClass
95 public static void initClass() {
96 width = 640;
97 height = 480;
98 glCaps = new GLCapabilities(null);
99 }
100
101 @AfterClass
102 public static void release() {
103 }
104
105 @Before
106 public void initTest() {
107 }
108
109 @After
110 public void releaseTest() {
111 }
112
113 @Test(timeout=180000) // TO 3 min
114 public void test01NEWT() throws AWTException, InterruptedException, InvocationTargetException {
115 final GLWindow glWindow = GLWindow.create(glCaps);
116 glWindow.setSize(width, height);
117 glWindow.setVisible(true);
118
119 testImpl(glWindow);
120
121 glWindow.destroy();
122 }
123
124 @Test(timeout=180000) // TO 3 min
125 public void test02NewtCanvasAWT() throws AWTException, InterruptedException, InvocationTargetException {
126 final GLWindow glWindow = GLWindow.create(glCaps);
127
128 // Wrap the window in a canvas.
129 final NewtCanvasAWT newtCanvasAWT = new NewtCanvasAWT(glWindow);
130
131 // Add the canvas to a frame, and make it all visible.
132 final JFrame frame1 = new JFrame("Swing AWT Parent Frame: "+ glWindow.getTitle());
133 frame1.getContentPane().add(newtCanvasAWT, BorderLayout.CENTER);
134 frame1.setSize(width, height);
135 javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
136 public void run() {
137 frame1.setVisible(true);
138 } } );
139
140 Assert.assertEquals(true, AWTRobotUtil.waitForVisible(frame1, true, null));
141
142 testImpl(glWindow);
143
144 try {
145 javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
146 public void run() {
147 frame1.setVisible(false);
148 frame1.dispose();
149 }});
150 } catch( final Throwable throwable ) {
151 throwable.printStackTrace();
152 Assume.assumeNoException( throwable );
153 }
154 glWindow.destroy();
155 }
156
157 static void testKeyEventAutoRepeat(final Robot robot, final NEWTKeyAdapter keyAdapter, final int loops, final int pressDurationMS) {
158 System.err.println("KEY Event Auto-Repeat Test: "+loops);
159 final EventObject[][] first = new EventObject[loops][2];
160 final EventObject[][] last = new EventObject[loops][2];
161
162 keyAdapter.reset();
163 int firstIdx = 0;
164 // final ArrayList<EventObject> keyEvents = new ArrayList<EventObject>();
165 for(int i=0; i<loops; i++) {
166 System.err.println("+++ KEY Event Auto-Repeat START Input Loop: "+i);
168 AWTRobotUtil.keyPress(0, robot, true, java.awt.event.KeyEvent.VK_A, pressDurationMS);
169 AWTRobotUtil.keyPress(0, robot, false, java.awt.event.KeyEvent.VK_A, 500); // 1s .. no AR anymore
171 final int minCodeCount = firstIdx + 2;
172 final int desiredCodeCount = firstIdx + 4;
173 for(int j=0; j < NEWTKeyUtil.POLL_DIVIDER && keyAdapter.getQueueSize() < desiredCodeCount; j++) { // wait until events are collected
174 robot.delay(NEWTKeyUtil.TIME_SLICE);
175 }
176 final List<EventObject> keyEvents = keyAdapter.copyQueue();
177 Assert.assertTrue("AR Test didn't collect enough key events: required min "+minCodeCount+", received "+(keyAdapter.getQueueSize()-firstIdx)+", "+keyEvents,
178 keyAdapter.getQueueSize() >= minCodeCount );
179 first[i][0] = keyEvents.get(firstIdx+0);
180 first[i][1] = keyEvents.get(firstIdx+1);
181 firstIdx = keyEvents.size() - 2;
182 last[i][0] = keyEvents.get(firstIdx+0);
183 last[i][1] = keyEvents.get(firstIdx+1);
184 System.err.println("+++ KEY Event Auto-Repeat END Input Loop: "+i);
185
186 // add a pair of normal press/release in between auto-repeat!
187 firstIdx = keyEvents.size();
189 AWTRobotUtil.keyPress(0, robot, true, java.awt.event.KeyEvent.VK_B, 10);
190 AWTRobotUtil.keyPress(0, robot, false, java.awt.event.KeyEvent.VK_B, 250);
192 for(int j=0; j < NEWTKeyUtil.POLL_DIVIDER && keyAdapter.getQueueSize() < firstIdx+2; j++) { // wait until events are collected
193 robot.delay(NEWTKeyUtil.TIME_SLICE);
194 }
195 firstIdx = keyAdapter.getQueueSize();
196 }
197 // dumpKeyEvents(keyEvents);
198 final List<EventObject> keyEvents = keyAdapter.copyQueue();
200
201 final boolean hasAR = 0 < keyAdapter.getKeyPressedCount(true) ;
202
203 {
204 final int perLoopSI = 2; // per loop: 1 non AR event and 1 for non AR 'B'
205 final int expSI, expAR;
206 if( hasAR ) {
207 expSI = perLoopSI * loops;
208 expAR = ( keyEvents.size() - expSI*2 ) / 2; // auto-repeat release
209 } else {
210 expSI = keyEvents.size() / 2; // all released events
211 expAR = 0;
212 }
213
215 expSI /* press-SI */, expSI /* release-SI */,
216 expAR /* press-AR */, expAR /* release-AR */ );
217 }
218
219 if( !hasAR ) {
220 System.err.println("No AUTO-REPEAT triggered by AWT Robot .. aborting test analysis");
221 return;
222 }
223
224 for(int i=0; i<loops; i++) {
225 System.err.println("Auto-Repeat Loop "+i+" - Head:");
226 NEWTKeyUtil.dumpKeyEvents(Arrays.asList(first[i]));
227 System.err.println("Auto-Repeat Loop "+i+" - Tail:");
228 NEWTKeyUtil.dumpKeyEvents(Arrays.asList(last[i]));
229 }
230 for(int i=0; i<loops; i++) {
231 KeyEvent e = (KeyEvent) first[i][0];
232 Assert.assertTrue("1st Shall be A, but is "+e, KeyEvent.VK_A == e.getKeyCode() );
233 Assert.assertTrue("1st Shall be PRESSED, but is "+e, KeyEvent.EVENT_KEY_PRESSED == e.getEventType() );
234 Assert.assertTrue("1st Shall not be AR, but is "+e, 0 == ( InputEvent.AUTOREPEAT_MASK & e.getModifiers() ) );
235
236 e = (KeyEvent) first[i][1];
237 Assert.assertTrue("2nd Shall be A, but is "+e, KeyEvent.VK_A == e.getKeyCode() );
238 Assert.assertTrue("2nd Shall be RELEASED, but is "+e, KeyEvent.EVENT_KEY_RELEASED == e.getEventType() );
239 Assert.assertTrue("2nd Shall be AR, but is "+e, 0 != ( InputEvent.AUTOREPEAT_MASK & e.getModifiers() ) );
240
241 e = (KeyEvent) last[i][0];
242 Assert.assertTrue("last-1 Shall be A, but is "+e, KeyEvent.VK_A == e.getKeyCode() );
243 Assert.assertTrue("last-1 Shall be PRESSED, but is "+e, KeyEvent.EVENT_KEY_PRESSED == e.getEventType() );
244 Assert.assertTrue("last-1 Shall be AR, but is "+e, 0 != ( InputEvent.AUTOREPEAT_MASK & e.getModifiers() ) );
245
246 e = (KeyEvent) last[i][1];
247 Assert.assertTrue("last-0 Shall be A, but is "+e, KeyEvent.VK_A == e.getKeyCode() );
248 Assert.assertTrue("last-2 Shall be RELEASED, but is "+e, KeyEvent.EVENT_KEY_RELEASED == e.getEventType() );
249 Assert.assertTrue("last-0 Shall not be AR, but is "+e, 0 == ( InputEvent.AUTOREPEAT_MASK & e.getModifiers() ) );
250 }
251 }
252
253 void testImpl(final GLWindow glWindow) throws AWTException, InterruptedException, InvocationTargetException {
254 final Robot robot = new Robot();
255 robot.setAutoWaitForIdle(true);
256
257 final GLEventListener demo1 = new RedSquareES2();
258 glWindow.addGLEventListener(demo1);
259
260 final NEWTKeyAdapter glWindow1KA = new NEWTKeyAdapter("GLWindow1");
261 glWindow1KA.setVerbose(false);
262 glWindow.addKeyListener(glWindow1KA);
263
264 Assert.assertEquals(true, NewtTestUtil.waitForRealized(glWindow, true, null));
265
266 // Continuous animation ..
267 final Animator animator = new Animator(glWindow);
268 animator.start();
269
270 Thread.sleep(durationPerTest); // manual testing
271
272 AWTRobotUtil.assertRequestFocusAndWait(null, glWindow, glWindow, null, null); // programmatic
273 AWTRobotUtil.requestFocus(robot, glWindow, false); // within unit framework, prev. tests (TestFocus02SwingAWTRobot) 'confuses' Windows keyboard input
274 glWindow1KA.reset();
275
276 //
277 // Test the key event order w/ auto-repeat
278 //
279 final int origAutoDelay = robot.getAutoDelay();
280 robot.setAutoDelay(10);
281 try {
282 testKeyEventAutoRepeat(robot, glWindow1KA, 3, 1000);
283 } finally {
284 robot.setAutoDelay(origAutoDelay);
285 }
286
287 // Remove listeners to avoid logging during dispose/destroy.
288 glWindow.removeKeyListener(glWindow1KA);
289
290 // Shutdown the test.
291 animator.stop();
292 }
293
294 static int atoi(final String a) {
295 int i=0;
296 try {
297 i = Integer.parseInt(a);
298 } catch (final Exception ex) { ex.printStackTrace(); }
299 return i;
300 }
301
302 public static void main(final String args[]) throws IOException {
303 for(int i=0; i<args.length; i++) {
304 if(args[i].equals("-time")) {
305 durationPerTest = atoi(args[++i]);
306 }
307 }
308 /**
309 BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
310 System.err.println("Press enter to continue");
311 System.err.println(stdin.readLine());
312 */
313 System.out.println("durationPerTest: "+durationPerTest);
314 final String tstname = TestNewtKeyEventAutoRepeatAWT.class.getName();
315 org.junit.runner.JUnitCore.main(tstname);
316 }
317
318
319}
AWT Canvas containing a NEWT Window using native parenting.
final int getModifiers()
Return the modifier bits of this event, e.g.
static final int AUTOREPEAT_MASK
Event is caused by auto-repeat.
Definition: InputEvent.java:71
static final short VK_A
VK_A thru VK_Z are the same as Capital UTF16/ASCII 'A' thru 'Z' (0x41 - 0x5A)
Definition: KeyEvent.java:595
static final short EVENT_KEY_PRESSED
A key has been pressed, excluding auto-repeat-modifier keys.
Definition: KeyEvent.java:362
final short getKeyCode()
Returns the virtual key code using a fixed mapping to the US keyboard layout.
Definition: KeyEvent.java:195
static final short EVENT_KEY_RELEASED
A key has been released, excluding auto-repeat-modifier keys.
Definition: KeyEvent.java:364
final short getEventType()
Returns the event type of this event.
Definition: NEWTEvent.java:72
An implementation of GLAutoDrawable and Window interface, using a delegated Window instance,...
Definition: GLWindow.java:121
final void setSize(final int width, final int height)
Sets the size of the window's client area in window units, excluding decorations.
Definition: GLWindow.java:625
final void setVisible(final boolean visible)
Calls setVisible(true, visible), i.e.
Definition: GLWindow.java:615
final void destroy()
Destroys all resources associated with this GLAutoDrawable, inclusive the GLContext.
Definition: GLWindow.java:605
static GLWindow create(final GLCapabilitiesImmutable caps)
Creates a new GLWindow attaching a new Window referencing a new default Screen and default Display wi...
Definition: GLWindow.java:169
Specifies a set of OpenGL capabilities.
static void requestFocus(final Robot robot, final Object obj)
FIXME: AWTRobotUtil Cleanup: Use specific type for argument object.
static boolean waitForVisible(final java.awt.Component comp, final boolean visible, final Runnable waitAction)
static int keyPress(final int i, final Robot robot, final boolean press, final int keyCode, final int msDelay)
No validation is performed .
static void assertRequestFocusAndWait(final Robot robot, final Object requestFocus, final Object waitForFocus, final FocusEventCountAdapter gain, final FocusEventCountAdapter lost)
static void waitForIdle(final Robot robot)
Issuing validateAWTEDTIsAlive() before calling Robot#waitForIdle().
synchronized List< EventObject > copyQueue()
synchronized void setVerbose(final boolean v)
Instance starts in verbose mode, call w/ false to disable verbosity.
synchronized int getKeyPressedCount(final boolean autoRepeatOnly)
static void dumpKeyEvents(final List< EventObject > keyEvents)
static void validateKeyEventOrder(final List< EventObject > keyEvents)
static void validateKeyAdapterStats(final NEWTKeyAdapter keyAdapter, final int expPressedCountSI, final int expReleasedCountSI, final int expPressedCountAR, final int expReleasedCountAR)
static boolean waitForRealized(final Screen screen, final boolean realized, final Runnable waitAction)
Declares events which client code can use to manage OpenGL rendering into a GLAutoDrawable.