Komponenty Swing FullScreen nie otrzymują wejścia klawiatury w Javie 7 na Mac OS X Mountain Lion

Aktualizacja 12/21:

7u10 został niedawno wydany. Potwierdził, że:

Problem nadal występujeNa szczęście obejście nadal działa!

Aktualizacja 11/7:

I mamy obejście!

Leonid Romanov z Oracle na liście mailingowej openjdk.java.net zapewnił pewne informacje na temat tego, co się dzieje:

Cóż, chociaż nie jestem jeszcze w 100% pewien, ale wygląda na to, że gdy wchodzimy na pełny ekran, pierwsze okno odpowiada drugiemu, stąd sygnał dźwiękowy. Czy mógłbyś wypróbować następujące obejście: po wywołaniu setFullScreenWindow () na ramce wywołaj setVisible (false), a następnie setVisible (true). To, teoretycznie, powinno przywrócić prawidłową pierwszą odpowiedź.

Fragment kodu, który wydaje się działać, jest po prostu:

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

Zaktualizowałem przykładowy kod z możliwością włączania i wyłączania tej poprawki; jest wymagane za każdym razem, gdy okno wchodzi w tryb pełnoekranowy.

W szerszym kontekście mojej bardziej złożonej aplikacji nadal mam problemy z koncentracją klawiatury na podskładnikach w oknie pełnoekranowym, gdzie kliknięcie myszą powoduje utratę ostrości przez moje okno. (Zgaduję, że trafi do niepożądanego okna pierwszego odpowiadającego, o którym mowa powyżej.) Odezwę się, gdy będę miał więcej informacji o tej sprawie - nie mogę jeszcze odtworzyć go w mniejszej próbce.

Aktualizacja 10/31:

Główna aktualizacja przykładowego kodu:

Obejmuje przełączanie między trybami FullScreen i trybami FullScreen w stylu LionSłuchaKeyboardFocusManager aby wyświetlić hierarchię dla aktualnie skupionego komponentuUżywa obu map wejściowych iKeyListeners, aby spróbować przechwycić dane wejściowe

Przeprowadziłem też kilka dodatkowych poszukiwań ze współpracownikami, aby spróbować wyizolować problemy:

Z jednej strony próbowaliśmy przesłonić niektóre metody w RT.jar, aby sprawdzić, czy wystąpiły problemy ze sposobem wyboru urządzenia ekranowego. Próbowano również, aby punkty wejścia do funkcji Toolkit.beep () sprawdzały, czy dźwięki alertów pochodzą ze strony Java - wydaje się, że nie.

Na innym froncie było jasne, że nawet strona macierzysta nie odbiera wydarzeń z klawiatury. Współpracownik przypisuje to do przełącznika zAWTView do aNSWindow w 7u6.

Znaleziono kilka istniejących błędów Oracle, które możesz sprawdzićtutaj:

8000276: [macosx] graphicsDevice.setFullScreenWindow (ramka) zawiesza JVM8000430: [macosx] problemy java.awt.FileDialog na macosx7175707: [macosx] PIT: 8 b43 Nie działa ponownie w wątku AppKit

Aktualizacja 10/26:

Dzięki komentarzowi @maslovalex poniżej dotyczącym apletu działającego na 7u5, wróciłem i starannie przeanalizowałem zgodność z wersjami JDK dla OSX:

