Компоненты FullScreen Swing не могут получить ввод с клавиатуры на Java 7 в Mac OS X Mountain Lion

Обновление 12/21:

7u10 был недавно выпущен. Подтвердили, что:

Проблема все еще сохраняетсяК счастью, обходной путь все еще функционирует!

Обновление 11/7:

И у нас есть обходной путь!

Леонид Романов из Oracle в списке рассылки openjdk.java.net дал некоторое представление о том, что 'происходит:

Ну, хотя яЯ еще не уверен на 100%, но, похоже, когда мы входим в полноэкранный режим, какое-то другое окно становится первым респондентом, следовательно, звуковым сигналом. Не могли бы вы попробовать следующий обходной путь: после вызова setFullScreenWindow () для фрейма вызовите setVisible (false), а затем setVisible (true). Это, по идее, должно восстановить правильного первого респондента.

Фрагмент кода, который, кажется, работает:

dev.setFullScreenWindow(f);
f.setVisible(false);
f.setVisible(true);

Я обновил пример кода с возможностью включать и выключать это исправление; это требуется каждый раз, когда окно входит в полноэкранный режим.

В более широком контексте моего более сложного приложения я все еще сталкиваюсь с проблемами фокусировки клавиатуры на подкомпонентах в полноэкранном окне, где щелчок мыши приводит к тому, что мое окно теряет фокус. (Я'Я угадал этоидет к нежелательному окну первого респондента, указанному выше.)Я сообщу, когда у меня будет больше информации об этом случае - я пока не могу воспроизвести ее в меньшем образце.

Обновление 10/31:

Основное обновление примера кода:

Включает переключение между эксклюзивным режимом FullScreen и режимом FullScreen в стиле LionСлушаетKeyboardFocusManager отобразить иерархию для текущего сфокусированного компонентаИспользует обе входные карты иKeyListeners, чтобы попытаться захватить ввод

Также сделал еще несколько копаний с коллегами, чтобы попытаться изолировать проблемы:

С одной стороны, мы попытались переопределить некоторые методы в RT.jar, чтобы увидеть, были ли проблемы с выбором устройства экрана. Также были опробованы точки входа в функциональность Toolkit.beep (), чтобы увидеть, поступают ли звуки оповещения со стороны Java - не отображается.

С другой стороны, было ясно, что даже нативная сторона не получает события клавиатуры. Коллега приписывает это переключению сAWTView кNSWindow в 7u6.

Найден набор существующих ошибок Oracle, который вы можете посмотретьВот:

8000276: [macosx] graphicsDevice.setFullScreenWindow (frame) завершает работу JVM8000430: [macosx] java.awt.FileDialog выдает в macosx7175707: [macosx] PIT: 8 b43 Не работает снова проблема с потоком AppKit

Обновление 10/26:

Благодаря комментарию @maslovalex ниже относительно Апплета, работающего на 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 отвечает за переключение приложений Command-Tab, Mission Control и Launch Pad, только чтобы выяснить, что это 'также отвечает за работу с полноэкранными приложениями! Таким образом, вызовы Java становятся не функциональными и никогда не возвращаются.

Если там'это способотключить Command-Tab (и управление полетом, и панель запуска, и пробелы), не влияя на док-станциюs полноэкранная обработка, это было бы чрезвычайно полезно. В качестве альтернативы можно попытаться переназначить определенные ключи, такие как Command, но это повлияет на возможность использовать этот модификатор в другом месте программы и самой системы (не совсем идеально, когда вам нужно Command-C для копирования текста) .I '

нам не повезло с 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 и увидел ту же проблему, так чтоs JDK- не зависит от ОС.

Основной вопрос заключается в том, можете ли вы встраивать в полноэкранное окно базовые компоненты Swing, которые по умолчанию обрабатывают ввод с клавиатуры (JTextField/JTextArea или даже редактируемые поля со списком) и ожидайте, что они будут вести себя нормально (не прибегая к перестройке основных привязок клавиш вручную). Также вопрос в том, должны ли работать другие сторонники оконных макетов, такие как использование табуляции для обхода фокуса.

Идеальной целью было бы иметь возможность взять оконное приложение Swing со всеми его кнопками, вкладками, полями и т. Д. И запустить его в полноэкранном монопольном режиме / режиме киоска с сохранением большей части функциональности. (Ранее я видел, что всплывающие окна Dialog или выпадающие списки ComboBox не работают в полноэкранном режиме на Java 6 на OS X, но другие компоненты ведут себя нормально.)

Мы рассмотрим возможности eawt FullScreen, которые будут интересны, если они будут поддерживать опции блокировки киоска, такие как устранение переключения приложений 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 не регистрирует ввод с клавиатуры, Однако этот вопрос не является специфическим для версии или платформы Java.

При дополнительном поиске появилась отдельная полноэкранная опция, представленная в Lion:Полноэкранный режим для приложений Java на OSX Lion, Я еще не попробовал использовать этот подход, поскольку ввод с клавиатуры кажется неотъемлемой частью целевых сценариев использования режима FullScreen Exclusive, таких как игры.

В JavaDoc для этого режима есть упоминание о том, что методы ввода могут быть отключены. Я пытался позвонить предложенномуComponent.enableInputMethods(false), но это казалосьне имеет никакого эффекта.

Я несколько оптимистичен, чтоРешение этой проблемы основано на записи в заметках о выпуске Java-приложения, с которым я столкнулся (JAlbum).заявлено исправление для 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/questions/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's 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);
            }
        });
    }
}

Ответы на вопрос(3)

Ваш ответ на вопрос