8194327: [macos] AWT windows have incorrect main/key window behaviors
Reviewed-by: serb, erikj
Contributed-by: Alan Snyder <fishgarage@cbfiddle.com>
--- a/make/test/JtregNativeJdk.gmk Wed May 16 10:27:25 2018 -0700
+++ b/make/test/JtregNativeJdk.gmk Wed May 16 16:46:51 2018 -0700
@@ -64,6 +64,14 @@
endif
endif
+ifeq ($(OPENJDK_TARGET_OS), macosx)
+ BUILD_JDK_JTREG_LIBRARIES_CFLAGS_libTestMainKeyWindow := -ObjC
+ BUILD_JDK_JTREG_LIBRARIES_LIBS_libTestMainKeyWindow := -framework JavaVM \
+ -framework Cocoa -framework JavaNativeFoundation
+else
+ BUILD_JDK_JTREG_EXCLUDE += libTestMainKeyWindow.c
+endif
+
$(eval $(call SetupTestFilesCompilation, BUILD_JDK_JTREG_LIBRARIES, \
TYPE := LIBRARY, \
SOURCE_DIRS := $(BUILD_JDK_JTREG_NATIVE_SRC), \
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m Wed May 16 10:27:25 2018 -0700
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m Wed May 16 16:46:51 2018 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -262,7 +262,7 @@
} else {
[self.nsWindow setCollectionBehavior:NSWindowCollectionBehaviorDefault];
}
- }
+ }
}
- (id) initWithPlatformWindow:(JNFWeakJObjectWrapper *)platformWindow
@@ -465,7 +465,7 @@
// Tests whether window is blocked by modal dialog/window
- (BOOL) isBlocked {
BOOL isBlocked = NO;
-
+
JNIEnv *env = [ThreadUtilities getJNIEnv];
jobject platformWindow = [self.javaPlatformWindow jObjectWithEnv:env];
if (platformWindow != NULL) {
@@ -473,7 +473,7 @@
isBlocked = JNFCallBooleanMethod(env, platformWindow, jm_isBlocked) == JNI_TRUE ? YES : NO;
(*env)->DeleteLocalRef(env, platformWindow);
}
-
+
return isBlocked;
}
@@ -679,7 +679,7 @@
JNFCallVoidMethod(env, platformWindow, jm_windowWillMiniaturize);
(*env)->DeleteLocalRef(env, platformWindow);
}
- // Excplicitly make myself a key window to avoid possible
+ // Explicitly make myself a key window to avoid possible
// negative visual effects during iconify operation
[self.nsWindow makeKeyAndOrderFront:self.nsWindow];
[self iconifyChildWindows:YES];
@@ -713,13 +713,47 @@
}
}
+- (void) windowDidBecomeMain: (NSNotification *) notification {
+AWT_ASSERT_APPKIT_THREAD;
+ [AWTToolkit eventCountPlusPlus];
+#ifdef DEBUG
+ NSLog(@"became main: %d %@ %@", [self.nsWindow isKeyWindow], [self.nsWindow title], [self menuBarForWindow]);
+#endif
+
+ if (![self.nsWindow isKeyWindow]) {
+ [self activateWindowMenuBar];
+ }
+
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ jobject platformWindow = [self.javaPlatformWindow jObjectWithEnv:env];
+ if (platformWindow != NULL) {
+ static JNF_MEMBER_CACHE(jm_windowDidBecomeMain, jc_CPlatformWindow, "windowDidBecomeMain", "()V");
+ JNFCallVoidMethod(env, platformWindow, jm_windowDidBecomeMain);
+ (*env)->DeleteLocalRef(env, platformWindow);
+ }
+}
- (void) windowDidBecomeKey: (NSNotification *) notification {
AWT_ASSERT_APPKIT_THREAD;
[AWTToolkit eventCountPlusPlus];
+#ifdef DEBUG
+ NSLog(@"became key: %d %@ %@", [self.nsWindow isMainWindow], [self.nsWindow title], [self menuBarForWindow]);
+#endif
AWTWindow *opposite = [AWTWindow lastKeyWindow];
- // Finds appropriate menubar in our hierarchy,
+ if (![self.nsWindow isMainWindow]) {
+ [self activateWindowMenuBar];
+ }
+
+ [AWTWindow setLastKeyWindow:nil];
+
+ [self _deliverWindowFocusEvent:YES oppositeWindow: opposite];
+ [self orderChildWindows:YES];
+}
+
+- (void) activateWindowMenuBar {
+AWT_ASSERT_APPKIT_THREAD;
+ // Finds appropriate menubar in our hierarchy
AWTWindow *awtWindow = self;
while (awtWindow.ownerWindow != nil) {
awtWindow = awtWindow.ownerWindow;
@@ -738,26 +772,48 @@
}
[CMenuBar activate:menuBar modallyDisabled:isDisabled];
-
- [AWTWindow setLastKeyWindow:nil];
+}
- [self _deliverWindowFocusEvent:YES oppositeWindow: opposite];
- [self orderChildWindows:YES];
+#ifdef DEBUG
+- (CMenuBar *) menuBarForWindow {
+AWT_ASSERT_APPKIT_THREAD;
+ AWTWindow *awtWindow = self;
+ while (awtWindow.ownerWindow != nil) {
+ awtWindow = awtWindow.ownerWindow;
+ }
+ return awtWindow.javaMenuBar;
}
+#endif
- (void) windowDidResignKey: (NSNotification *) notification {
// TODO: check why sometimes at start is invoked *not* on AppKit main thread.
AWT_ASSERT_APPKIT_THREAD;
[AWTToolkit eventCountPlusPlus];
- [self.javaMenuBar deactivate];
+#ifdef DEBUG
+ NSLog(@"resigned key: %d %@ %@", [self.nsWindow isMainWindow], [self.nsWindow title], [self menuBarForWindow]);
+#endif
+ if (![self.nsWindow isMainWindow]) {
+ [self deactivateWindow];
+ }
+}
- // In theory, this might cause flickering if the window gaining focus
- // has its own menu. However, I couldn't reproduce it on practice, so
- // perhaps this is a non issue.
- CMenuBar* defaultMenu = [[ApplicationDelegate sharedDelegate] defaultMenuBar];
- if (defaultMenu != nil) {
- [CMenuBar activate:defaultMenu modallyDisabled:NO];
+- (void) windowDidResignMain: (NSNotification *) notification {
+AWT_ASSERT_APPKIT_THREAD;
+ [AWTToolkit eventCountPlusPlus];
+#ifdef DEBUG
+ NSLog(@"resigned main: %d %@ %@", [self.nsWindow isKeyWindow], [self.nsWindow title], [self menuBarForWindow]);
+#endif
+ if (![self.nsWindow isKeyWindow]) {
+ [self deactivateWindow];
}
+}
+
+- (void) deactivateWindow {
+AWT_ASSERT_APPKIT_THREAD;
+#ifdef DEBUG
+ NSLog(@"deactivating window: %@", [self.nsWindow title]);
+#endif
+ [self.javaMenuBar deactivate];
// the new key window
NSWindow *keyWindow = [NSApp keyWindow];
@@ -773,19 +829,6 @@
[self orderChildWindows:NO];
}
-- (void) windowDidBecomeMain: (NSNotification *) notification {
-AWT_ASSERT_APPKIT_THREAD;
- [AWTToolkit eventCountPlusPlus];
-
- JNIEnv *env = [ThreadUtilities getJNIEnv];
- jobject platformWindow = [self.javaPlatformWindow jObjectWithEnv:env];
- if (platformWindow != NULL) {
- static JNF_MEMBER_CACHE(jm_windowDidBecomeMain, jc_CPlatformWindow, "windowDidBecomeMain", "()V");
- JNFCallVoidMethod(env, platformWindow, jm_windowDidBecomeMain);
- (*env)->DeleteLocalRef(env, platformWindow);
- }
-}
-
- (BOOL)windowShouldClose:(id)sender {
AWT_ASSERT_APPKIT_THREAD;
[AWTToolkit eventCountPlusPlus];
@@ -1040,7 +1083,7 @@
AWTWindow *window = (AWTWindow*)[nsWindow delegate];
- if ([nsWindow isKeyWindow]) {
+ if ([nsWindow isKeyWindow] || [nsWindow isMainWindow]) {
[window.javaMenuBar deactivate];
}
@@ -1051,7 +1094,7 @@
actualMenuBar = [[ApplicationDelegate sharedDelegate] defaultMenuBar];
}
- if ([nsWindow isKeyWindow]) {
+ if ([nsWindow isKeyWindow] || [nsWindow isMainWindow]) {
[CMenuBar activate:actualMenuBar modallyDisabled:NO];
}
}];
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CMenuBar.m Wed May 16 10:27:25 2018 -0700
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CMenuBar.m Wed May 16 16:46:51 2018 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -31,6 +31,7 @@
#import "CMenuBar.h"
#import "CMenu.h"
#import "ThreadUtilities.h"
+#import "ApplicationDelegate.h"
#import "sun_lwawt_macosx_CMenuBar.h"
@@ -101,6 +102,10 @@
return;
}
+#ifdef DEBUG
+ NSLog(@"activating menu bar: %@", menubar);
+#endif
+
@synchronized([CMenuBar class]) {
sActiveMenuBar = menubar;
}
@@ -184,12 +189,30 @@
-(void) deactivate {
AWT_ASSERT_APPKIT_THREAD;
+ BOOL isDeactivated = NO;
@synchronized([CMenuBar class]) {
- sActiveMenuBar = nil;
+ if (sActiveMenuBar == self) {
+ sActiveMenuBar = nil;
+ isDeactivated = YES;
+ }
}
- @synchronized(self) {
- fModallyDisabled = NO;
+ if (isDeactivated) {
+#ifdef DEBUG
+ NSLog(@"deactivating menu bar: %@", self);
+#endif
+
+ @synchronized(self) {
+ fModallyDisabled = NO;
+ }
+
+ // In theory, this might cause flickering if the window gaining focus
+ // has its own menu. However, I couldn't reproduce it on practice, so
+ // perhaps this is a non issue.
+ CMenuBar* defaultMenu = [[ApplicationDelegate sharedDelegate] defaultMenuBar];
+ if (defaultMenu != nil) {
+ [CMenuBar activate:defaultMenu modallyDisabled:NO];
+ }
}
}
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CMenuItem.m Wed May 16 10:27:25 2018 -0700
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CMenuItem.m Wed May 16 16:46:51 2018 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,6 +28,7 @@
#import "CMenuItem.h"
#import "CMenu.h"
#import "AWTEvent.h"
+#import "AWTWindow.h"
#import "ThreadUtilities.h"
#import "java_awt_Event.h"
@@ -111,12 +112,14 @@
}
eventKey = [NSString stringWithCharacters: &newChar length: 1];
}
+ // The action event can be ignored only if the key window is an AWT window.
+ // Otherwise, the action event is the only notification and must be processed.
NSWindow *keyWindow = [NSApp keyWindow];
- if (keyWindow != nil) {
+ if (keyWindow != nil && [AWTWindow isAWTWindow: keyWindow]) {
return;
}
- }
-
+ }
+
static JNF_CLASS_CACHE(jc_CMenuItem, "sun/lwawt/macosx/CMenuItem");
static JNF_MEMBER_CACHE(jm_handleAction, jc_CMenuItem, "handleAction", "(JI)V"); // AWT_THREADING Safe (event)
@@ -126,7 +129,7 @@
JNFCallVoidMethod(env, fPeer, jm_handleAction, UTC(currEvent), javaModifiers); // AWT_THREADING Safe (event)
}
JNF_COCOA_EXIT(env);
-
+
}
- (void) setJavaLabel:(NSString *)theLabel shortcut:(NSString *)theKeyEquivalent modifierMask:(jint)modifiers {
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/LWCToolkit.h Wed May 16 10:27:25 2018 -0700
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/LWCToolkit.h Wed May 16 16:46:51 2018 -0700
@@ -30,7 +30,7 @@
#import <Cocoa/Cocoa.h>
-#define DEBUG 1
+//#define DEBUG 1
// number of mouse buttons supported
extern int gNumberOfButtons;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/awt/Window/MainKeyWindowTest/TestMainKeyWindow.java Wed May 16 16:46:51 2018 -0700
@@ -0,0 +1,409 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @key headful
+ * @bug 8194327
+ * @summary [macosx] AWT windows have incorrect main/key window behaviors
+ * @author Alan Snyder
+ * @run main/othervm/native TestMainKeyWindow
+ * @requires (os.family == "mac")
+ */
+
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Objects;
+import javax.swing.*;
+
+public class TestMainKeyWindow
+{
+ static TestMainKeyWindow theTest;
+
+ KeyStroke commandT = KeyStroke.getKeyStroke(KeyEvent.VK_T, KeyEvent.META_DOWN_MASK);
+
+ int nextX = 130;
+
+ private final MyFrame frame1;
+ private final MyFrame frame2;
+ private final Object COLOR_PANEL = "Color Panel";
+ private final Object NATIVE_WINDOW = "Native Window";
+
+ // these bounds must agree with the native code that creates the windows
+ private Rectangle colorPanelBounds = new Rectangle(130, 300, 225, 400); // approximate is OK
+ private Rectangle nativeWindowBounds = new Rectangle(130, 200, 200, 100);
+
+ private Robot robot;
+
+ private int actionCounter;
+ private Object actionTarget;
+
+ private int failureCount;
+ private boolean isApplicationOpened;
+
+ public TestMainKeyWindow()
+ {
+ System.loadLibrary("testMainKeyWindow");
+
+ JMenuBar defaultMenuBar = createMenuBar("Application", true);
+ Desktop.getDesktop().setDefaultMenuBar(defaultMenuBar);
+
+ setup();
+
+ frame1 = new MyFrame("Frame 1");
+ frame2 = new MyFrame("Frame 2");
+ frame1.setVisible(true);
+ frame2.setVisible(true);
+
+ try {
+ robot = new Robot();
+ robot.setAutoDelay(50);
+ } catch (AWTException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ class MyFrame
+ extends JFrame
+ {
+ public MyFrame(String title)
+ throws HeadlessException
+ {
+ super(title);
+
+ JMenuBar mainMenuBar = createMenuBar(title, true);
+ setJMenuBar(mainMenuBar);
+ setBounds(nextX, 60, 200, 90);
+ nextX += 250;
+ JComponent contentPane = new JPanel();
+ setContentPane(contentPane);
+ contentPane.setLayout(new FlowLayout());
+ contentPane.add(new JCheckBox("foo", true));
+ InputMap inputMap = contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
+ inputMap.put(commandT, "test");
+ ActionMap actionMap = contentPane.getActionMap();
+ actionMap.put("test", new MyAction(title + " Key"));
+ }
+ }
+
+ private void runTest()
+ {
+ failureCount = 0;
+ robot.waitForIdle();
+ performTest(frame1, false);
+ performTest(frame1, true);
+ performTest(frame2, false);
+ performTest(frame2, true);
+ performTest(NATIVE_WINDOW, false);
+ performTest(NATIVE_WINDOW, true);
+ performTest(COLOR_PANEL, false);
+ if (failureCount > 0) {
+ throw new RuntimeException("Test failed: " + failureCount + " failure(s)");
+ }
+ }
+
+ private void performTest(Object windowIdentification, boolean selectColorPanel)
+ {
+ setupWindows(windowIdentification, selectColorPanel);
+
+ performMenuShortcutTest(windowIdentification, selectColorPanel);
+ performMenuItemTest(windowIdentification, selectColorPanel);
+
+ // test deactivating and reactivating the application
+ // the window state and behavior should be restored
+
+ openOtherApplication();
+ activateApplication();
+ robot.delay(1000);
+
+ performMenuShortcutTest(windowIdentification, selectColorPanel);
+ performMenuItemTest(windowIdentification, selectColorPanel);
+ }
+
+ private void openOtherApplication() {
+ try {
+ String[] cmd = { "/usr/bin/open", "/Applications/System Preferences.app" };
+ Runtime.getRuntime().exec(cmd);
+ if (!isApplicationOpened) {
+ String[] cmd2 = { "/usr/bin/osascript", "-e",
+ "tell application \"System Preferences\" to set bounds of window 1 to {400, 180, 1068, 821}" };
+ Runtime.getRuntime().exec(cmd2);
+ }
+ isApplicationOpened = true;
+ } catch (IOException ex) {
+ throw new RuntimeException("Unable to deactivate test application");
+ }
+ robot.delay(1000);
+ }
+
+ private void performMenuShortcutTest(Object windowIdentification, boolean selectColorPanel)
+ {
+ int currentActionCount = actionCounter;
+
+ // Perform the menu shortcut
+ robot.keyPress(KeyEvent.VK_META);
+ robot.keyPress(KeyEvent.VK_T);
+ robot.keyRelease(KeyEvent.VK_T);
+ robot.keyRelease(KeyEvent.VK_META);
+ robot.waitForIdle();
+
+ Object target = waitForAction(currentActionCount + 1);
+ boolean isDirectKey = windowIdentification instanceof Window && !selectColorPanel;
+ Object expectedTarget = getExpectedTarget(windowIdentification, isDirectKey);
+ if (!Objects.equals(target, expectedTarget)) {
+ failureCount++;
+ String configuration = getConfigurationName(windowIdentification, selectColorPanel);
+ System.err.println("***** Menu shortcut test failed for " + configuration + ". Expected: " + expectedTarget + ", Actual: " + target);
+ }
+ }
+
+ private void performMenuItemTest(Object windowIdentification, boolean selectColorPanel)
+ {
+ int currentActionCount = actionCounter;
+
+ // Find the menu on the screen menu bar
+ // The location depends upon the application name which is the name of the first menu.
+ // Unfortunately, the application name can vary based on how the application is run.
+ // The work around is to make the menu and the menu item names very long.
+
+ int menuBarX = 250;
+ int menuBarY = 11;
+ int menuItemX = menuBarX;
+ int menuItemY = 34;
+
+ robot.mouseMove(menuBarX, menuBarY);
+ robot.delay(100);
+ robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
+ robot.delay(100);
+ robot.mouseMove(menuItemX, menuItemY);
+ robot.delay(100);
+ robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
+ robot.waitForIdle();
+
+ Object target = waitForAction(currentActionCount + 1);
+ Object expectedTarget = getExpectedTarget(windowIdentification, false);
+ if (!Objects.equals(target, expectedTarget)) {
+ failureCount++;
+ String configuration = getConfigurationName(windowIdentification, selectColorPanel);
+ System.err.println("***** Menu item test failed for " + configuration + ". Expected: " + expectedTarget + ", Actual: " + target);
+ }
+ }
+
+ private String getConfigurationName(Object windowIdentification, boolean selectColorPanel)
+ {
+ String name = "Unknown";
+ if (windowIdentification instanceof Window) {
+ Window w = (Window) windowIdentification;
+ name = getWindowTitle(w);
+ } else if (windowIdentification == NATIVE_WINDOW) {
+ name = "Native Window";
+ } else if (windowIdentification == COLOR_PANEL) {
+ name = "Color Panel";
+ }
+ if (selectColorPanel) {
+ return name + " with color panel";
+ } else {
+ return name;
+ }
+ }
+
+ private Object getExpectedTarget(Object windowIdentification, boolean isDirectKey)
+ {
+ if (windowIdentification instanceof Window) {
+ Window w = (Window) windowIdentification;
+ String title = getWindowTitle(w);
+ if (isDirectKey) {
+ title = title + " Key";
+ }
+ return title;
+ }
+ return "Application";
+ }
+
+ private String getWindowTitle(Window w)
+ {
+ if (w instanceof Frame) {
+ Frame f = (Frame) w;
+ return f.getTitle();
+ }
+ if (w instanceof Dialog) {
+ Dialog d = (Dialog) w;
+ return d.getTitle();
+ }
+ throw new IllegalStateException();
+ }
+
+ private synchronized void registerAction(Object target)
+ {
+ actionCounter++;
+ actionTarget = target;
+ }
+
+ private synchronized Object waitForAction(int count)
+ {
+ try {
+ for (int i = 0; i < 10; i++) {
+ if (actionCounter == count) {
+ return actionTarget;
+ }
+ if (actionCounter > count) {
+ throw new IllegalStateException();
+ }
+ wait(100);
+ }
+ } catch (InterruptedException ex) {
+ }
+ return "No Action";
+ }
+
+ private void setupWindows(Object windowIdentification, boolean selectColorPanel)
+ {
+ clickOnWindowTitleBar(windowIdentification);
+ if (selectColorPanel) {
+ clickOnWindowTitleBar(COLOR_PANEL);
+ }
+ }
+
+ private void clickOnWindowTitleBar(Object windowIdentification)
+ {
+ Rectangle bounds = getWindowBounds(windowIdentification);
+ int x = bounds.x + 70; // to the right of the stoplight buttons
+ int y = bounds.y + 12; // in the title bar
+ robot.mouseMove(x, y);
+ robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
+ robot.waitForIdle();
+ robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
+ robot.waitForIdle();
+ }
+
+ private Rectangle getWindowBounds(Object windowIdentification)
+ {
+ if (windowIdentification instanceof Window) {
+ Window w = (Window) windowIdentification;
+ return w.getBounds();
+ }
+ if (windowIdentification == COLOR_PANEL) {
+ return colorPanelBounds;
+ }
+ if (windowIdentification == NATIVE_WINDOW) {
+ return nativeWindowBounds;
+ }
+ throw new IllegalArgumentException();
+ }
+
+ JMenuBar createMenuBar(String text, boolean isEnabled)
+ {
+ JMenuBar mb = new JMenuBar();
+ // A very long name makes it more likely that the robot will hit the menu
+ JMenu menu = new JMenu("TestTestTestTestTestTestTestTestTestTest");
+ mb.add(menu);
+ JMenuItem item = new JMenuItem("TestTestTestTestTestTestTestTestTestTest");
+ item.setAccelerator(commandT);
+ item.setEnabled(isEnabled);
+ item.addActionListener(ev -> {
+ registerAction(text);
+ });
+ menu.add(item);
+ return mb;
+ }
+
+ void dispose()
+ {
+ frame1.setVisible(false);
+ frame2.setVisible(false);
+ frame1.dispose();
+ frame2.dispose();
+ takedown();
+ Desktop.getDesktop().setDefaultMenuBar(null);
+ if (isApplicationOpened) {
+ try {
+ String[] cmd = { "/usr/bin/osascript", "-e", "tell application \"System Preferences\" to close window 1" };
+ Process p = Runtime.getRuntime().exec(cmd);
+ p.waitFor();
+ } catch (IOException | InterruptedException ex) {
+ }
+ }
+ }
+
+ class MyAction
+ extends AbstractAction
+ {
+ String text;
+
+ public MyAction(String text)
+ {
+ super("Test");
+
+ this.text = text;
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ registerAction(text);
+ }
+ }
+
+ private static native void setup();
+ private static native void takedown();
+ private static native void activateApplication();
+
+ public static void main(String[] args)
+ {
+ if (!System.getProperty("os.name").contains("OS X")) {
+ System.out.println("This test is for MacOS only. Automatically passed on other platforms.");
+ return;
+ }
+
+ System.setProperty("apple.laf.useScreenMenuBar", "true");
+
+ try {
+ runSwing(() -> {
+ theTest = new TestMainKeyWindow();
+ });
+ theTest.runTest();
+ } finally {
+ if (theTest != null) {
+ runSwing(() -> {
+ theTest.dispose();
+ });
+ }
+ }
+ }
+
+ private static void runSwing(Runnable r)
+ {
+ try {
+ SwingUtilities.invokeAndWait(r);
+ } catch (InterruptedException e) {
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/awt/Window/MainKeyWindowTest/libTestMainKeyWindow.c Wed May 16 16:46:51 2018 -0700
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#import <Cocoa/Cocoa.h>
+#import <JavaNativeFoundation/JavaNativeFoundation.h>
+
+static NSWindow *testWindow;
+static NSColorPanel *colorPanel;
+
+/*
+ * Class: TestMainKeyWindow
+ * Method: setup
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_TestMainKeyWindow_setup(JNIEnv *env, jclass cl)
+{
+ JNF_COCOA_ENTER(env);
+
+ void (^block)() = ^(){
+ NSScreen *mainScreen = [NSScreen mainScreen];
+ NSRect screenFrame = [mainScreen frame];
+ NSRect frame = NSMakeRect(130, screenFrame.size.height - 280, 200, 100);
+
+#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
+ NSWindowStyleMask style = NSWindowStyleMaskTitled;
+#else
+ NSInteger style = NSTitledWindowMask;
+#endif
+
+ NSRect rect = [NSWindow contentRectForFrameRect:frame styleMask:style];
+ NSBackingStoreType bt = NSBackingStoreBuffered;
+ testWindow = [[[NSWindow alloc] initWithContentRect:rect styleMask:style backing:bt defer:NO] retain];
+ testWindow.title = @"NSWindow";
+ [testWindow setBackgroundColor:[NSColor blueColor]];
+ [testWindow makeKeyAndOrderFront:nil];
+ // Java coordinates are 100, 200
+
+ colorPanel = [[NSColorPanel sharedColorPanel] retain];
+ [colorPanel setReleasedWhenClosed: YES];
+ colorPanel.restorable = NO;
+ [colorPanel setFrameTopLeftPoint: NSMakePoint(130, screenFrame.size.height - 300)];
+ // Java coordinates are 100, 400
+ [colorPanel makeKeyAndOrderFront: nil];
+ };
+
+ if ([NSThread isMainThread]) {
+ block();
+ } else {
+ [JNFRunLoop performOnMainThreadWaiting:YES withBlock:block];
+ }
+
+ JNF_COCOA_EXIT(env);
+}
+
+/*
+ * Class: TestMainKeyWindow
+ * Method: takedown
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_TestMainKeyWindow_takedown(JNIEnv *env, jclass cl)
+{
+ JNF_COCOA_ENTER(env);
+
+ void (^block)() = ^(){
+ if (testWindow != nil) {
+ [testWindow close];
+ testWindow = nil;
+ }
+ if (colorPanel != nil) {
+ [colorPanel orderOut:nil];
+ colorPanel = nil;
+ }
+ };
+
+ if ([NSThread isMainThread]) {
+ block();
+ } else {
+ [JNFRunLoop performOnMainThreadWaiting:YES withBlock:block];
+ }
+
+ JNF_COCOA_EXIT(env);
+}
+
+/*
+ * Class: TestMainKeyWindow
+ * Method: activateApplication
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_TestMainKeyWindow_activateApplication
+ (JNIEnv *env, jclass cl)
+{
+ JNF_COCOA_ENTER(env);
+
+ void (^block)() = ^(){
+ [NSApp activateIgnoringOtherApps:YES];
+ };
+
+ [JNFRunLoop performOnMainThreadWaiting:YES withBlock:block];
+
+ JNF_COCOA_EXIT(env);
+}