10.7.1 z 7u4: Fullscreen Works!10.7.1 z 7u5: Fullscreen Works!10.7.5 z 7u5: Fullscreen Works!10.7.5 z 7u6: przerwy na pełnym ekranie :(

W połączeniu z innymi testami odnotowanymi gdzie indziej, jest oczywiste, że wprowadzono problem z 7u6, który pozostaje w 7u7 i 7u9, i dotyczy zarówno Lion 10.7, jak i Mountain Lion 10.8.

7u6 było ważnym krokiem milowym, przynoszącym pełną obsługę JRE i JDK do Mac OS X, a także Java FX jako część dystrybucji. Dalsze informacje są dostępne wInformacje o wydaniu iMapa drogowa. Nie jest zaskakujące, że taki problem może pojawić się, gdy wsparcie przejdzie na Java FX.

Pytanie brzmi:

Czy Oracle naprawi to w najbliższym wydaniu JDK? (Jeśli masz linki do istniejących błędów, dołącz je tutaj.)Czy w międzyczasie możliwe jest obejście problemu?

Inne aktualizacje od dzisiaj:

WłączyłemRozszerzenia Apple zbliżają się do trybu pełnoekranowego jako alternatywna ścieżka eksploracji (zaktualizowany kod próbki do czasu oczyszczenia). Dobra wiadomość: działa wejście! Zła wiadomość: naprawdę nie ma żadnych opcji kiosku / izolacji.
Próbowałem zabić Docka -bezpośrednio lub zApp - jak rozumiem, Dock jest odpowiedzialny za przełączanie aplikacji Command-Tab, sterowanie misją i Launch Pad, tylko po to, aby dowiedzieć się, że jest odpowiedzialny również za obsługę aplikacji pełnoekranowych! W związku z tym wywołania Java stają się niefunkcjonalne i nigdy nie powracają.
Jeśli istnieje sposóbwyłącz Command-Tab (i Kontrola misji i Launchpad oraz Spaces) bez wpływu na obsługę pełnoekranową Docka, co byłoby niezwykle przydatne. Alternatywnie można spróbować przemapować niektóre klucze, takie jak Command, ale to wpłynie na możliwość użycia tego modyfikatora w innym miejscu programu i samego systemu (nie jest to idealne rozwiązanie, gdy trzeba skopiować Command-C, aby skopiować jakiś tekst).

Nie miałem szczęścia z KeyListeners (nie otrzymuję żadnych wywołań zwrotnych), ale mam jeszcze kilka opcji do wypróbowania.

Na podstawie sugestii współpracownika próbowałem((sun.lwawt.macosx.LWCToolkit)Toolkit.getDefaultToolkit()).isApplicationActive() poprzez refleksję. Chodziło o to, że:
jest rodzimą metodą z komentarzem „Zwraca true, jeśli aplikacja (jedno z jej okien) posiada fokus klawiatury.”. Połączenia z tą metodą zostały dodane w CPlatformWindow.java w ciągu ostatnich kilku miesięcy związane z logiką fokusa. Jeśli w kodzie testowym zwróci wartość false, prawdopodobnie jest to część problemu.
Niestety, wszędzie tam, gdzie to sprawdziłem, metoda zwróciła prawdę. Więc nawet zgodnie z systemem niskiego poziomu moje okna powinny mieć skupienie na klawiaturze.

Mój poprzedni optymizm dotyczący poprawki JAlbum został przerywany. Deweloperopublikował odpowiedź na swoim forum wyjaśnia to, w jaki sposób po prostu usunęli poprawną obsługę pełnoekranową na OS X podczas uruchamiania Java 7. Mają błąd w Oracle (i mam nadzieję uzyskać numer błędu).

Aktualizacja 10/25:

Teraz wypróbowałem także Java 7u9 na Lion 10.7.4 i widziałem dokładnie ten sam problem, więc jest to JDK, a nie specyficzny dla systemu operacyjnego.

Podstawowym pytaniem stało się, czy można osadzić w pełnoekranowym rdzeniu okna Komponenty Swingowe, które mają domyślną obsługę wejścia klawiatury (JTextField/JTextArea lub nawet edytowalne pola kombi) i oczekują, że będą zachowywać się normalnie (bez konieczności ręcznego przebudowywania podstawowych powiązań klawiszy). Chodzi także o to, czy powinny działać inne mocne strony układów okienkowych, takie jak użycie karty do przechodzenia fokusa.

Idealnym celem byłoby posiadanie umiejętności korzystania z okienkowej aplikacji Swing ze wszystkimi jej przyciskami, kartami, polami itp. I uruchamianie jej w trybie pełnoekranowym z wyłącznym trybem / kioskiem z największą funkcjonalnością. (Wcześniej widziałem, że wyskakujące okienka w oknie dialogowym lub listy rozwijane ComboBox nie działają w pełnym ekranie w Javie 6 na OS X, ale inne komponenty zachowują się dobrze).

Będę przyglądał się możliwościom pełnych ekranów FullScreen, które będą interesujące, jeśli będą wspierać opcje blokowania kiosku, takie jak eliminacja przełączania aplikacji Command-Tab.

Pytanie oryginalne:

Mam aplikację Swing, która od lat wspieraFullScreen (wyłącznie) tryb na Mac OS X w Javie 6. Testowałem kompatybilność z najnowszą wersją Mountain Lion (10.8.2 Supplemental) i Oracle JDK 7 i zauważyłem rażący problem w tym trybie: ruch myszy i kliknięcia działają dobrze, ale wejście klawiatury nie jest dostarczane do komponentów.

(Zawęziłem to w poniższym przypadku testowym, aby nie móc wpisywać w prostym polu JTextField w trybie pełnoekranowym).

Jednym z symptomów jest to, że każde naciśnięcie klawisza powoduje sygnał dźwiękowy systemu, tak jakby system operacyjny nie zezwalał na dostarczanie zdarzeń klawiatury do aplikacji.

Oddzielnie moja aplikacja ma zainstalowany hak wyjścia, a kombi Command-Q uruchomi ten hak - jasne jest, że system operacyjny nasłuchuje standardowych kombinacji klawiszy.

Przetestowałem to osobno na trzech różnych komputerach Mac z różnymi instalacjami:

W Apple Java 6u35 i 6u37: zarówno tryby okienkowe, jak i pełnoekranowe otrzymują dane wejściowe.W Oracle Java 7u7 i 7u9: tryb okienkowy działa zgodnie z oczekiwaniami, podczas gdy pełny ekran ma powyższe symptomy.

Może to być wcześniej zgłoszone:Tryb pełnoekranowy grafiki Java nie rejestruje wejścia klawiatury. Pytanie to nie dotyczy jednak wersji lub platformy Java.

Dodatkowe wyszukiwanie sprawiło, że w Lionie pojawiła się osobna opcja pełnego ekranu:Funkcja pełnoekranowa dla aplikacji Java na OSX Lion. Do tej pory nie próbowałem użyć tego podejścia, ponieważ wprowadzanie z klawiatury wydaje się integralną częścią docelowych zastosowań trybu wyłącznego FullScreen, takich jak gry.

W JavaDoc jest wzmianka o tym trybie, że metody wprowadzania mogą być wyłączone. Próbowałem zadzwonić do sugerowanegoComponent.enableInputMethods(false), ale wydawało sięnie działają.

Jestem nieco optymistyczny, że istnieje rozwiązanie tego problemu na podstawie wpisu w uwagach do wydania aplikacji Java, którą natknąłem się (JAlbum). ZAustalona poprawka do 10.10.6: „Obsługa klawiatury nie działała podczas uruchamiania pełnoekranowego pokazu slajdów na komputerach Mac i Java 7”

Mój przypadek testowy jest poniżej. Został on lekko zmodyfikowany w porównaniu z drugim przykładem w tym numerze (który, niezmodyfikowany, pokazuje również mój problem):Jak obsługiwać zdarzenia z klawiatury i myszy w trybie pełnoekranowym na wyłączność w Javie? W szczególności dodaje przycisk do przełączania na pełny ekran.

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);
            }
        });
    }
}

questionAnswers(3)

yourAnswerToTheQuestion