24

12/21 更新:

7u10 が最近リリースされました。次のことを確認しました。

  1. 問題はまだ解決していません
  2. ありがたいことに、回避策は引き続き機能します。

11/7 更新:

そして、回避策があります!

openjdk.java.net メーリング リストの Oracle の Leonid Romanov は、何が起こっているかについていくつかの洞察を提供しました

まあ、まだ 100% 確実ではありませんが、フルスクリーンに入ると、他のウィンドウが最初の応答者になるように見えます。次の回避策をお試しください: フレームで setFullScreenWindow() を呼び出した後、setVisible(false) に続いて setVisible(true) を呼び出します。これにより、理論的には、正しいファーストレスポンダが復元されるはずです。

動作するように見えるコードのスニペットは次のとおりです。

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

サンプル コードを更新して、この修正のオンとオフを切り替える機能を追加しました。ウィンドウがフルスクリーンになるたびに必要です。

より複雑なアプリケーションのより大きなコンテキストでは、フルスクリーン ウィンドウ内のサブコンポーネントでキーボード フォーカスの問題が発生し、マウス クリックでウィンドウのフォーカスが失われます。(上記で参照した望ましくない最初の応答者ウィンドウに行くと推測しています。) このケースについてさらに情報が得られたら、また報告します。まだ小さなサンプルでは再現できません。


10/31 更新:

サンプル コードのメジャー アップデート:

  • FullScreen 専用モードと Lion スタイルの FullScreen モードの切り替えが含まれています
  • をリッスンしてKeyboardFocusManager、現在フォーカスされているコンポーネントの階層を表示します
  • 入力マップとKeyListeners の両方を使用して、入力をキャプチャしようとします

また、問題を特定するために、同僚とさらに掘り下げました。

一方で、RT.jar のいくつかのメソッドをオーバーライドして、画面デバイスの選択方法に問題がないかどうかを確認しました。また、Toolkit.beep() 機能へのエントリ ポイントを試して、警告音が Java 側から来ているかどうかを確認しましたが、そうではないようです。

別の面では、ネイティブ側でさえキーボード イベントを受信して​​いないことは明らかでした。同僚は、これを7u6の anAWTViewから aへの切り替えに起因すると考えています。NSWindow

既存の Oracle バグの選択が見つかりました。ここで調べることができます。


10/26 更新:

