Window.setFullScreenWindow в Java 8 вызывает зависание приложения на alt-tab
Так что это то, с чем я экспериментировал ранее в этот день - сам режим работает нормально, мой цикл рендеринга работает нормально (пока, пока я не улучшу его), и так далее, и так далее.
Приложение не имеет никаких проблем при перемещении по моему компьютеру, когда не настроено полноэкранное отображение. Анимация тикает каждую секунду, ничего не происходит неправильно.
Это только когда я звоню:
device.setFullScreenWindow((Window) frame);
Что любая потеря фокуса окна приводит к тому, что приложение минимизирует себя и затем зависает, когда я возвращаюсь к нему. Вся база кода подробно описана ниже (на данном этапе это довольно простой проект). Тенденции использования ЦП от небольшого% (~2% на i5-3570K) до 0%.
Loader.java
package loader;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.SplashScreen;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import gui.MainFrame;
import misc.Device;
import utils.VideoUtils;
public class Loader {
private static final int SCREEN_CHOICE = 1;
public static void main(String[] args) {
System.setProperty("awt.useSystemAAFontSettings","on");
System.setProperty("swing.aatext", "true");
int PASSED_SCREEN_CHOICE = -1;
if(args.length > 0) {
PASSED_SCREEN_CHOICE = Integer.parseInt(args[0]);
} else {
PASSED_SCREEN_CHOICE = SCREEN_CHOICE;
}
SplashScreen splash = SplashScreen.getSplashScreen();
Graphics2D g = null;
if(null != splash) {
g = splash.createGraphics();
renderSplashFrame(g, 0);
splash.update();
}
displayUI(g, splash, PASSED_SCREEN_CHOICE);
}
// TODO :: rework rendering to leverage SwingWorker and a target FPS
private static void displayUI(final Graphics2D g, final SplashScreen splash, int SCREEN_CHOICE) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
setLookAndFeel();
Device[] devices = null;
if(null != g && null != splash) {
for(int i = 0; i < 265; i++) {
renderSplashFrame(g, i);
splash.update();
try {
Thread.sleep(7);
} catch(InterruptedException e) {
System.out.println("Thread sleep exception, on inner splashscreen timer!");
}
}
devices = VideoUtils.getMonitorDetails();
splash.close();
} else {
// set up display devices anyhow
devices = VideoUtils.getMonitorDetails();
}
MainFrame.displayAndShowGUI(devices, SCREEN_CHOICE);
}
});
}
private static void renderSplashFrame(Graphics2D g, int frame) {
final String[] comps = {
"Initialising",
"Preparing UI",
"Finalising"};
g.setComposite(AlphaComposite.Clear);
g.fillRect(25, 190, 290, 45);
g.setPaintMode();
g.setColor(new Color(255, 255, 150, 190));
g.drawString(comps[(frame / 90) % 3] + " . . ", 30, 230);
g.drawLine(25, 195, 290, 195); // -------
g.drawLine(25, 195, 25, 215); // |
g.drawLine(25, 215, 290, 215); // -------
g.drawLine(290, 195, 290, 215); // |
g.fillRect(25, 195, frame, 20);
}
private static void setLookAndFeel() {
// sets the look and feel to be that of the operating system's
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException |
InstantiationException |
IllegalAccessException |
UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
}
}
MainFrame.java
package gui;
import java.awt.FlowLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import misc.Device;
import utils.VideoUtils;
public class MainFrame {
private static JFrame mainframe;
private static JFrame debugframe;
private static MainView mainview;
private static Thread runner;
public static void displayAndShowGUI(Device[] devices, int choice) {
mainframe = new JFrame("Item-Driven Development");
mainframe.getContentPane().setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
Device chosenDevice;
for(Device d : devices) {
System.out.println(d.toString());
}
if(choice < devices.length) {
chosenDevice = devices[choice];
} else {
// default to the only one you have
chosenDevice = devices[0];
}
System.out.println("We have chosen device: " + chosenDevice.getDevice().getIDstring());
mainview = new MainView(chosenDevice);
mainframe.add(mainview);
mainframe.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
exitApplication((JFrame) e.getSource());
}
});
// let's centre this sucker
// mainframe.setLocation(chosenDevice.getCentreX() - mainview.getSimDimension().width / 2,
// chosenDevice.getCentreY() - mainview.getSimDimension().height / 2);
VideoUtils.setFullscreenResolution(mainframe, chosenDevice.getDevice(), chosenDevice.getDisplayMode());
mainframe.pack();
mainframe.setVisible(true);
runner = new Thread(mainview);
runner.start();
mainview.requestFocusInWindow();
}
public static void exitApplication(JFrame mainframe) {
int response = JOptionPane.showConfirmDialog(mainframe,
"Are you sure you want to exit?",
"Exit application?",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE);
if(response == JOptionPane.YES_OPTION) {
mainframe.setVisible(false);
mainframe.dispose();
System.exit(0);
}
}
public JFrame getMainframe() {
return mainframe;
}
public JFrame getDebugframe() {
return debugframe;
}
public Thread getRunner() {
return runner;
}
}
MainView.java
package gui;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Toolkit;
import javax.swing.JPanel;
import misc.Device;
import utils.ShaderUtils;
public class MainView extends JPanel implements Runnable {
private static final long serialVersionUID = 1L;
private Dimension simDimension;
private final long TARGET_FRAMESPERSECOND;
private long sleepInMillis;
private long startInNanos = System.nanoTime();
private long currentCycleInNanos = System.nanoTime() - startInNanos;
private long lastCycleInNanos = 0;
private long currentInSeconds = 0;
private long currentFrame = 0;
private boolean gameRunning = true;
private boolean gamePaused = false;
public MainView(Device display) {
simDimension = new Dimension(display.getDisplayMode().getWidth(), display.getDisplayMode().getHeight());
setPreferredSize(simDimension);
setBackground(Color.BLACK);
if(!display.isRefreshUnknown()) {
TARGET_FRAMESPERSECOND = display.getRefreshRate();
} else {
// default to 60
TARGET_FRAMESPERSECOND = 60;
}
sleepInMillis = 1000 / TARGET_FRAMESPERSECOND;
}
@Override
public void run() {
while(gameRunning) {
try {
long temp = currentCycleInNanos;
currentCycleInNanos = System.nanoTime() - startInNanos;
lastCycleInNanos = currentCycleInNanos - temp;
if((currentCycleInNanos / 1000 / 1000 / 1000) > currentInSeconds) {
currentInSeconds = currentInSeconds + 1;
System.out.println(sleepInMillis + "ms sleep per frame (" + (sleepInMillis * TARGET_FRAMESPERSECOND) + "ms sleep in total per second)");
System.out.println("max frames per second at: " + currentInSeconds + " seconds: " + currentFrame + "FPS (" + currentCycleInNanos + " nanos)");
currentFrame = 0;
}
repaint();
currentFrame = currentFrame + 1;
long timeout = sleepInMillis; // - (lastCycleInNanos / 1000 / 1000)
if(timeout < 0) timeout = 0;
Thread.sleep(timeout);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void paintComponent(Graphics g) {
Toolkit.getDefaultToolkit().sync();
super.paintComponent(g);
doDrawing(g);
}
private void doDrawing(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
ShaderUtils.applyQualityRenderingHints(g2d);
if(currentInSeconds % 2 == 0) {
g2d.setColor(Color.RED);
} else {
g2d.setColor(Color.GREEN);
}
Composite original = g2d.getComposite();
Composite alpha = AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.35f);
g2d.draw(new Rectangle(simDimension.width / 2 - (300 / 2), simDimension.height / 2 - (150 / 2), 300, 150));
}
public Dimension getSimDimension() {
return simDimension;
}
public boolean isGameRunning() {
return gameRunning;
}
public void setGameRunning(boolean gameRunning) {
this.gameRunning = gameRunning;
}
public boolean isGamePaused() {
return gamePaused;
}
public void setGamePaused(boolean gamePaused) {
this.gamePaused = gamePaused;
}
}
Device.java
package misc;
import java.awt.DisplayMode;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.Rectangle;
import java.awt.Window;
import utils.VideoUtils;
public class Device {
private GraphicsDevice device;
private DisplayMode displayMode;
private DisplayMode[] availableModes;
private GraphicsConfiguration graphicsConfig;
private int bitDepth;
private int supportedColours;
private int refreshRate;
private boolean refreshUnknown;
private int acceleratedMemory;
private boolean fullscreenSupport;
private Rectangle bounds;
private int centreX;
private int centreY;
public Device(GraphicsDevice device) {
displayMode = device.getDisplayMode();
availableModes = device.getDisplayModes();
graphicsConfig = device.getDefaultConfiguration();
bitDepth = displayMode.getBitDepth();
supportedColours = (int) Math.pow(2, bitDepth);
refreshRate = displayMode.getRefreshRate();
if(refreshRate == DisplayMode.REFRESH_RATE_UNKNOWN) {
refreshUnknown = true;
} else {
refreshUnknown = false;
}
acceleratedMemory = device.getAvailableAcceleratedMemory();
fullscreenSupport = device.isFullScreenSupported();
bounds = device.getDefaultConfiguration().getBounds();
centreX = bounds.x + ((int) bounds.getWidth() / 2);
centreY = bounds.y + ((int) bounds.getHeight() / 2);
this.device = device;
}
public GraphicsDevice getDevice() {
return device;
}
public void setDevice(GraphicsDevice device) {
this.device = device;
}
public DisplayMode getDisplayMode() {
return displayMode;
}
public void setDisplayMode(DisplayMode displayMode) {
this.displayMode = displayMode;
}
public DisplayMode[] getAvailableModes() {
return availableModes;
}
public void setAvailableModes(DisplayMode[] availableModes) {
this.availableModes = availableModes;
}
public GraphicsConfiguration getGraphicsConfig() {
return graphicsConfig;
}
public void setGraphicsConfig(GraphicsConfiguration graphicsConfig) {
this.graphicsConfig = graphicsConfig;
}
public int getBitDepth() {
return bitDepth;
}
public void setBitDepth(int bitDepth) {
this.bitDepth = bitDepth;
}
public int getSupportedColours() {
return supportedColours;
}
public void setSupportedColours(int supportedColours) {
this.supportedColours = supportedColours;
}
public int getRefreshRate() {
return refreshRate;
}
public void setRefreshRate(int refreshRate) {
this.refreshRate = refreshRate;
}
public boolean isRefreshUnknown() {
return refreshUnknown;
}
public void setRefreshUnknown(boolean refreshUnknown) {
this.refreshUnknown = refreshUnknown;
}
public int getAcceleratedMemory() {
return acceleratedMemory;
}
public void setAcceleratedMemory(int acceleratedMemory) {
this.acceleratedMemory = acceleratedMemory;
}
public boolean isFullscreenSupport() {
return fullscreenSupport;
}
public void setFullscreenSupport(boolean fullscreenSupport) {
this.fullscreenSupport = fullscreenSupport;
}
public Rectangle getBounds() {
return bounds;
}
public void setBounds(Rectangle bounds) {
this.bounds = bounds;
}
public int getCentreX() {
return centreX;
}
public void setCentreX(int centreX) {
this.centreX = centreX;
}
public int getCentreY() {
return centreY;
}
public void setCentreY(int centreY) {
this.centreY = centreY;
}
@Override
public String toString() {
String result = "Device: ";
result = result + device.getIDstring() + "\n\n";
result = result + "\tDisplay Mode: " + VideoUtils.printDisplayModeDetails(displayMode) + "\n";
result = result + "\t" + availableModes.length + " available display modes to choose from\n";
result = result + "\tRefresh Rate: ";
if(refreshUnknown) {
result = result + "UNKNOWN\n";
} else {
result = result + refreshRate + "Hz\n";
}
double xBounds = (bounds.getX() + bounds.getMaxX());
if(xBounds < 0) xBounds = 0 - xBounds;
double yBounds = (bounds.getY() - bounds.getMaxY());
if(yBounds < 0) yBounds = 0 - yBounds;
result = result + "\tDefined Bounds: " + xBounds + " / " + yBounds + "\n\n";
String fullscreenBool = fullscreenSupport ? "Yes" : "No";
if(acceleratedMemory < 0) {
result = result + "\tAvailable Accelerated Memory: n/a\n";
} else {
result = result + "\tAvailable Accelerated Memory: " + acceleratedMemory + "\n";
}
result = result + "\tSupports Fullscreen? " + fullscreenBool + "\n";
result = result + "\tImage Capabilities (Accelerated): " + (graphicsConfig.getImageCapabilities().isAccelerated() ? "Yes" : "No") + "\n";
result = result + "\tImage Capabilities (True Volatile): " + (graphicsConfig.getImageCapabilities().isTrueVolatile() ? "Yes" : "No") + "\n\n";
result = result + "\tSupported Colours: " + supportedColours + "\n";
result = result + "\tGraphics Configuration: " + graphicsConfig + "\n\n";
result = result + "------------------------------------------------\n";
return result;
}
}
VideoUtils.java
package utils;
import java.awt.DisplayMode;
import java.awt.Frame;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Window;
import javax.swing.JFrame;
import misc.Device;
public class VideoUtils {
public static Device[] getMonitorDetails() {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
Device[] result = new Device[ge.getScreenDevices().length];
GraphicsDevice[] gds = ge.getScreenDevices();
for(int n = 0; n < gds.length; n++) {
result[n] = new Device(gds[n]);
}
return result;
}
public static void setFullscreenResolution(JFrame frame, GraphicsDevice device, DisplayMode displayMode) {
frame.setUndecorated(true);
frame.setResizable(false);
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
if(device.isFullScreenSupported()) {
device.setFullScreenWindow((Window) frame);
if(device.isDisplayChangeSupported()) {
try {
// bit redundant on first run, removed for now
// device.setDisplayMode(displayMode);
} catch (IllegalArgumentException | UnsupportedOperationException e) {
if(e instanceof IllegalArgumentException) {
System.err.println("Illegal argument provided: " + printDisplayModeDetails(displayMode));
} else {
System.err.println("Unsupported Operation attempted!");
}
e.printStackTrace();
}
} else {
System.err.println("Operation to set DisplayMode not supported by this device.");
}
} else {
System.err.println("Fullscreen Window mode not supported by this device.");
}
}
public static String printDisplayModeDetails(DisplayMode dm) {
return dm.getWidth() + " * " + dm.getHeight() + " @ " + dm.getRefreshRate() + "Hz (" + dm.getBitDepth() + "-bit)";
}
}
ShaderUtils.java
package utils;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
public class ShaderUtils {
public static void applyQualityRenderingHints(Graphics2D g2d) {
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
}
// with thanks to http://stackru.com/questions/21382966/colorize-a-picture-in-java/21385150#21385150
public static BufferedImage applyShader(BufferedImage input, Color shader) {
BufferedImage output = new BufferedImage(input.getWidth(), input.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = output.createGraphics();
g.drawImage(input, 0, 0, null);
g.setComposite(AlphaComposite.SrcAtop);
g.setColor(shader);
g.fillRect(0, 0, input.getWidth(), input.getHeight());
g.dispose();
return output;
}
}