FullScreen Swing-Komponenten empfangen keine Tastatureingaben unter Java 7 unter Mac OS X Mountain Lion

Update 12/21:

7u10 wurde kürzlich veröffentlicht. Bestätigte, dass:

Das Problem besteht weiterhinZum Glück funktioniert die Problemumgehung immer noch!

Update 11/7:

Und wir haben einen Workaround!

Leonid Romanov von Oracle auf der Mailingliste openjdk.java.net gab einen Einblick in das Geschehen:

Nun, ich bin mir zwar noch nicht zu 100% sicher, aber es sieht so aus, als ob beim Betreten des Vollbildmodus ein anderes Fenster der erste Antwortgeber wird, daher der Piepton. Versuchen Sie bitte die folgende Problemumgehung: Rufen Sie nach dem Aufrufen von setFullScreenWindow () für einen Frame setVisible (false) gefolgt von setVisible (true) auf. Dies sollte theoretisch den richtigen Ersthelfer wiederherstellen.

Das Codefragment, das zu funktionieren scheint, ist einfach:

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

Ich habe den Beispielcode mit der Möglichkeit aktualisiert, diesen Fix ein- und auszuschalten. Dies ist jedes Mal erforderlich, wenn ein Fenster in den Vollbildmodus wechselt.

Im größeren Kontext meiner komplexeren Anwendung treten immer noch Probleme mit dem Tastaturfokus auf Unterkomponenten im Vollbildfenster auf, bei denen ein Mausklick dazu führt, dass mein Fenster den Fokus verliert. (Ich vermute, es wird zu dem oben genannten unerwünschten Ersthelferfenster weitergeleitet.) Ich melde mich zurück, wenn ich weitere Informationen zu diesem Fall habe - ich kann es in der kleineren Stichprobe noch nicht reproduzieren.

Update 10/31:

Hauptaktualisierung des Beispielcodes:

Enthält das Umschalten zwischen dem exklusiven Vollbildmodus und dem Vollbildmodus im Lion-StilHört gerneKeyboardFocusManager um die Hierarchie für die aktuell fokussierte Komponente anzuzeigenVerwendet die beiden Eingabe-Maps undKeyListeners, um zu versuchen, Eingaben zu erfassen

Wir haben auch noch mehr mit Kollegen zusammengegraben, um Probleme zu isolieren:

Auf der einen Seite haben wir versucht, einige Methoden in RT.jar zu überschreiben, um festzustellen, ob Probleme mit der Auswahl des Bildschirmgeräts aufgetreten sind. Es wurde auch versucht, die Einstiegspunkte für die Toolkit.beep () -Funktionalität zu ermitteln, um festzustellen, ob die Warntöne von der Java-Seite stammen - wird nicht angezeigt.

An einer anderen Front war klar, dass nicht einmal die native Seite Tastaturereignisse empfängt. Ein Mitarbeiter schreibt dies einem Wechsel von einem zuAWTView zu einemNSWindow in 7u6.

Es wurde eine Auswahl vorhandener Oracle-Fehler gefunden, die Sie nachschlagen könnenHier:

8000276: [macosx] graphicsDevice.setFullScreenWindow (frame) stürzt JVM ab8000430: [macosx] Probleme mit java.awt.FileDialog unter macosx7175707: [macosx] PIT: 8 b43 Wird bei einem AppKit-Thread-Problem nicht mehr ausgeführt

Update 26.10 .:

Dank des Kommentars von @maslovalex zu einem Applet auf 7u5 ging ich zurück und überprüfte sorgfältig die Kompatibilität mit den JDK-Versionen für OSX:

