minecraft-source/src/com/mojang/blaze3d/platform/Window.java

460 lines
17 KiB
Java

package com.mojang.blaze3d.platform;
import net.minecraft.client.main.SilentInitException;
import org.apache.logging.log4j.LogManager;
import org.lwjgl.glfw.Callbacks;
import org.lwjgl.glfw.GLFWErrorCallbackI;
import org.lwjgl.util.tinyfd.TinyFileDialogs;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.io.IOException;
import org.lwjgl.stb.STBImage;
import org.lwjgl.glfw.GLFWImage;
import java.io.FileNotFoundException;
import java.io.InputStream;
import org.lwjgl.PointerBuffer;
import org.lwjgl.system.MemoryUtil;
import org.lwjgl.system.MemoryStack;
import java.util.function.BiConsumer;
import org.lwjgl.opengl.GL;
import org.lwjgl.glfw.GLFW;
import com.mojang.blaze3d.systems.RenderSystem;
import javax.annotation.Nullable;
import java.util.Optional;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.apache.logging.log4j.Logger;
public final class Window implements AutoCloseable {
private static final Logger LOGGER;
private final GLFWErrorCallback defaultErrorCallback;
private final WindowEventHandler eventHandler;
private final ScreenManager screenManager;
private final long window;
private int windowedX;
private int windowedY;
private int windowedWidth;
private int windowedHeight;
private Optional<VideoMode> preferredFullscreenVideoMode;
private boolean fullscreen;
private boolean actuallyFullscreen;
private int x;
private int y;
private int width;
private int height;
private int framebufferWidth;
private int framebufferHeight;
private int guiScaledWidth;
private int guiScaledHeight;
private double guiScale;
private String errorSection;
private boolean dirty;
private int framerateLimit;
private boolean vsync;
public Window(final WindowEventHandler dgz, final ScreenManager dgu, final DisplayData dgi, @Nullable final String string4, final String string5) {
this.defaultErrorCallback = GLFWErrorCallback.create(this::defaultErrorCallback);
this.errorSection = "";
RenderSystem.assertThread(RenderSystem::isInInitPhase);
this.screenManager = dgu;
this.setBootErrorCallback();
this.setErrorSection("Pre startup");
this.eventHandler = dgz;
final Optional<VideoMode> optional7 = VideoMode.read(string4);
if (optional7.isPresent()) {
this.preferredFullscreenVideoMode = optional7;
}
else if (dgi.fullscreenWidth.isPresent() && dgi.fullscreenHeight.isPresent()) {
this.preferredFullscreenVideoMode = Optional.<VideoMode>of(new VideoMode(dgi.fullscreenWidth.getAsInt(), dgi.fullscreenHeight.getAsInt(), 8, 8, 8, 60));
}
else {
this.preferredFullscreenVideoMode = Optional.<VideoMode>empty();
}
final boolean isFullscreen = dgi.isFullscreen;
this.fullscreen = isFullscreen;
this.actuallyFullscreen = isFullscreen;
final Monitor dgq8 = dgu.getMonitor(GLFW.glfwGetPrimaryMonitor());
final int n = (dgi.width > 0) ? dgi.width : 1;
this.width = n;
this.windowedWidth = n;
final int n2 = (dgi.height > 0) ? dgi.height : 1;
this.height = n2;
this.windowedHeight = n2;
GLFW.glfwDefaultWindowHints();
GLFW.glfwWindowHint(139265, 196609);
GLFW.glfwWindowHint(139275, 221185);
GLFW.glfwWindowHint(139266, 2);
GLFW.glfwWindowHint(139267, 0);
GLFW.glfwWindowHint(139272, 0);
this.window = GLFW.glfwCreateWindow(this.width, this.height, (CharSequence)string5, (this.fullscreen && dgq8 != null) ? dgq8.getMonitor() : 0L, 0L);
if (dgq8 != null) {
final VideoMode dgx9 = dgq8.getPreferredVidMode(this.fullscreen ? this.preferredFullscreenVideoMode : Optional.<VideoMode>empty());
final int n3 = dgq8.getX() + dgx9.getWidth() / 2 - this.width / 2;
this.x = n3;
this.windowedX = n3;
final int n4 = dgq8.getY() + dgx9.getHeight() / 2 - this.height / 2;
this.y = n4;
this.windowedY = n4;
}
else {
final int[] arr9 = { 0 };
final int[] arr10 = { 0 };
GLFW.glfwGetWindowPos(this.window, arr9, arr10);
final int n5 = arr9[0];
this.x = n5;
this.windowedX = n5;
final int n6 = arr10[0];
this.y = n6;
this.windowedY = n6;
}
GLFW.glfwMakeContextCurrent(this.window);
GL.createCapabilities();
this.setMode();
this.refreshFramebufferSize();
GLFW.glfwSetFramebufferSizeCallback(this.window, this::onFramebufferResize);
GLFW.glfwSetWindowPosCallback(this.window, this::onMove);
GLFW.glfwSetWindowSizeCallback(this.window, this::onResize);
GLFW.glfwSetWindowFocusCallback(this.window, this::onFocus);
GLFW.glfwSetCursorEnterCallback(this.window, this::onEnter);
}
public int getRefreshRate() {
RenderSystem.assertThread(RenderSystem::isOnRenderThread);
return GLX._getRefreshRate(this);
}
public boolean shouldClose() {
return GLX._shouldClose(this);
}
public static void checkGlfwError(final BiConsumer<Integer, String> biConsumer) {
RenderSystem.assertThread(RenderSystem::isInInitPhase);
try (final MemoryStack memoryStack2 = MemoryStack.stackPush()) {
final PointerBuffer pointerBuffer4 = memoryStack2.mallocPointer(1);
final int integer5 = GLFW.glfwGetError(pointerBuffer4);
if (integer5 != 0) {
final long long6 = pointerBuffer4.get();
final String string8 = (long6 == 0L) ? "" : MemoryUtil.memUTF8(long6);
biConsumer.accept(integer5, string8);
}
}
}
public void setIcon(final InputStream inputStream1, final InputStream inputStream2) {
RenderSystem.assertThread(RenderSystem::isInInitPhase);
try (final MemoryStack memoryStack4 = MemoryStack.stackPush()) {
if (inputStream1 == null) {
throw new FileNotFoundException("icons/icon_16x16.png");
}
if (inputStream2 == null) {
throw new FileNotFoundException("icons/icon_32x32.png");
}
final IntBuffer intBuffer6 = memoryStack4.mallocInt(1);
final IntBuffer intBuffer7 = memoryStack4.mallocInt(1);
final IntBuffer intBuffer8 = memoryStack4.mallocInt(1);
final GLFWImage.Buffer buffer9 = GLFWImage.mallocStack(2, memoryStack4);
final ByteBuffer byteBuffer10 = this.readIconPixels(inputStream1, intBuffer6, intBuffer7, intBuffer8);
if (byteBuffer10 == null) {
throw new IllegalStateException("Could not load icon: " + STBImage.stbi_failure_reason());
}
buffer9.position(0);
buffer9.width(intBuffer6.get(0));
buffer9.height(intBuffer7.get(0));
buffer9.pixels(byteBuffer10);
final ByteBuffer byteBuffer11 = this.readIconPixels(inputStream2, intBuffer6, intBuffer7, intBuffer8);
if (byteBuffer11 == null) {
throw new IllegalStateException("Could not load icon: " + STBImage.stbi_failure_reason());
}
buffer9.position(1);
buffer9.width(intBuffer6.get(0));
buffer9.height(intBuffer7.get(0));
buffer9.pixels(byteBuffer11);
buffer9.position(0);
GLFW.glfwSetWindowIcon(this.window, buffer9);
STBImage.stbi_image_free(byteBuffer10);
STBImage.stbi_image_free(byteBuffer11);
}
catch (IOException iOException4) {
Window.LOGGER.error("Couldn't set icon", (Throwable)iOException4);
}
}
@Nullable
private ByteBuffer readIconPixels(final InputStream inputStream, final IntBuffer intBuffer2, final IntBuffer intBuffer3, final IntBuffer intBuffer4) throws IOException {
RenderSystem.assertThread(RenderSystem::isInInitPhase);
ByteBuffer byteBuffer6 = null;
try {
byteBuffer6 = TextureUtil.readResource(inputStream);
byteBuffer6.rewind();
return STBImage.stbi_load_from_memory(byteBuffer6, intBuffer2, intBuffer3, intBuffer4, 0);
}
finally {
if (byteBuffer6 != null) {
MemoryUtil.memFree((Buffer)byteBuffer6);
}
}
}
public void setErrorSection(final String string) {
this.errorSection = string;
}
private void setBootErrorCallback() {
RenderSystem.assertThread(RenderSystem::isInInitPhase);
GLFW.glfwSetErrorCallback(Window::bootCrash);
}
private static void bootCrash(final int integer, final long long2) {
RenderSystem.assertThread(RenderSystem::isInInitPhase);
final String string4 = "GLFW error " + integer + ": " + MemoryUtil.memUTF8(long2);
TinyFileDialogs.tinyfd_messageBox((CharSequence)"Minecraft", (CharSequence)(string4 + ".\n\nPlease make sure you have up-to-date drivers (see aka.ms/mcdriver for instructions)."), (CharSequence)"ok", (CharSequence)"error", false);
throw new WindowInitFailed(string4);
}
public void defaultErrorCallback(final int integer, final long long2) {
RenderSystem.assertThread(RenderSystem::isOnRenderThread);
final String string5 = MemoryUtil.memUTF8(long2);
Window.LOGGER.error("########## GL ERROR ##########");
Window.LOGGER.error("@ {}", this.errorSection);
Window.LOGGER.error("{}: {}", integer, string5);
}
public void setDefaultErrorCallback() {
final GLFWErrorCallback gLFWErrorCallback2 = GLFW.glfwSetErrorCallback((GLFWErrorCallbackI)this.defaultErrorCallback);
if (gLFWErrorCallback2 != null) {
gLFWErrorCallback2.free();
}
}
public void updateVsync(final boolean boolean1) {
RenderSystem.assertThread(RenderSystem::isOnRenderThreadOrInit);
GLFW.glfwSwapInterval((int)((this.vsync = boolean1) ? 1 : 0));
}
@Override
public void close() {
RenderSystem.assertThread(RenderSystem::isOnRenderThread);
Callbacks.glfwFreeCallbacks(this.window);
this.defaultErrorCallback.close();
GLFW.glfwDestroyWindow(this.window);
GLFW.glfwTerminate();
}
private void onMove(final long long1, final int integer2, final int integer3) {
this.x = integer2;
this.y = integer3;
}
private void onFramebufferResize(final long long1, final int integer2, final int integer3) {
if (long1 != this.window) {
return;
}
final int integer4 = this.getWidth();
final int integer5 = this.getHeight();
if (integer2 == 0 || integer3 == 0) {
return;
}
this.framebufferWidth = integer2;
this.framebufferHeight = integer3;
if (this.getWidth() != integer4 || this.getHeight() != integer5) {
this.eventHandler.resizeDisplay();
}
}
private void refreshFramebufferSize() {
RenderSystem.assertThread(RenderSystem::isInInitPhase);
final int[] arr2 = { 0 };
final int[] arr3 = { 0 };
GLFW.glfwGetFramebufferSize(this.window, arr2, arr3);
this.framebufferWidth = arr2[0];
this.framebufferHeight = arr3[0];
}
private void onResize(final long long1, final int integer2, final int integer3) {
this.width = integer2;
this.height = integer3;
}
private void onFocus(final long long1, final boolean boolean2) {
if (long1 == this.window) {
this.eventHandler.setWindowActive(boolean2);
}
}
private void onEnter(final long long1, final boolean boolean2) {
if (boolean2) {
this.eventHandler.cursorEntered();
}
}
public void setFramerateLimit(final int integer) {
this.framerateLimit = integer;
}
public int getFramerateLimit() {
return this.framerateLimit;
}
public void updateDisplay() {
RenderSystem.flipFrame(this.window);
if (this.fullscreen != this.actuallyFullscreen) {
this.actuallyFullscreen = this.fullscreen;
this.updateFullscreen(this.vsync);
}
}
public Optional<VideoMode> getPreferredFullscreenVideoMode() {
return this.preferredFullscreenVideoMode;
}
public void setPreferredFullscreenVideoMode(final Optional<VideoMode> optional) {
final boolean boolean3 = !optional.equals(this.preferredFullscreenVideoMode);
this.preferredFullscreenVideoMode = optional;
if (boolean3) {
this.dirty = true;
}
}
public void changeFullscreenVideoMode() {
if (this.fullscreen && this.dirty) {
this.dirty = false;
this.setMode();
this.eventHandler.resizeDisplay();
}
}
private void setMode() {
RenderSystem.assertThread(RenderSystem::isInInitPhase);
final boolean boolean2 = GLFW.glfwGetWindowMonitor(this.window) != 0L;
if (this.fullscreen) {
final Monitor dgq3 = this.screenManager.findBestMonitor(this);
if (dgq3 == null) {
Window.LOGGER.warn("Failed to find suitable monitor for fullscreen mode");
this.fullscreen = false;
}
else {
final VideoMode dgx4 = dgq3.getPreferredVidMode(this.preferredFullscreenVideoMode);
if (!boolean2) {
this.windowedX = this.x;
this.windowedY = this.y;
this.windowedWidth = this.width;
this.windowedHeight = this.height;
}
this.x = 0;
this.y = 0;
this.width = dgx4.getWidth();
this.height = dgx4.getHeight();
GLFW.glfwSetWindowMonitor(this.window, dgq3.getMonitor(), this.x, this.y, this.width, this.height, dgx4.getRefreshRate());
}
}
else {
this.x = this.windowedX;
this.y = this.windowedY;
this.width = this.windowedWidth;
this.height = this.windowedHeight;
GLFW.glfwSetWindowMonitor(this.window, 0L, this.x, this.y, this.width, this.height, -1);
}
}
public void toggleFullScreen() {
this.fullscreen = !this.fullscreen;
}
private void updateFullscreen(final boolean boolean1) {
RenderSystem.assertThread(RenderSystem::isOnRenderThread);
try {
this.setMode();
this.eventHandler.resizeDisplay();
this.updateVsync(boolean1);
this.updateDisplay();
}
catch (Exception exception3) {
Window.LOGGER.error("Couldn't toggle fullscreen", (Throwable)exception3);
}
}
public int calculateScale(final int integer, final boolean boolean2) {
int integer2;
for (integer2 = 1; integer2 != integer && integer2 < this.framebufferWidth && integer2 < this.framebufferHeight && this.framebufferWidth / (integer2 + 1) >= 320 && this.framebufferHeight / (integer2 + 1) >= 240; ++integer2) {}
if (boolean2 && integer2 % 2 != 0) {
++integer2;
}
return integer2;
}
public void setGuiScale(final double double1) {
this.guiScale = double1;
final int integer4 = (int)(this.framebufferWidth / double1);
this.guiScaledWidth = ((this.framebufferWidth / double1 > integer4) ? (integer4 + 1) : integer4);
final int integer5 = (int)(this.framebufferHeight / double1);
this.guiScaledHeight = ((this.framebufferHeight / double1 > integer5) ? (integer5 + 1) : integer5);
}
public void setTitle(final String string) {
GLFW.glfwSetWindowTitle(this.window, (CharSequence)string);
}
public long getWindow() {
return this.window;
}
public boolean isFullscreen() {
return this.fullscreen;
}
public int getWidth() {
return this.framebufferWidth;
}
public int getHeight() {
return this.framebufferHeight;
}
public int getScreenWidth() {
return this.width;
}
public int getScreenHeight() {
return this.height;
}
public int getGuiScaledWidth() {
return this.guiScaledWidth;
}
public int getGuiScaledHeight() {
return this.guiScaledHeight;
}
public int getX() {
return this.x;
}
public int getY() {
return this.y;
}
public double getGuiScale() {
return this.guiScale;
}
@Nullable
public Monitor findBestMonitor() {
return this.screenManager.findBestMonitor(this);
}
public void updateRawMouseInput(final boolean boolean1) {
InputConstants.updateRawMouseInput(this.window, boolean1);
}
static {
LOGGER = LogManager.getLogger();
}
public static class WindowInitFailed extends SilentInitException {
private WindowInitFailed(final String string) {
super(string);
}
}
}