7u5 で動作するアプレットに関する以下の @maslovalex のコメントのおかげで、私は戻って、OSX の JDK バージョンとの互換性を入念に調べました。

  • 10.7.1 と 7u4: フルスクリーンで動作!
  • 10.7.1 と 7u5: フルスクリーンで動作!
  • 10.7.5 と 7u5: フルスクリーンで動作!
  • 10.7.5 と 7u6: フルスクリーン ブレーク :(

他の場所で指摘されている他のテストと組み合わせると、7u7 と 7u9 に残っている 7u6 で導入された問題があり、Lion 10.7 と Mountain Lion 10.8 の両方に影響することは明らかです。

7u6 はメジャー マイルストーン リリースであり、JRE と JDK の完全なサポートを Mac OS X にもたらし、ディストリビューションの一部として Java FX も含めました。詳細については、リリース ノートおよびロードマップを参照してください。サポートが Java FX に移行するにつれて、このような問題が発生する可能性があることは驚くべきことではありません。

問題は次のようになります。

  1. Oracle は JDK の近い将来のリリースでこれを修正しますか? (既存のバグへのリンクがある場合は、ここに含めてください。)
  2. その間、回避策は可能ですか?

今日からのその他の更新:

  • 調査の代替パスとして、フルスクリーン モードへの Apple 拡張機能のアプローチを組み込みました(更新されたサンプル コードは、クリーンアップが保留中です)。良いニュース: 入力が機能します! 悪いニュース: キオスク/分離オプションは実際にはないようです. 直接またはアプリ
    を使用して Dock を強制終了しようとしましたが、Dock は Command-Tab アプリの切り替え、Mission Control、Launch Pad を担当していることを理解しているため、フルスクリーン アプリの処理も担当していることがわかります。そのため、Java 呼び出しは機能しなくなり、戻りません。Command-Tab を無効にする方法がある場合
    Dock のフルスクリーン処理に影響を与えずに (および Mission Control と Launchpad と Spaces を) 表示できれば、非常に便利です。別の方法として、Command などの特定のキーを再マップしようとすることもできますが、それはプログラムやシステム自体の他の場所でその修飾子を使用する機能に影響を与えます (テキストをコピーするために Command-C が必要な場合、正確には理想的ではありません)。

  • 私は KeyListeners でうまくいきませんでした (コールバックを受け取っていません) が、試してみるオプションがいくつかあります。

  • 同僚の提案に基づいて、((sun.lwawt.macosx.LWCToolkit)Toolkit.getDefaultToolkit()).isApplicationActive()リフレクションを試みました。「
    アプリケーション (ウィンドウの 1 つ) がキーボード フォーカスを所有している場合は true を返します。」というコメントを持つネイティブ メソッドです。このメソッドへの呼び出しは、フォーカス ロジックに関連して、過去数か月で CPlatformWindow.java に追加されました。テスト コードで false が返される場合は、おそらく問題の一部です。
    残念ながら、私がチェックしたところはどこでも、メソッドは true を返しました。したがって、低レベルのシステムによると、私のウィンドウにはキーボード フォーカスがあるはずです。

  • JAlbum の修正に関する私の以前の楽観主義は打ち砕かれました。開発者はフォーラムに、Java 7 の実行中に OS X で適切なフルスクリーン サポートを単純に削除した方法を説明する応答を投稿しました。Oracle にバグがあります (バグ番号を取得したいと考えています)。


10/25 更新:

Lion 10.7.4 で Java 7u9 も試してみましたが、まったく同じ問題が発生したため、OS 固有ではなく JDK です。

中心的な問題は、キーボード入力 (または編集可能なコンボ ボックス) のデフォルト処理を備えたフルスクリーン ウィンドウ コア Swing コンポーネントに組み込み、JTextField/JTextAreaそれらが正常に動作することを期待できるかどうかです (基本的なキー バインドを手動で再構築する必要はありません)。また、フォーカストラバーサルにタブを使用するなど、ウィンドウ化されたレイアウトの他の強力な機能が機能するかどうかも問題です。

理想的な目標は、すべてのボタン、タブ、フィールドなどを備えたウィンドウ化された Swing アプリを取得し、ほとんどの機能を損なわずにフルスクリーンの排他的/キオスク モードで実行できるようにすることです。(以前、OS X 上の Java 6 ではダイアログのポップアップまたはコンボボックスのドロップダウンがフルスクリーンで機能しないことを見てきましたが、他のコンポーネントは正常に動作します。)

Command-Tab アプリケーションの切り替えをなくすなど、キオスクのロック ダウン オプションをサポートする場合は興味深いでしょう。


元の質問:

Mac OS X で Java 6 までのフルスクリーン (排他的)モードを何年もサポートしてきた Swing アプリがあります。そのモードでの明らかな問題: マウスの移動とクリックは正常に機能しますが、キーボード入力はコンポーネントに配信されません。

(以下のテスト ケースでは、フルスクリーン モードで単純な JTextField に入力できないようにこれを絞り込みました。)

症状の 1 つは、キーを押すたびにシステム ビープ音が発生することです。これは、OS がキーボード イベントをアプリケーションに配信することを許可していないかのようです。

これとは別に、私のアプリケーションには終了フックがインストールされており、Command-Q コンボがそのフックをトリガーします。OS が標準のキー コンボをリッスンしていることは明らかです。

これを、さまざまなインストールを行った 3 つの異なる Mac で個別にテストしました。

  • Apple Java 6u35 および 6u37 では、ウィンドウ モードとフルスクリーン モードの両方が入力を受け取ります。
  • Oracle Java 7u7 および 7u9 の場合: ウィンドウ モードは期待どおりに動作しますが、フルスクリーンには上記の症状があります。

これは以前に報告された可能性があります: Java Graphics Full Screen Mode not Registering Keyboard Input。ただし、その質問は Java のバージョンやプラットフォームに固有のものではありません。

追加の検索により、Lion で導入された別のフルスクリーン オプションが見つかりました: OSX Lion 上の Java アプリのフルスクリーン機能。ゲームなどのフルスクリーン排他モードのターゲット ユース ケースにはキーボード入力が不可欠であるように思われるため、このアプローチはまだ試していません。

このモードの JavaDoc には、入力メソッドが無効になる可能性があるという記述があります。提案された を呼び出しComponent.enableInputMethods(false)てみましたが、効果がないようでした。

私が遭遇した Java アプリ (JAlbum) のリリース ノートのエントリに基づいて、この問題の解決策があることを多少楽観視しています。10.10.6 の修正: 「Mac および Java 7 で全画面スライド ショーを実行すると、キーボード サポートが機能しませんでした」

私のテストケースは以下です。この問題の 2 番目の例から少し変更されています (変更されていない場合でも、私の問題が発生します): How to handle events from keyboard and mouse in full screen exclusive mode in 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);
            }
        });
    }
}
4

3 に答える 3

2

代わりに、この に示すように、キー バインディングFullScreenTestを使用してください。また、ここDocumentListenerに示すテキスト コンポーネントの も考慮してください。

于 2012-10-25T10:29:54.340 に答える
2

これは、もう一方を追加したコンポーネントがフォーカスを失ったためです。次のいずれかでこれを修正できます。

  • srequestFocus()を追加するコンポーネント インスタンスの呼び出しKeyBinding

また

  • 代わりJComponent.WHEN_IN_FOCUSED_WINDOWKeyBindings を使用:

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

参照:

于 2012-10-25T08:52:06.707 に答える
0

JFrame自体に対してクリックリスナーを登録することで、最終的に解決策を見つけたと思います。(これは JFrame を拡張するクラスであるため、すべての「this」参照です。)

/**
 * 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);
    }
};
于 2013-12-27T03:56:24.270 に答える