Обновление 12/21:
Недавно был выпущен 7u10. Подтверждено, что:- Проблема все еще сохраняется.
- К счастью, обходной путь все еще функционирует!
Обновление 11/7:
И у нас есть обходной путь!
Хорошо, хотя я еще не уверен на 100%, но похоже, что когда мы входим в полноэкранный режим, другое окно становится первым ответчиком, а значит и звуковым сигналом. Не могли бы вы попробовать следующее обходное решение: после вызова setFullScreenWindow() в фрейме вызовите setVisible (false), а затем setVisible (true). Это, теоретически, должно восстановить правильного первого ответчика.
Фрагмент кода, который, кажется, работает, просто:
dev.setFullScreenWindow(f);
f.setVisible(false);
f.setVisible(true);
Я обновил код примера с возможностью включения и выключения этого исправления; он требуется каждый раз, когда окно входит в полноэкранный режим.
В более широком контексте моего более сложного приложения я все еще сталкиваюсь с проблемами фокуса клавиатуры на подкомпонентах в полноэкранном окне, где щелчок мыши вызывает мое окно, чтобы потерять фокус. (Я предполагаю, что это произойдет с нежелательным окном первого ответчика, о котором говорилось выше.) Я отчитаю, когда у меня будет больше информации об этом случае - я не могу воспроизвести его еще в маленьком образце.
Обновление 10/31:
Основное обновление к образцу кода:
- Включает переключение между полноэкранными режимами FullScreen и режимами FullScreen в стиле Lion.
- Прослушивает
KeyboardFocusManager
для отображения иерархии для текущего сфокусированного компонента - Использует как входные карты, так и
KeyListener
, чтобы попытаться захватить ввод
Также было сделано несколько копаний с коллегами, чтобы попытаться изолировать проблемы:
На одном фронте мы попытались переопределить некоторые методы в RT.jar, чтобы увидеть, были ли проблемы с выбором экранного устройства. Также были указаны точки входа в функциональность Toolkit.beep(), чтобы увидеть, звучат ли звуковые сигналы со стороны Java - не отображается.
На другом фронте было ясно, что даже на родной стороне не принимаются события клавиатуры. Сотрудник связывает это с переключателем от AWTView
до NSWindow
в 7u6.
Был найден выбор существующих ошибок Oracle, которые вы можете найти здесь:
- 8000276: [macosx] graphicsDevice.setFullScreenWindow(фрейм) разбивает JVM
- 8000430: [macosx] java.awt.FileDialog проблемы на macosx
- 7175707: [macosx] PIT: 8 b43 Не работает снова в потоке AppKit
Обновление 10/26:
Благодаря комментарию от @maslovalex ниже относительно Applet, работающего на 7u5, я вернулся и тщательно изучил совместимость с версиями JDK для OSX:
- 10.7.1 с 7u4: Полноэкранные работы!
- 10.7.1 с 7u5: Работает в полноэкранном режиме!
- 10.7.5 с 7u5: Полноэкранные работы!
- 10.7.5 с 7u6: Полноэкранные перерывы: (
В сочетании с другими тестами, отмеченными в других местах, ясно, что проблема была введена с 7u6, которая остается в 7u7 и 7u9, и она затрагивает как Lion 10.7, так и Mountain Lion 10.8.
7u6 стал основным релизом релиза, полностью поддерживающим JRE и JDK для Mac OS X, а также включая Java FX как часть дистрибутива. Дополнительная информация доступна в Примечания к выпуску и Дорожная карта. Не удивительно, что такая проблема может возникнуть в связи с переносом поддержки на Java FX.
Вопрос будет:
- Будет ли Oracle исправлять это в ближайшей версии JDK? (Если у вас есть ссылки на существующие ошибки, укажите их здесь.)
- Возможно ли временное решение?
Другие обновления с сегодняшнего дня:
-
Я включил подход к расширению Apple в полноэкранный режим в качестве альтернативного пути исследования (обновленный пример кода, ожидающий очистки). Хорошие новости: входные работы! Плохая новость: действительно, похоже, нет вариантов киоскинга/изоляции.
Я пытался убить Dock - напрямую или с App- Насколько я понимаю, Dock отвечает за переключение приложений Command-Tab, Mission Control и Launch Pad, только чтобы узнать, что он отвечает за обработку полноэкранных приложений! Таким образом, вызовы Java становятся нефункциональными и никогда не возвращаются.
Если есть способ отключить Command-Tab (и управление миссией, Launchpad и Spaces), не влияя на полноэкранную обработку Dock, это было бы чрезвычайно полезно. В качестве альтернативы можно попытаться переназначить определенные ключи, такие как Command, но это повлияет на возможность использования этого модификатора в другом месте программы и самой системы (не совсем идеально, когда вам нужно Command-C для копирования некоторого текста). -
Мне не повезло с KeyListeners (я не получаю никаких обратных вызовов), но у меня есть еще несколько вариантов, чтобы попробовать.
-
Основываясь на предложении коллеги, я пробовал
((sun.lwawt.macosx.LWCToolkit)Toolkit.getDefaultToolkit()).isApplicationActive()
через отражение. Идея заключалась в том, что она:
является нативным методом с комментарием "Возвращает true, если приложение (одно из его окон) владеет фокусом клавиатуры". Звонки на этот метод были добавлены в CPlatformWindow.java в последние несколько месяцев, связанные с логикой фокуса. Если он возвращает false в вашем тестовом коде, вероятно, это часть проблемы.
К сожалению, везде я проверил его, метод вернул true. Так что даже в соответствии с системой низкого уровня мои окна должны иметь фокус клавиатуры. -
Мой предыдущий оптимизм относительно исправления JAlbum был разбит. Разработчик разместил ответ на своем форуме, в котором объясняется, как они просто удалили правильную полноэкранную поддержку в OS X во время работы Java 7. У них есть ошибка в Oracle (и я надеюсь получить номер ошибки).
Обновление 10/25:
Я также попробовал Java 7u9 на Lion 10.7.4 и увидел ту же самую проблему, поэтому JDK - не специфичный для ОС.
Основной вопрос заключается в том, можно ли встраивать в полноэкранное ядро Swing Components, которые имеют обработку по умолчанию для ввода с клавиатуры (JTextField/JTextArea
или даже редактируемые комбинированные поля) и ожидать, что они будут вести себя нормально (без необходимости прибегать к восстановлению своих базовые привязки клавиш вручную). Также стоит вопрос о том, должны ли работать другие компоновки оконных макетов, например, использовать вкладку для обхода фокуса.
Идеальной целью было бы иметь возможность использовать оконное приложение Swing со всеми его кнопками, вкладками, полями и т.д. и запускать его в полноэкранном режиме эксклюзивного/киоска с большей функциональностью. (Раньше я видел, что всплывающие окна Dialog или выпадающие списки ComboBox не работают в полноэкранном режиме на Java 6 на OS X, но другие компоненты ведут себя отлично.)
Я буду изучать возможности FullScreen eawt, которые будут интересны, если они поддерживают параметры блокировки киоска, такие как устранение переключения приложений Command-Tab.
Оригинальный вопрос:
У меня есть приложение Swing, которое в течение многих лет поддерживало FullScreen (эксклюзивный) режим в Mac OS X через Java 6. Я проводили тестирование совместимости с последним выпуском Mountain Lion (10.8.2 Supplemental) и Oracle JDK 7 и заметили вопиющую проблему в этом режиме: движение мыши и щелчки работают нормально, но ввод с клавиатуры не доставляется компонентам.
(Я сузил это ниже в тестовом примере ниже, чтобы не входить в простой JTextField в полноэкранном режиме.)
Один из симптомов заключается в том, что каждое нажатие клавиши приводит к системному звуку, как если бы ОС не разрешала клавиатурные события доставляться в приложение.
Отдельно мое приложение имеет установленный крюк выхода, а комманда Command-Q запускает этот крючок - он очищает, что ОС прослушивает стандартные комманды клавиш.
Я тестировал это отдельно на трех разных компьютерах Mac с различными установками:
- В Apple Java 6u35 и 6u37: как в оконном, так и в полноэкранном режимах принимаются входные данные.
- В Oracle Java 7u7 и 7u9: оконный режим работает так, как ожидалось, в то время как полноэкранный режим имеет симптомы выше.
Об этом, возможно, сообщалось ранее: Графический режим Java Graphics Full Screen не регистрирует ввод клавиатуры. Однако этот вопрос не является специфическим для версии Java или платформы.
В дополнительном поиске появилась отдельная полноэкранная опция, представленная в Lion: Полноэкранный режим для Java-приложений на OSX Lion, Мне еще нужно попытаться использовать этот подход, поскольку ввод с клавиатуры кажется неотъемлемой частью целевых применений полноэкранного режима FullScreen, например игр.
В JavaDoc есть упоминание о том, что методы ввода могут быть отключены. Я попытался вызвать предложенный Component.enableInputMethods(false)
, но, казалось, не имеет эффекта.
Я несколько оптимистично, что решение этой проблемы основано на записи в примечаниях к выпуску приложения Java, с которым я столкнулся (JAlbum). A заявлено исправление для 10.10.6: "Поддержка клавиатуры не работала при запуске полноэкранного слайд-шоу на Mac и Java 7"
Мой тестовый пример ниже. Он слегка изменен из второго примера в этом выпуске (который не изменен, также показывает мою проблему): Как обрабатывать события с клавиатуры и мыши в полноэкранном эксклюзивном режиме в java? В частности, он добавляет кнопку для переключения полноэкранного режима.
import java.lang.reflect.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.beans.*;
/** @see https://stackoverflow.com/info/13064607/ */
public class FullScreenTest extends JPanel {
private GraphicsDevice dev = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
private JFrame f = new JFrame("FullScreenTest");
private static final String EXIT = "Exit";
private Action exit = new AbstractAction(EXIT) {
@Override
public void actionPerformed(ActionEvent e) {
Object o = dev.getFullScreenWindow();
if(o != null) {
dev.setFullScreenWindow(null);
}
f.dispatchEvent(new WindowEvent(f, WindowEvent.WINDOW_CLOSING));
}
};
private JButton exitBTN = new JButton(exit);
private JTextField jtf = new JTextField("Uneditable in FullScreen with Java7u6+ on Mac OS X 10.7.3+");
private JLabel keystrokeLabel = new JLabel("(Last Modifier+Key Pressed in JTextField)");
private JLabel jtfFocusLabel = new JLabel("(JTextField Focus State)");
private JLabel focusLabel = new JLabel("(Focused Component Hierarchy)");
private JCheckBox useOSXFullScreenCB = new JCheckBox("Use Lion-Style FullScreen Mode");
private JCheckBox useWorkaroundCB = new JCheckBox("Use Visibility Workaround to Restore 1st Responder Window");
private static final String TOGGLE = "Toggle FullScreen (Command-T or Enter)";
private Action toggle = new AbstractAction(TOGGLE) {
@Override
public void actionPerformed(ActionEvent e) {
Object o = dev.getFullScreenWindow();
if(o == null) {
f.pack();
/**
* !! Neither of these calls seem to have any later effect.
* One exception: I have a report of a
* Mini going into an unrecoverable black screen without setVisible(true);
* May be only a Java 6 compatibility issue. !!
*/
//f.setVisible(true);
//f.setVisible(false);
if(!useOSXFullScreenCB.isSelected()) {
// No keyboard input after this call unless workaround is used
dev.setFullScreenWindow(f);
/**
* Workaround provided by Leonid Romanov at Oracle.
*/
if(useWorkaroundCB.isSelected()) {
f.setVisible(false);
f.setVisible(true);
//Not necessary to invoke later...
/*SwingUtilities.invokeLater(new Runnable() {
public void run() {
f.setVisible(false);
f.setVisible(true);
}
});*/
}
}
else {
toggleOSXFullscreen(f);
}
}
else {
dev.setFullScreenWindow(null);
f.pack();
f.setVisible(true);
}
isAppActive();
}
};
private JButton toggleBTN = new JButton(toggle);
public FullScreenTest() {
// -- Layout --
this.setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
exitBTN.setAlignmentX(JComponent.CENTER_ALIGNMENT);
exitBTN.setMaximumSize(new Dimension(Short.MAX_VALUE, 50));
this.add(exitBTN);
jtf.setAlignmentX(JComponent.CENTER_ALIGNMENT);
jtf.setMaximumSize(new Dimension(Short.MAX_VALUE, Short.MAX_VALUE));
this.add(jtf);
keystrokeLabel.setAlignmentX(JComponent.CENTER_ALIGNMENT);
keystrokeLabel.setMaximumSize(new Dimension(Short.MAX_VALUE, 50));
keystrokeLabel.setHorizontalAlignment(SwingConstants.CENTER);
keystrokeLabel.setForeground(Color.DARK_GRAY);
this.add(keystrokeLabel);
jtfFocusLabel.setAlignmentX(JComponent.CENTER_ALIGNMENT);
jtfFocusLabel.setMaximumSize(new Dimension(Short.MAX_VALUE, 50));
jtfFocusLabel.setHorizontalAlignment(SwingConstants.CENTER);
jtfFocusLabel.setForeground(Color.DARK_GRAY);
this.add(jtfFocusLabel);
focusLabel.setAlignmentX(JComponent.CENTER_ALIGNMENT);
focusLabel.setMaximumSize(new Dimension(Short.MAX_VALUE, 50));
focusLabel.setHorizontalAlignment(SwingConstants.CENTER);
focusLabel.setForeground(Color.DARK_GRAY);
this.add(focusLabel);
useOSXFullScreenCB.setAlignmentX(JComponent.CENTER_ALIGNMENT);
useOSXFullScreenCB.setMaximumSize(new Dimension(Short.MAX_VALUE, 50));
useOSXFullScreenCB.setHorizontalAlignment(SwingConstants.CENTER);
this.add(useOSXFullScreenCB);
useWorkaroundCB.setAlignmentX(JComponent.CENTER_ALIGNMENT);
useWorkaroundCB.setMaximumSize(new Dimension(Short.MAX_VALUE, 50));
useWorkaroundCB.setHorizontalAlignment(SwingConstants.CENTER);
this.add(useWorkaroundCB);
toggleBTN.setAlignmentX(JComponent.CENTER_ALIGNMENT);
toggleBTN.setMaximumSize(new Dimension(Short.MAX_VALUE, 50));
this.add(toggleBTN);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setResizable(false);
f.setUndecorated(true);
f.add(this);
f.pack();
enableOSXFullscreen(f);
// -- Listeners --
// Default BTN set to see how input maps respond in fullscreen
f.getRootPane().setDefaultButton(toggleBTN);
// Explicit input map test with Command-T toggle action from anywhere in the window
this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
KeyStroke.getKeyStroke(KeyEvent.VK_T, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
toggle.getValue(Action.NAME));
this.getActionMap().put(toggle.getValue(Action.NAME), toggle);
// KeyListener test
jtf.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
String ktext = "KeyPressed: "+e.getKeyModifiersText(e.getModifiers()) + "_"+ e.getKeyText(e.getKeyCode());
keystrokeLabel.setText(ktext);
System.out.println(ktext);
}
});
// FocusListener test
jtf.addFocusListener(new FocusListener() {
public void focusGained(FocusEvent fe) {
focused(fe);
}
public void focusLost(FocusEvent fe) {
focused(fe);
}
private void focused(FocusEvent fe) {
boolean allGood = jtf.hasFocus() && jtf.isEditable() && jtf.isEnabled();
jtfFocusLabel.setText("JTextField has focus (and is enabled/editable): " + allGood);
isAppActive();
}
});
// Keyboard Focus Manager
KeyboardFocusManager focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
focusManager.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
if (!("focusOwner".equals(e.getPropertyName()))) return;
Component comp = (Component)e.getNewValue();
if(comp == null) {
focusLabel.setText("(No Component Focused)");
return;
}
String label = comp.getClass().getName();
while(true) {
comp = comp.getParent();
if(comp == null) break;
label = comp.getClass().getSimpleName() + " -> " + label;
}
focusLabel.setText("Focus Hierarchy: " + label);
isAppActive();
}
});
}
/**
* Hint that this Window can enter fullscreen. Only need to call this once per Window.
* @param window
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static void enableOSXFullscreen(Window window) {
try {
Class util = Class.forName("com.apple.eawt.FullScreenUtilities");
Class params[] = new Class[]{Window.class, Boolean.TYPE};
Method method = util.getMethod("setWindowCanFullScreen", params);
method.invoke(util, window, true);
} catch (ClassNotFoundException e1) {
} catch (Exception e) {
System.out.println("Failed to enable Mac Fullscreen: "+e);
}
}
/**
* Toggle OSX fullscreen Window state. Must call enableOSXFullscreen first.
* Reflection version of: com.apple.eawt.Application.getApplication().requestToggleFullScreen(f);
* @param window
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static void toggleOSXFullscreen(Window window) {
try {
Class appClass = Class.forName("com.apple.eawt.Application");
Method method = appClass.getMethod("getApplication");
Object appInstance = method.invoke(appClass);
Class params[] = new Class[]{Window.class};
method = appClass.getMethod("requestToggleFullScreen", params);
method.invoke(appInstance, window);
} catch (ClassNotFoundException e1) {
} catch (Exception e) {
System.out.println("Failed to toggle Mac Fullscreen: "+e);
}
}
/**
* Quick check of the low-level window focus state based on Apple Javadoc:
* "Returns true if the application (one of its windows) owns keyboard focus."
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static void isAppActive() {
try {
Class util = Class.forName("sun.lwawt.macosx.LWCToolkit");
Method method = util.getMethod("isApplicationActive");
Object obj = method.invoke(Toolkit.getDefaultToolkit());
System.out.println("AppActive: "+obj);
} catch (ClassNotFoundException e1) {
} catch (Exception e) {
System.out.println("Failed to check App: "+e);
}
}
public static void main(String[] args) {
System.out.println("Java Version: " + System.getProperty("java.version"));
System.out.println("OS Version: " + System.getProperty("os.version"));
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
FullScreenTest fst = new FullScreenTest();
if(!fst.dev.isFullScreenSupported()) {
System.out.println("FullScreen not supported on this graphics device. Exiting.");
System.exit(0);
}
fst.toggle.actionPerformed(null);
}
});
}
}