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 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 optional7 = VideoMode.read(string4); if (optional7.isPresent()) { this.preferredFullscreenVideoMode = optional7; } else if (dgi.fullscreenWidth.isPresent() && dgi.fullscreenHeight.isPresent()) { this.preferredFullscreenVideoMode = Optional.of(new VideoMode(dgi.fullscreenWidth.getAsInt(), dgi.fullscreenHeight.getAsInt(), 8, 8, 8, 60)); } else { this.preferredFullscreenVideoMode = Optional.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.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 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 getPreferredFullscreenVideoMode() { return this.preferredFullscreenVideoMode; } public void setPreferredFullscreenVideoMode(final Optional 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); } } }