Les composants Swing FullScreen ne reçoivent pas les entrées du clavier sur Java 7 sur Mac OS X Mountain Lion

mise à jour 12/21:

7u10 a été publié récemment. Confirmé que:

  1. le problème persiste
  2. heureusement, la solution fonctionne toujours!

mise à Jour 11/7:

Et nous avons une solution!

Leonid Romanov d'Oracle sur le openjdk.java.net la liste de diffusion donne un aperçu de ce qui se passe :

bien, bien que je ne suis pas sûr à 100% encore, mais il semble que lorsque nous entrons plein écran une autre fenêtre devient le premier intervenant, d'où le bip. Pourriez-vous essayer la solution suivante: après avoir appelé setFullScreenWindow() sur une image, appeler setVisible(false) suivi de setVisible(true). Ce qui, en théorie, devrait rétablir les premiers intervenants.

L'extrait de code qui semble fonctionner est tout simplement:

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

j'ai mis à jour le code de l'échantillon avec la possibilité de basculer cette fixation sur et hors tension; il est nécessaire chaque fois qu'une fenêtre entre Plein écran.

dans le contexte plus large de mon application plus complexe, je suis encore en train de rencontrer des problèmes de mise au point de clavier sur des sous-composants dans la fenêtre plein écran, où un clic de souris provoque une perte de mise au point de ma fenêtre. (Je suppose il va à la fenêtre de premier intervenant indésirable référencée ci-dessus.) Je ferai un rapport lorsque j'aurai plus d'information sur cette affaire - Je ne peux pas encore la reproduire dans le plus petit échantillon.


mise à jour 10/31:

mise à jour majeure du code d'échantillon:

  • Comprend basculer entre le mode plein écran exclusif et Lion-style plein écran en mode
  • écoute au KeyboardFocusManager pour afficher la hiérarchie du composant actuellement ciblé
  • utilise à la fois des cartes d'entrée et KeyListener s pour essayer de capturer l'entrée

a également creusé un peu plus avec ses collègues pour tenter d'isoler les problèmes:

sur un front, nous avons essayé de passer outre certaines méthodes RT.jar pour voir s'il y avait des problèmes avec la façon dont le périphérique d'écran est sélectionné. Aussi essayé ont été les points d'entrée à la Toolkit.fonctionnalité de beep () pour voir si les sons d'alerte venaient du côté de Java - ne semble pas.

sur un autre front, il était clair que même le côté natif ne reçoit pas d'événements clavier. Un collaborateur attribue cela à un changement d'un AWTView à un NSWindow dans 7u6.

une sélection de bogues Oracle existants a été trouvée, que vous pouvez consulter ici :


mise à jour 10/26:

