1 /*
   2  * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25   @test
  26   @key headful
  27   @bug 4799136
  28   @summary Tests that type-ahead for dialog works and doesn't block program
  29   @library    ../../regtesthelpers
  30   @modules java.desktop/sun.awt
  31   @build      Util
  32   @run main TestDialogTypeAhead
  33 */
  34 
  35 import java.awt.*;
  36 import java.awt.event.*;
  37 import java.lang.reflect.InvocationTargetException;
  38 
  39 public class TestDialogTypeAhead {
  40     //Declare things used in the test, like buttons and labels here
  41     static Frame f;
  42     static Button b;
  43     static Dialog d;
  44     static Button ok;
  45     static Semaphore pressSema = new Semaphore();
  46     static Semaphore robotSema = new Semaphore();
  47     static volatile boolean gotFocus = false;
  48     static Robot robot;
  49 
  50     public static void main(final String[] args) {
  51         TestDialogTypeAhead app = new TestDialogTypeAhead();
  52         app.init();
  53         app.start();
  54     }
  55 
  56     public void init()
  57     {
  58         Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
  59                 public void eventDispatched(AWTEvent e) {
  60                     System.err.println(e.toString());
  61                 }
  62             }, AWTEvent.KEY_EVENT_MASK);
  63 
  64         KeyboardFocusManager.setCurrentKeyboardFocusManager(new TestKFM());
  65 
  66         f = new Frame("frame");
  67         b = new Button("press");
  68         d = new Dialog(f, "dialog", true);
  69         ok = new Button("ok");
  70         d.add(ok);
  71         d.pack();
  72 
  73         ok.addKeyListener(new KeyAdapter() {
  74                 public void keyPressed(KeyEvent e) {
  75                     System.err.println("OK pressed");
  76                     d.dispose();
  77                     f.dispose();
  78                     // Typed-ahead key events should only be accepted if
  79                     // they arrive after FOCUS_GAINED
  80                     if (gotFocus) {
  81                         pressSema.raise();
  82                     }
  83                 }
  84             });
  85         ok.addFocusListener(new FocusAdapter() {
  86                 public void focusGained(FocusEvent e) {
  87                     gotFocus = true;
  88                     System.err.println("Ok got focus");
  89                 }
  90             });
  91         f.add(b);
  92         f.pack();
  93         b.addActionListener(new ActionListener() {
  94                 public void actionPerformed(ActionEvent e) {
  95                     System.err.println("B pressed");
  96 
  97                     EventQueue.invokeLater(new Runnable() {
  98                             public void run() {
  99                                 waitTillShown(d);
 100                                 TestDialogTypeAhead.this.d.toFront();
 101                                 TestDialogTypeAhead.this.moveMouseOver(d);
 102                             }
 103                         });
 104 
 105                     d.setVisible(true);
 106                 }
 107             });
 108 
 109     }//End  init()
 110 
 111     public void start ()
 112     {
 113         try {
 114             robot = new Robot();
 115         } catch (Exception e) {
 116             throw new RuntimeException("Can't create robot:" + e);
 117         }
 118         f.setLocationRelativeTo(null);
 119         f.setVisible(true);
 120         waitTillShown(b);
 121         System.err.println("b is shown");
 122         f.toFront();
 123         moveMouseOver(f);
 124         waitForIdle();
 125         makeFocused(b);
 126         waitForIdle();
 127         System.err.println("b is focused");
 128 
 129         robot.keyPress(KeyEvent.VK_SPACE);
 130         robot.keyRelease(KeyEvent.VK_SPACE);
 131         try {
 132             robotSema.doWait(1000);
 133         } catch (InterruptedException ie) {
 134             throw new RuntimeException("Interrupted!");
 135         }
 136         if (!robotSema.getState()) {
 137             throw new RuntimeException("robotSema hasn't been triggered");
 138         }
 139 
 140         System.err.println("typing ahead");
 141         robot.keyPress(KeyEvent.VK_SPACE);
 142         robot.keyRelease(KeyEvent.VK_SPACE);
 143         waitForIdle();
 144         try {
 145             pressSema.doWait(3000);
 146         } catch (InterruptedException ie) {
 147             throw new RuntimeException("Interrupted!");
 148         }
 149         if (!pressSema.getState()) {
 150             throw new RuntimeException("Type-ahead doesn't work");
 151         }
 152 
 153     }// start()
 154 
 155     private void moveMouseOver(Container c) {
 156         Point p = c.getLocationOnScreen();
 157         Dimension d = c.getSize();
 158         robot.mouseMove(p.x + (int)(d.getWidth()/2), p.y + (int)(d.getHeight()/2));
 159     }
 160     private void waitForIdle() {
 161         try {
 162             robot.waitForIdle();
 163             EventQueue.invokeAndWait( new Runnable() {
 164                                             public void run() {
 165                                                 // dummy implementation
 166                                             }
 167                                         } );
 168         } catch(InterruptedException ite) {
 169             System.err.println("Robot.waitForIdle, non-fatal exception caught:");
 170             ite.printStackTrace();
 171         } catch(InvocationTargetException ine) {
 172             System.err.println("Robot.waitForIdle, non-fatal exception caught:");
 173             ine.printStackTrace();
 174         }
 175     }
 176 
 177     private void waitTillShown(Component c) {
 178         while (true) {
 179             try {
 180                 Thread.sleep(100);
 181                 c.getLocationOnScreen();
 182                 break;
 183             } catch (InterruptedException ie) {
 184                 ie.printStackTrace();
 185                 break;
 186             } catch (Exception e) {
 187             }
 188         }
 189     }
 190     private void makeFocused(Component comp) {
 191         if (comp.isFocusOwner()) {
 192             return;
 193         }
 194         final Semaphore sema = new Semaphore();
 195         final FocusAdapter fa = new FocusAdapter() {
 196                 public void focusGained(FocusEvent fe) {
 197                     sema.raise();
 198                 }
 199             };
 200         comp.addFocusListener(fa);
 201         comp.requestFocusInWindow();
 202         if (comp.isFocusOwner()) {
 203             return;
 204         }
 205         try {
 206             sema.doWait(3000);
 207         } catch (InterruptedException ie) {
 208             ie.printStackTrace();
 209         }
 210         comp.removeFocusListener(fa);
 211         if (!comp.isFocusOwner()) {
 212             throw new RuntimeException("Can't make " + comp + " focused, current owner is " + KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner());
 213         }
 214     }
 215 
 216     static class Semaphore {
 217         boolean state = false;
 218         int waiting = 0;
 219         public Semaphore() {
 220         }
 221         public synchronized void doWait() throws InterruptedException {
 222             if (state) {
 223                 return;
 224             }
 225             waiting++;
 226             wait();
 227             waiting--;
 228         }
 229         public synchronized void doWait(int timeout) throws InterruptedException {
 230             if (state) {
 231                 return;
 232             }
 233             waiting++;
 234             wait(timeout);
 235             waiting--;
 236         }
 237         public synchronized void raise() {
 238             state = true;
 239             if (waiting > 0) {
 240                 notifyAll();
 241             }
 242         }
 243         public synchronized boolean getState() {
 244             return state;
 245         }
 246     }
 247 
 248     class TestKFM extends DefaultKeyboardFocusManager {
 249         protected synchronized void enqueueKeyEvents(long after,
 250                                                      Component untilFocused)
 251         {
 252             super.enqueueKeyEvents(after, untilFocused);
 253 
 254             if (untilFocused == TestDialogTypeAhead.this.ok) {
 255                 TestDialogTypeAhead.this.robotSema.raise();
 256             }
 257         }
 258     }
 259 }// class TestDialogTypeAhead
 260