10.7.1 mit 7u4: Vollbild funktioniert!10.7.1 mit 7u5: Vollbild funktioniert!10.7.5 mit 7u5: Vollbild funktioniert!10.7.5 mit 7u6: Fullscreen Breaks :(

In Kombination mit den anderen Tests, die an anderer Stelle erwähnt wurden, ist es klar, dass mit 7u6 ein Problem eingeführt wurde, das in 7u7 und 7u9 verbleibt und sowohl Lion 10.7 als auch Mountain Lion 10.8 betrifft.

7u6 war ein wichtiger Meilenstein, der die vollständige Unterstützung von JRE und JDK für Mac OS X brachte und Java FX als Teil der Distribution umfasste. Weitere Informationen finden Sie in derVersionshinweise und dasFahrplan. Es ist nicht verwunderlich, dass ein solches Problem auftreten kann, wenn die Unterstützung auf Java FX umgestellt wird.

Die Frage wird:

Behebt Oracle das Problem in einem kurzfristigen Release des JDK? (Falls Sie Links zu vorhandenen Bugs haben, fügen Sie diese bitte hier ein.)Ist ein Workaround in der Zwischenzeit möglich?

Weitere Updates von heute:

Ich habe dieApple-Erweiterungen nähern sich dem Vollbildmodus als alternativer Erkundungspfad (aktualisierter Beispielcode bis zur Bereinigung). Die gute Nachricht: Eingabe funktioniert! Die schlechte Nachricht: Es scheint wirklich keine Kiosk- / Isolationsoptionen zu geben.
Ich habe versucht, das Dock zu töten -direkt oder mit einemApp - Soweit ich weiß, ist das Dock für das Wechseln der Command-Tab-App, die Missionssteuerung und das Launch Pad verantwortlich, nur um herauszufinden, dass es auch für den Umgang mit Vollbild-Apps verantwortlich ist! Daher werden die Java-Aufrufe nicht mehr funktionsfähig und kehren nie mehr zurück.
Wenn es einen Weg gibtdeaktiviere Command-Tab (und Mission Control und Launchpad und Spaces), ohne die Handhabung des Docks im Vollbildmodus zu beeinträchtigen, wäre dies äußerst nützlich. Alternativ kann man versuchen, bestimmte Tasten wie Command neu zuzuordnen, was sich jedoch auf die Möglichkeit auswirkt, diesen Modifikator an einer anderen Stelle im Programm und im System selbst zu verwenden (nicht gerade ideal, wenn Sie zum Kopieren von Text Command-C benötigen).

Ich hatte kein Glück mit KeyListeners (ich erhalte keine Rückrufe), aber ich habe noch ein paar Optionen zum Ausprobieren.

Auf Vorschlag eines Mitarbeiters habe ich es versucht((sun.lwawt.macosx.LWCToolkit)Toolkit.getDefaultToolkit()).isApplicationActive() über Reflexion. Die Idee war, dass es:
ist eine native Methode mit dem Kommentar "Gibt true zurück, wenn die Anwendung (eines ihrer Fenster) den Tastaturfokus besitzt." Aufrufe dieser Methode wurden in den letzten Monaten in CPlatformWindow.java im Zusammenhang mit der Fokuslogik hinzugefügt. Wenn es in Ihrem Testcode false zurückgibt, ist es wahrscheinlich ein Teil des Problems.
Leider hat die Methode überall, wo ich sie überprüft habe, true zurückgegeben. Selbst nach dem Low-Level-System sollten meine Fenster einen Tastaturfokus haben.

Mein früherer Optimismus in Bezug auf die JAlbum-Korrektur wurde zunichte gemacht. Der Entwicklerhat eine Antwort in ihrem Forum gepostet Das erklärt, wie sie einfach die richtige Vollbild-Unterstützung unter OS X entfernt haben, während Java 7 ausgeführt wurde. Sie haben einen Fehler in Oracle (und ich hoffe, die Fehlernummer zu bekommen).

Update 25.10 .:

Ich habe jetzt auch Java 7u9 auf Lion 10.7.4 ausprobiert und habe genau dasselbe Problem gesehen, es ist also JDK- nicht OS-spezifisch.

Die Kernfrage ist, ob Sie in ein Vollbildfenster Kern-Swing-Komponenten einbetten können, die die Standardbehandlung für Tastatureingaben haben (JTextField/JTextArea oder sogar bearbeitbare Kombinationsfelder) und erwarten, dass sie sich normal verhalten (ohne dass die grundlegenden Tastenkombinationen manuell neu erstellt werden müssen). Es ist auch fraglich, ob andere Stufen von fenstergesteuerten Layouts, beispielsweise die Verwendung von Registerkarten für die Fokusüberquerung, funktionieren sollten.

Das ideale Ziel wäre, die Möglichkeit zu haben, eine Windows-Swing-App mit all ihren Schaltflächen, Registerkarten, Feldern usw. zu verwenden und sie im exklusiven Vollbild- / Kiosk-Modus mit den meisten intakten Funktionen auszuführen. (Bisher habe ich gesehen, dass Dialog-Popups oder Dropdowns in der ComboBox unter Java 6 unter OS X nicht im Vollbildmodus funktionieren, andere Komponenten jedoch einwandfrei funktionieren.)

Ich werde mich mit den einzelnen FullScreen-Funktionen befassen, die interessant sein werden, wenn sie Kiosksperroptionen unterstützen, z. B. die Beseitigung des Befehls-Tab-Anwendungswechsels.

Ursprüngliche Frage:

Ich habe eine Swing-App, die seit Jahren unterstütztFullScreen (exklusiv) -Modus unter Mac OS X bis Java 6. Ich habe Kompatibilitätstests mit der neuesten Mountain Lion-Version (10.8.2 Supplemental) und Oracle JDK 7 durchgeführt und dabei ein eklatantes Problem festgestellt: Mausbewegungen und Klicks funktionieren einwandfrei. Tastatureingaben werden jedoch nicht an die Komponenten gesendet.

(Ich habe dies in einem Testfall weiter unten eingegrenzt, damit ich im Vollbildmodus nicht in ein einfaches JTextField tippen kann.)

Ein Symptom ist, dass bei jedem Tastendruck ein Signalton ausgegeben wird, als ob das Betriebssystem nicht zulässt, dass Tastaturereignisse an die Anwendung gesendet werden.

Unabhängig davon ist in meiner Anwendung ein Exit-Hook installiert, und die Command-Q-Kombination löst diesen Hook aus. Es ist klar, dass das Betriebssystem Standardtastenkombinationen abhört.

Ich habe dies separat auf drei verschiedenen Macs mit verschiedenen Installationen getestet:

Auf Apple Java 6u35 und 6u37: Sowohl der Fenstermodus als auch der Vollbildmodus empfangen Eingaben.Unter Oracle Java 7u7 und 7u9: Der Fenstermodus funktioniert wie erwartet, während der Vollbildmodus die oben genannten Symptome aufweist.

Dies wurde möglicherweise zuvor gemeldet:Java-Grafik-Vollbildmodus registriert keine Tastatureingaben. Diese Frage ist jedoch nicht spezifisch für die Java-Version oder -Plattform.

Bei der zusätzlichen Suche wurde eine separate Vollbildoption in Lion eingeführt:Vollbildfunktion für Java-Apps unter OSX Lion. Ich habe diesen Ansatz noch nicht ausprobiert, da die Tastatureingabe in den Anwendungsfällen des exklusiven Vollbildmodus (z. B. Spiele) von wesentlicher Bedeutung zu sein scheint.

In JavaDoc wird für diesen Modus erwähnt, dass Eingabemethoden möglicherweise deaktiviert sind. Ich habe versucht, den vorgeschlagenen anzurufenComponent.enableInputMethods(false), aber es schien zuhaben keine Wirkung.

Ich bin etwas optimistisch, dass es eine Lösung für dieses Problem gibt, die auf einem Eintrag in den Versionshinweisen einer Java-App basiert, auf die ich gestoßen bin (JAlbum). EINFix für 10.10.6 angegeben: "Tastaturunterstützung funktionierte nicht, wenn die Vollbild-Diashow auf Mac und Java 7 ausgeführt wurde"

Mein Testfall ist unten. Es wurde gegenüber dem zweiten Beispiel in dieser Ausgabe leicht modifiziert (das, unverändert, auch mein Problem aufweist):Wie werden Ereignisse von Tastatur und Maus im exklusiven Vollbildmodus in Java verarbeitet? Insbesondere wird eine Schaltfläche zum Umschalten des Vollbildmodus hinzugefügt.

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

Antworten auf die Frage(3)

Ihre Antwort auf die Frage