0

I am working on a small game using full screen exclusive and I need to be able to receive keyboard input from the player.

In my program, I have a Window which I set to Full Screen Exclusive, and a rendering loop.

Window creation:

private void initialize() {
    //This is used for my game loop...
    running = true;

    //Create the instance variable 'window' here.
    window = new Window(null);
    //Ignoring OS paint requests...
    window.setIgnoreRepaint(true);
    //Set the window to full screen exclusive.
    GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(window);

    ...

Game Loop:

private void loop() {
    Graphics graphics = window.getGraphics();
    graphics.setColor(Color.CYAN);
    graphics.fillRect(0, 0, 1920, 1080);
    graphics.dispose();
}

My Imports:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
import java.awt.Window;

Now, this works fine. Just as it should. It renders a cyan colored rectangle, across my screen, however, I need to be able to detect when the user hits keys e.g. hitting Escape to close the program, and such. I don't know how to do this. :(

I have tried adding a KeyListener to my window (didn't work). I have tried adding a JPanel to my window and adding a Listener to that (also didn't work). I have tried requesting focus on my JPanel and doing the same thing above. I have tried making a JFrame with a KeyListener then passing it into my window's constructor. I have tried passing the same JFrame with Key Bindings to my window, rather than a KeyListener.

Obviously, none of the above has worked. (Errors weren't thrown, I simply couldn't get the program to output the text in my sysouts when I pressed a key or exit the program using System.exit(int);) I have taken out everything that didn't work for me from the above code; I currently have a window and a game loop. If there are any other ways for me to get Key Input in full screen exclusive, please inform me. (I feel as if there is a conventional way specifically for full screen exclusive, but I haven't found one yet.) Or if you believe there is a way to get one of the methods I have tried to work, (maybe you believe I did something wrong), please let me know. (I am getting somewhat desperate at this point).

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
Kröw
  • 504
  • 2
  • 13
  • 31
  • 1
    I'd start by having a look at [How to Use Key Bindings](http://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html), I'd then encourage you to have a look at [Painting in AWT and Swing](http://www.oracle.com/technetwork/java/painting-140037.html) and [Performing Custom Painting](http://docs.oracle.com/javase/tutorial/uiswing/painting/) because `Graphics graphics = window.getGraphics();` is not how custom painting should done. I know someone will suggest it, but `KeyListener` is generally a poor choice as it doesn't have the flexibility of the key bindings API - IMHO – MadProgrammer Feb 12 '17 at 06:15
  • 1
    If you're up for something more advanced, I'd also recommend having a look at [`BufferStrategy`](http://docs.oracle.com/javase/7/docs/api/java/awt/image/BufferStrategy.html) and [BufferStrategy and BufferCapabilities](https://docs.oracle.com/javase/tutorial/extra/fullscreen/bufferstrategy.html) (ps - `Window` is not double buffered, so you're likely to suffer from flashing durning updates) – MadProgrammer Feb 12 '17 at 06:16
  • 1
    And if you're wondering why I don't recommend `KeyListener` - [this is another reason why](http://stackoverflow.com/questions/24157794/java-jframe-fullscreen-keylisteners-doesnt-work/24157968#24157968) – MadProgrammer Feb 12 '17 at 06:21
  • @MadProgrammer Thank you for your help! I did go through the Key Binding page you referred me to and it didn't seem to work for me, but I'll continue to experiment with it. Anyways, I found the `Graphics graphics = window.getGraphics();` from [this](https://docs.oracle.com/javase/tutorial/extra/fullscreen/rendering.html) page. I'll look into the other pages you showed me now. – Kröw Feb 12 '17 at 06:21
  • You've interpreted the information incorrectly (and I haven't done that a thousand times), they don't use `getGraphics`, they use a method which returns a `Graphics` instance, which is preluding to the double buffering and page flipping sections which precede it – MadProgrammer Feb 12 '17 at 06:25
  • @MadProgrammer Oh. That makes sense. Thanks. – Kröw Feb 12 '17 at 06:26

2 Answers2

2

Example using Key Bindings. Really simple, also demonstrates the use of DisplayMode if you're so inclined, but once it's running, simply press and hold space, it will update, release it, it will update. Double click to close ;)

import java.awt.DisplayMode;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;

public class Test {

    public static void main(String[] args) {
        JFrame f = new JFrame("Test");
        f.setUndecorated(true);
        f.add(new TestPane());
        f.setResizable(false);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        GraphicsDevice device = GraphicsEnvironment
                .getLocalGraphicsEnvironment().getDefaultScreenDevice();
        if (device.isFullScreenSupported()) {
            device.setFullScreenWindow(f);
            if (device.isDisplayChangeSupported()) {
                try {
                    List<DisplayMode> matchingModes = new ArrayList<>(25);

                    DisplayMode[] modes = device.getDisplayModes();
                    for (DisplayMode mode : modes) {
                        if (mode.getWidth() == 1280 && mode.getHeight() == 720) {
                            matchingModes.add(mode);
                        }
                    }

                    if (!matchingModes.isEmpty()) {
                        for (DisplayMode mode : matchingModes) {
                            try {
                                device.setDisplayMode(mode);
                                System.out.println(mode.getWidth() + "x" + mode.getHeight() + " " + mode.getBitDepth() + " @ " + mode.getRefreshRate());
                                break;
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    } else {
                        System.err.println("!! No matching modes available");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                System.err.println("Change display mode not supported");
            }
        } else {
            System.err.println("Full screen not supported");
        }
    }

    public static class TestPane extends JPanel {

        private boolean spaced = false;

        public TestPane() {
            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    requestFocusInWindow(true);
                    if (e.getClickCount() == 2) {
                        SwingUtilities.windowForComponent(TestPane.this).dispose();
                    }
                }
            });

            InputMap im = getInputMap();
            ActionMap am = getActionMap();

            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, false), "spaced-pressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, true), "spaced-released");
            am.put("spaced-pressed", new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    spaced = true;
                    repaint();
                }
            });
            am.put("spaced-released", new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    spaced = false;
                    repaint();
                }
            });

            requestFocusInWindow(true);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            String text = getWidth() + "x" + getHeight();
            FontMetrics fm = g.getFontMetrics();
            int x = (getWidth() - fm.stringWidth(text)) / 2;
            int y = (getHeight() - fm.getHeight()) / 2;
            g.drawString(text, x, y + fm.getAscent());

            GraphicsDevice device = GraphicsEnvironment
                    .getLocalGraphicsEnvironment().getDefaultScreenDevice();

            DisplayMode mode = device.getDisplayMode();
            text = mode.getWidth() + "x" + mode.getHeight() + " " + mode.getBitDepth() + " @ " + mode.getRefreshRate();
            x = (getWidth() - fm.stringWidth(text)) / 2;
            y += fm.getHeight();
            g.drawString(text, x, y + fm.getAscent());

            text = "Spaced [" + spaced + "]";
            x = (getWidth() - fm.stringWidth(text)) / 2;
            y += fm.getHeight();
            g.drawString(text, x, y + fm.getAscent());
        }

    }
}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • When I tried to run this, it zoomed into my screen a little and I couldn't click anything. I think the frame was invisible but still covered the screen. The main problem though, was that it wouldn't receive any key/mouse input so I couldn't close it. (Also, nothing was rendered). Anyways, I Alt+Tabbed to my IDE and exited it to close the program. Did this work for you? I feel like there is something specific to my computer which won't let me get input. – Kröw Feb 12 '17 at 18:53
  • Remove the section which changes the display mode, and yes, it worked fine for me ;) – MadProgrammer Feb 12 '17 at 19:04
  • Thanks for this, it worked for me, but do you know of a way to do this with a window rather than a JFrame object? (Just out of curiosity) I will use this if there isn't a solution with a window object. – Kröw Feb 12 '17 at 19:27
  • JWindow is a curiosity, from what I remember, it's not actually focusable, at least from a keyboard point of view, which probably explains part of your issue. With regards to FSCM, and there shouldn't be any visual difference, if you're worried about to make it work with a BufferedStategy, you'd use a Canvas and add that to the frame – MadProgrammer Feb 12 '17 at 20:20
1

I think the default event handling for keystrokes is that you could only get them from a focusable component, i.e. a text-field. However, referencing this post, you could try using adding a custom KeyEventDispatcher (which I think is the underlying event-handling for AWT) to the KeyboardFocusManager.

Community
  • 1
  • 1
theKidOfArcrania
  • 488
  • 4
  • 13
  • I can't try this yet since I'm on my phone, but it looks promising! I will inform you if it works, and thanks again for your help! – Kröw Feb 12 '17 at 06:36
  • 1
    Or just use the [key bindings API](http://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html) as that's what it was designed for :P – MadProgrammer Feb 12 '17 at 06:39