grâce à la commentaire de @maslovalex ci-dessous au sujet d'une Applet travaillant sur 7u5, je suis retourné en arrière et examiné minutieusement la compatibilité avec les versions JDK pour OSX:

  • 10.7.1 avec 7u4: plein écran Fonctionne!
  • 10.7.1 avec 7u5: plein écran Fonctionne!
  • 10.7.5 avec 7u5: Fullscreen fonctionne!
  • 10.7.5 avec 7u6: pauses Plein écran: (

combiné avec les autres essais mentionnés ailleurs, il est clair qu'il y a eu un problème introduit avec 7u6 qui reste dans 7u7 et 7u9, et qui affecte à la fois le Lion 10.7 et le Lion de montagne 10.8.

7u6 a été une version majeure, apportant un support complet de JRE et JDK à Mac OS X et incluant Java FX dans la distribution. De plus amples informations sont disponibles dans les "Release Notes et la Roadmap . Il n'est pas extrêmement surprenant qu'un tel problème pourrait il apparaît alors que le support se déplace vers Java FX.

la question devient:

  1. est-ce que Oracle va corriger cela dans une version à court terme du JDK? (Si vous avez des liens vers des bogues existants, veuillez les inclure ici.)
  2. est-ce qu'une solution est possible dans l'intervalle?

autres mises à jour à partir d'aujourd'hui:

  • j'ai incorporé les extensions Apple approche du mode fullscreen comme voie alternative d'exploration (mise à jour du code échantillon en attendant le nettoyage). La bonne nouvelle: la saisie fonctionne! Les mauvaises nouvelles: il ne semble vraiment pas y avoir d'options de kiosque/isolement.

    j'ai essayé de tuer le Dock - directement ou avec un App - d'après ce que j'ai compris, le Dock est responsable de la commutation app Command-Tab, du contrôle de Mission et du Pas de tir, seulement pour découvrir que c'est responsable de la gestion des applications fullscreen! Ainsi, les appels Java deviennent non fonctionnels et ne reviennent jamais.

    S'il y a un moyen de désactiver le commande-onglet (ainsi que le contrôle de Mission et la rampe de lancement et les espaces) sans affecter la manipulation du Plein écran du Quai, cela serait extrêmement utile. Sinon, on peut essayer de reconfigurer certaines touches telles que la Commande, mais qui affectera la capacité d'utiliser ce modificateur d'ailleurs dans le programme et le système lui-même (pas exactement idéal, quand vous avez besoin de commander-C pour copier du texte).

  • Je n'ai pas eu de chance avec les KeyListeners (Je ne reçois pas de rappel), mais j'ai encore quelques options à essayer.

  • basé sur une suggestion d'un collaborateur, j'ai essayé ((sun.lwawt.macosx.LWCToolkit)Toolkit.getDefaultToolkit()).isApplicationActive() via la réflexion. L'idée était qu'il:

    est une méthode native avec le commentaire "Retourne true si l' application (une de ses fenêtres) possède le clavier focus.". Des appels à cette méthode ont été ajoutés dans CPlatformWindow.java au cours des derniers mois lié à focus logic. Si elle retourne false dans votre code de test, c'est probablement une partie du problème.

    Malheureusement, partout je l'ai vérifié, la méthode retourné véritable. Donc, même selon le système de bas niveau, mes fenêtres devraient avoir la mise au point du clavier.

  • mon précédent l'optimisme concernant le jAlbum a été déçu. Le développeur a posté une réponse sur leur forum qui explique comment ils ont simplement enlevé le support plein écran approprié sur OS X pendant L'exécution de Java 7. Ils ont un bug dans Oracle (et j'espère obtenir le numéro du bug).


mise à jour 10/25:

j'ai maintenant aussi essayé Java 7u9 sur Lion 10.7.4 et ai j'ai vu exactement le même problème, donc C'est JDK - pas spécifique à OS.

la question centrale est de savoir si vous pouvez intégrer dans un plein écran Les composants Swing du noyau de fenêtre qui ont la manipulation par défaut pour l'entrée de clavier ( JTextField/JTextArea ou même boîtes combo modifiables) et s'attendre à ce qu'ils se comportent normalement (sans avoir à reconstruire leurs fixations de clés de base manuellement). La question est également de savoir si d'autres piliers de la disposition fenêtrée, comme l'utilisation de tab pour focus traversal, devrait travail.

le but idéal serait d'avoir la capacité de prendre un Swing Application fenêtrée avec tous ses boutons, onglets, champs, etc. et l'exécuter en mode plein écran exclusif/kiosque avec la plupart des fonctionnalités intactes. (Auparavant, j'ai vu que les pop ups de dialogue ou les drop-Down de ComboBox ne fonctionnent pas en plein écran sur Java 6 sur OS X, mais d'autres composants se comportent bien.)

je vais étudier les fonctionnalités FullScreen de l'eawt, ce qui sera intéressant si elles les options de verrouillage de kiosque de soutien, telles que l'élimination de la commutation D'application Commande-onglet.


Question Originale:

j'ai une application Swing qui depuis des années a pris en charge FullScreen (exclusif) mode sur Mac OS X à travers Java 6. J'ai fait des tests de compatibilité avec la dernière version de Mountain Lion (10.8.2 Supplemental) et le JDK 7 D'Oracle et j'ai remarqué un problème dans ce mode: le mouvement de la souris et les clics fonctionnent bien, mais l'entrée au clavier n'est pas livrée aux composants.

(j'ai réduit cela dans un cas d'essai ci-dessous à ne pas pouvoir taper dans un simple JTextField en mode plein écran.)

l'un des symptômes est que chaque pression de touche produit un bip de système, comme si L'OS interdisait que des événements clavier soient livrés à l'application.

séparément, mon application dispose d'un crochet de sortie installé, et la commande-Q combo va déclencher ce crochet - il est clair que le système d'exploitation est à l'écoute des combinaisons de touches standard.

j'ai testé cela séparément sur trois Mac différents avec différentes installations:

  • Sur Apple Java 6u35 et 6u37: à la fois en mode fenêtré et plein écran dans les modes de recevoir des commentaires.
  • sur Oracle Java 7u7 et 7u9: le mode fenêtré fonctionne comme prévu alors que fullscreen a les symptômes surtout.

cela a peut-être déjà été déclaré: Java Graphiques en Mode Plein Écran ne pas Enregistrer l'Entrée du Clavier . Cependant, cette question n'est pas spécifique à la version ou à la plateforme Java.

recherche supplémentaire a fait apparaître une option d'écran complet séparé introduit dans le Lion: plein écran fonctionnalité pour les Applications Java sur mac OSX Lion . Je n'ai pas encore essayé d'utiliser cette approche, comme clavier les entrées semblent faire partie intégrante des cas d'utilisation cibles du mode exclusif FullScreen, tels que les jeux.

il y a une mention dans le JavaDoc pour ce mode que les méthodes d'entrée peuvent être désactivées. J'ai essayé d'appeler Component.enableInputMethods(false) , mais il semblait n'ont aucun effet .

je suis un peu optimiste qu'il y ait une solution à ce problème basée sur une entrée dans les notes de version D'une application Java que j'ai rencontré (JAlbum). A correction indiquée pour 10.10.6 : "le support du clavier ne fonctionnait pas lors de l'exécution du diaporama plein écran sur Mac et Java 7"

mon cas type est présenté ci-dessous. Il est légèrement modifié à partir du deuxième exemple de ce numéro (qui, non modifié, présente aussi mon problème): comment gérer les événements à partir du clavier et de la souris en mode exclusif plein écran en java? En particulier, il ajoute un bouton pour basculer en plein écran.

import java.lang.reflect.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.beans.*;

/** @see /q/fullscreen-swing-components-fail-to-receive-keyboard-input-on-java-7-on-mac-os-x-mountain-lion-9738/"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);
            }
        });
    }
}
24
demandé sur Community 2012-10-25 12:30:49

3 réponses

c'est parce que le composant auquel vous avez ajouté l'autre a maintenant perdu la mise au point, vous pouvez corriger cela par l'un ou l'autre:

  • appelant requestFocus() sur l'instance du composant à laquelle vous ajoutez KeyBinding s

ou

  • alternativement utiliser JComponent.WHEN_IN_FOCUSED_WINDOW avec KeyBinding s:

    component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_Q, 0),
                            "doSomething");
    component.getActionMap().put("doSomething",
                             anAction);
    

référence:

2
répondu David Kroukamp 2012-10-25 08:58:17

à la place, utilisez key bindings , comme indiqué dans la présente FullScreenTest . On peut aussi considérer un DocumentListener , représenté par ici , pour les éléments de texte.

2
répondu trashgod 2017-05-23 12:26:27

je pense que j'ai finalement trouvé une solution, en enregistrant les écouteurs de clic contre la JFrame elle-même. (C'est une classe qui étend JFrame, d'où toutes les références "Ceci".)

/**
 * Toggles full screen mode. Requires a lot of references to the JFrame.
 */
public void setFullScreen(boolean fullScreen){
    GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
    GraphicsDevice dev = env.getDefaultScreenDevice();//Gets the main screen
    if(!fullScreen){//Checks if a full screen application isn't open
        this.dispose();//Restarts the JFrame
        this.setVisible(false);
        this.setResizable(true);//Re-enables resize-ability.
        this.setUndecorated(false);//Adds title bar back
        this.setVisible(true);//Shows restarted JFrame
        this.removeMouseListener(macWorkAround);
        this.pack();
        this.setExtendedState(this.getExtendedState()|JFrame.MAXIMIZED_BOTH);//Returns to maximized state
        this.fullScreen = false;
    }
    else{
        this.dispose();//Restarts the JFrame
        this.setResizable(false);//Disables resizing else causes bugs
        this.setUndecorated(true);//removes title bar
        this.setVisible(true);//Makes it visible again
        this.revalidate();
        this.setSize(Toolkit.getDefaultToolkit().getScreenSize());
        try{
            dev.setFullScreenWindow(this);//Makes it full screen
            if(System.getProperty("os.name").indexOf("Mac OS X") >= 0){
                this.setVisible(false);
                this.setVisible(true);
                this.addMouseListener(macWorkAround);
            }
            this.repaint();
            this.revalidate();
        }
        catch(Exception e){
            dev.setFullScreenWindow(null);//Fall back behavior
        }
        this.requestFocus();
        this.fullScreen = true;
    }
}

private MouseAdapter macWorkAround = new MouseAdapter(){
    public void mouseClicked(MouseEvent e){
        MainGUI.this.setVisible(false);
        MainGUI.this.setVisible(true);
    }
};
0
répondu Skylion 2013-12-27 18:38:58