/* * Decompiled with CFR 0.152. * * Could not load the following classes: * com.mojang.logging.LogUtils * org.jspecify.annotations.Nullable * org.lwjgl.PointerBuffer * org.lwjgl.glfw.Callbacks * org.lwjgl.glfw.GLFW * org.lwjgl.glfw.GLFWErrorCallback * org.lwjgl.glfw.GLFWErrorCallbackI * org.lwjgl.glfw.GLFWImage * org.lwjgl.glfw.GLFWImage$Buffer * org.lwjgl.glfw.GLFWWindowCloseCallback * org.lwjgl.system.MemoryStack * org.lwjgl.system.MemoryUtil * org.lwjgl.util.tinyfd.TinyFileDialogs * org.slf4j.Logger */ package com.mojang.blaze3d.platform; import com.mojang.blaze3d.TracyFrameCapture; import com.mojang.blaze3d.platform.DisplayData; import com.mojang.blaze3d.platform.GLX; import com.mojang.blaze3d.platform.IconSet; import com.mojang.blaze3d.platform.InputConstants; import com.mojang.blaze3d.platform.MacosUtil; import com.mojang.blaze3d.platform.Monitor; import com.mojang.blaze3d.platform.NativeImage; import com.mojang.blaze3d.platform.ScreenManager; import com.mojang.blaze3d.platform.VideoMode; import com.mojang.blaze3d.platform.WindowEventHandler; import com.mojang.blaze3d.platform.cursor.CursorType; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.logging.LogUtils; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Optional; import java.util.function.BiConsumer; import net.minecraft.CrashReport; import net.minecraft.CrashReportCategory; import net.minecraft.ReportedException; import net.minecraft.client.main.SilentInitException; import net.minecraft.server.packs.PackResources; import net.minecraft.server.packs.resources.IoSupplier; import org.jspecify.annotations.Nullable; import org.lwjgl.PointerBuffer; import org.lwjgl.glfw.Callbacks; import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFWErrorCallback; import org.lwjgl.glfw.GLFWErrorCallbackI; import org.lwjgl.glfw.GLFWImage; import org.lwjgl.glfw.GLFWWindowCloseCallback; import org.lwjgl.system.MemoryStack; import org.lwjgl.system.MemoryUtil; import org.lwjgl.util.tinyfd.TinyFileDialogs; import org.slf4j.Logger; public final class Window implements AutoCloseable { private static final Logger LOGGER = LogUtils.getLogger(); public static final int BASE_WIDTH = 320; public static final int BASE_HEIGHT = 240; private final GLFWErrorCallback defaultErrorCallback = GLFWErrorCallback.create(this::defaultErrorCallback); private final WindowEventHandler eventHandler; private final ScreenManager screenManager; private final long handle; 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 int guiScale; private String errorSection = ""; private boolean dirty; private boolean vsync; private boolean iconified; private boolean minimized; private boolean allowCursorChanges; private CursorType currentCursor = CursorType.DEFAULT; public Window(WindowEventHandler eventHandler, ScreenManager screenManager, DisplayData displayData, @Nullable String fullscreenVideoModeString, String title) { this.screenManager = screenManager; this.setBootErrorCallback(); this.setErrorSection("Pre startup"); this.eventHandler = eventHandler; Optional optionsMode = VideoMode.read(fullscreenVideoModeString); this.preferredFullscreenVideoMode = optionsMode.isPresent() ? optionsMode : (displayData.fullscreenWidth().isPresent() && displayData.fullscreenHeight().isPresent() ? Optional.of(new VideoMode(displayData.fullscreenWidth().getAsInt(), displayData.fullscreenHeight().getAsInt(), 8, 8, 8, 60)) : Optional.empty()); this.actuallyFullscreen = this.fullscreen = displayData.isFullscreen(); Monitor initialMonitor = screenManager.getMonitor(GLFW.glfwGetPrimaryMonitor()); this.windowedWidth = this.width = Math.max(displayData.width(), 1); this.windowedHeight = this.height = Math.max(displayData.height(), 1); GLFW.glfwDefaultWindowHints(); GLFW.glfwWindowHint((int)139265, (int)196609); GLFW.glfwWindowHint((int)139275, (int)221185); GLFW.glfwWindowHint((int)139266, (int)3); GLFW.glfwWindowHint((int)139267, (int)3); GLFW.glfwWindowHint((int)139272, (int)204801); GLFW.glfwWindowHint((int)139270, (int)1); this.handle = GLFW.glfwCreateWindow((int)this.width, (int)this.height, (CharSequence)title, (long)(this.fullscreen && initialMonitor != null ? initialMonitor.getMonitor() : 0L), (long)0L); if (initialMonitor != null) { VideoMode mode = initialMonitor.getPreferredVidMode(this.fullscreen ? this.preferredFullscreenVideoMode : Optional.empty()); this.windowedX = this.x = initialMonitor.getX() + mode.getWidth() / 2 - this.width / 2; this.windowedY = this.y = initialMonitor.getY() + mode.getHeight() / 2 - this.height / 2; } else { int[] actualX = new int[1]; int[] actualY = new int[1]; GLFW.glfwGetWindowPos((long)this.handle, (int[])actualX, (int[])actualY); this.windowedX = this.x = actualX[0]; this.windowedY = this.y = actualY[0]; } this.setMode(); this.refreshFramebufferSize(); GLFW.glfwSetFramebufferSizeCallback((long)this.handle, this::onFramebufferResize); GLFW.glfwSetWindowPosCallback((long)this.handle, this::onMove); GLFW.glfwSetWindowSizeCallback((long)this.handle, this::onResize); GLFW.glfwSetWindowFocusCallback((long)this.handle, this::onFocus); GLFW.glfwSetCursorEnterCallback((long)this.handle, this::onEnter); GLFW.glfwSetWindowIconifyCallback((long)this.handle, this::onIconify); } public static String getPlatform() { int platform = GLFW.glfwGetPlatform(); return switch (platform) { case 0 -> ""; case 393217 -> "win32"; case 393218 -> "cocoa"; case 393219 -> "wayland"; case 393220 -> "x11"; case 393221 -> "null"; default -> String.format(Locale.ROOT, "unknown (%08X)", platform); }; } public int getRefreshRate() { RenderSystem.assertOnRenderThread(); return GLX._getRefreshRate(this); } public boolean shouldClose() { return GLX._shouldClose(this); } public static void checkGlfwError(BiConsumer errorConsumer) { try (MemoryStack stack = MemoryStack.stackPush();){ PointerBuffer errorDescription = stack.mallocPointer(1); int errorCode = GLFW.glfwGetError((PointerBuffer)errorDescription); if (errorCode != 0) { long errorDescriptionAddress = errorDescription.get(); String errorMessage = errorDescriptionAddress == 0L ? "" : MemoryUtil.memUTF8((long)errorDescriptionAddress); errorConsumer.accept(errorCode, errorMessage); } } } /* * WARNING - Removed try catching itself - possible behaviour change. */ public void setIcon(PackResources resources, IconSet iconSet) throws IOException { int platform = GLFW.glfwGetPlatform(); switch (platform) { case 393217: case 393220: { List> iconStreams = iconSet.getStandardIcons(resources); ArrayList allocatedBuffers = new ArrayList(iconStreams.size()); try (MemoryStack stack = MemoryStack.stackPush();){ GLFWImage.Buffer icons = GLFWImage.malloc((int)iconStreams.size(), (MemoryStack)stack); for (int i = 0; i < iconStreams.size(); ++i) { try (NativeImage image = NativeImage.read(iconStreams.get(i).get());){ ByteBuffer pixels = MemoryUtil.memAlloc((int)(image.getWidth() * image.getHeight() * 4)); allocatedBuffers.add(pixels); pixels.asIntBuffer().put(image.getPixelsABGR()); icons.position(i); icons.width(image.getWidth()); icons.height(image.getHeight()); icons.pixels(pixels); continue; } } GLFW.glfwSetWindowIcon((long)this.handle, (GLFWImage.Buffer)((GLFWImage.Buffer)icons.position(0))); break; } finally { allocatedBuffers.forEach(MemoryUtil::memFree); } } case 393218: { MacosUtil.loadIcon(iconSet.getMacIcon(resources)); break; } case 393219: case 393221: { break; } default: { LOGGER.warn("Not setting icon for unrecognized platform: {}", (Object)platform); } } } public void setErrorSection(String string) { this.errorSection = string; } private void setBootErrorCallback() { GLFW.glfwSetErrorCallback(Window::bootCrash); } private static void bootCrash(int error, long description) { String message = "GLFW error " + error + ": " + MemoryUtil.memUTF8((long)description); TinyFileDialogs.tinyfd_messageBox((CharSequence)"Minecraft", (CharSequence)(message + ".\n\nPlease make sure you have up-to-date drivers (see aka.ms/mcdriver for instructions)."), (CharSequence)"ok", (CharSequence)"error", (boolean)false); throw new WindowInitFailed(message); } public void defaultErrorCallback(int errorCode, long description) { RenderSystem.assertOnRenderThread(); String errorString = MemoryUtil.memUTF8((long)description); LOGGER.error("########## GL ERROR ##########"); LOGGER.error("@ {}", (Object)this.errorSection); LOGGER.error("{}: {}", (Object)errorCode, (Object)errorString); } public void setDefaultErrorCallback() { GLFWErrorCallback previousCallback = GLFW.glfwSetErrorCallback((GLFWErrorCallbackI)this.defaultErrorCallback); if (previousCallback != null) { previousCallback.free(); } } public void updateVsync(boolean enableVsync) { RenderSystem.assertOnRenderThread(); this.vsync = enableVsync; GLFW.glfwSwapInterval((int)(enableVsync ? 1 : 0)); } @Override public void close() { RenderSystem.assertOnRenderThread(); Callbacks.glfwFreeCallbacks((long)this.handle); this.defaultErrorCallback.close(); GLFW.glfwDestroyWindow((long)this.handle); GLFW.glfwTerminate(); } private void onMove(long handle, int x, int y) { this.x = x; this.y = y; } private void onFramebufferResize(long handle, int newWidth, int newHeight) { if (handle != this.handle) { return; } int oldWidth = this.getWidth(); int oldHeight = this.getHeight(); if (newWidth == 0 || newHeight == 0) { this.minimized = true; return; } this.minimized = false; this.framebufferWidth = newWidth; this.framebufferHeight = newHeight; if (this.getWidth() != oldWidth || this.getHeight() != oldHeight) { try { this.eventHandler.resizeDisplay(); } catch (Exception e) { CrashReport report = CrashReport.forThrowable(e, "Window resize"); CrashReportCategory windowSizeDetails = report.addCategory("Window Dimensions"); windowSizeDetails.setDetail("Old", oldWidth + "x" + oldHeight); windowSizeDetails.setDetail("New", newWidth + "x" + newHeight); throw new ReportedException(report); } } } private void refreshFramebufferSize() { int[] outWidth = new int[1]; int[] outHeight = new int[1]; GLFW.glfwGetFramebufferSize((long)this.handle, (int[])outWidth, (int[])outHeight); this.framebufferWidth = outWidth[0] > 0 ? outWidth[0] : 1; this.framebufferHeight = outHeight[0] > 0 ? outHeight[0] : 1; } private void onResize(long handle, int newWidth, int newHeight) { this.width = newWidth; this.height = newHeight; } private void onFocus(long handle, boolean focused) { if (handle == this.handle) { this.eventHandler.setWindowActive(focused); } } private void onEnter(long handle, boolean entered) { if (entered) { this.eventHandler.cursorEntered(); } } private void onIconify(long handle, boolean iconified) { this.iconified = iconified; } public void updateDisplay(@Nullable TracyFrameCapture tracyFrameCapture) { RenderSystem.flipFrame(this, tracyFrameCapture); if (this.fullscreen != this.actuallyFullscreen) { this.actuallyFullscreen = this.fullscreen; this.updateFullscreen(this.vsync, tracyFrameCapture); } } public Optional getPreferredFullscreenVideoMode() { return this.preferredFullscreenVideoMode; } public void setPreferredFullscreenVideoMode(Optional preferredFullscreenVideoMode) { boolean changed = !preferredFullscreenVideoMode.equals(this.preferredFullscreenVideoMode); this.preferredFullscreenVideoMode = preferredFullscreenVideoMode; if (changed) { this.dirty = true; } } public void changeFullscreenVideoMode() { if (this.fullscreen && this.dirty) { this.dirty = false; this.setMode(); this.eventHandler.resizeDisplay(); } } private void setMode() { boolean wasFullscreen; boolean bl = wasFullscreen = GLFW.glfwGetWindowMonitor((long)this.handle) != 0L; if (this.fullscreen) { Monitor monitor = this.screenManager.findBestMonitor(this); if (monitor == null) { LOGGER.warn("Failed to find suitable monitor for fullscreen mode"); this.fullscreen = false; } else { if (MacosUtil.IS_MACOS) { MacosUtil.exitNativeFullscreen(this); } VideoMode mode = monitor.getPreferredVidMode(this.preferredFullscreenVideoMode); if (!wasFullscreen) { this.windowedX = this.x; this.windowedY = this.y; this.windowedWidth = this.width; this.windowedHeight = this.height; } this.x = 0; this.y = 0; this.width = mode.getWidth(); this.height = mode.getHeight(); GLFW.glfwSetWindowMonitor((long)this.handle, (long)monitor.getMonitor(), (int)this.x, (int)this.y, (int)this.width, (int)this.height, (int)mode.getRefreshRate()); if (MacosUtil.IS_MACOS) { MacosUtil.clearResizableBit(this); } } } else { this.x = this.windowedX; this.y = this.windowedY; this.width = this.windowedWidth; this.height = this.windowedHeight; GLFW.glfwSetWindowMonitor((long)this.handle, (long)0L, (int)this.x, (int)this.y, (int)this.width, (int)this.height, (int)-1); } } public void toggleFullScreen() { this.fullscreen = !this.fullscreen; } public void setWindowed(int width, int height) { this.windowedWidth = width; this.windowedHeight = height; this.fullscreen = false; this.setMode(); } private void updateFullscreen(boolean enableVsync, @Nullable TracyFrameCapture tracyFrameCapture) { RenderSystem.assertOnRenderThread(); try { this.setMode(); this.eventHandler.resizeDisplay(); this.updateVsync(enableVsync); this.updateDisplay(tracyFrameCapture); } catch (Exception e) { LOGGER.error("Couldn't toggle fullscreen", (Throwable)e); } } public int calculateScale(int maxScale, boolean enforceUnicode) { int scale; for (scale = 1; scale != maxScale && scale < this.framebufferWidth && scale < this.framebufferHeight && this.framebufferWidth / (scale + 1) >= 320 && this.framebufferHeight / (scale + 1) >= 240; ++scale) { } if (enforceUnicode && scale % 2 != 0) { ++scale; } return scale; } public void setGuiScale(int guiScale) { this.guiScale = guiScale; double doubleGuiScale = guiScale; int width = (int)((double)this.framebufferWidth / doubleGuiScale); this.guiScaledWidth = (double)this.framebufferWidth / doubleGuiScale > (double)width ? width + 1 : width; int height = (int)((double)this.framebufferHeight / doubleGuiScale); this.guiScaledHeight = (double)this.framebufferHeight / doubleGuiScale > (double)height ? height + 1 : height; } public void setTitle(String title) { GLFW.glfwSetWindowTitle((long)this.handle, (CharSequence)title); } public long handle() { return this.handle; } public boolean isFullscreen() { return this.fullscreen; } public boolean isIconified() { return this.iconified; } public int getWidth() { return this.framebufferWidth; } public int getHeight() { return this.framebufferHeight; } public void setWidth(int width) { this.framebufferWidth = width; } public void setHeight(int height) { this.framebufferHeight = height; } 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 int getGuiScale() { return this.guiScale; } public @Nullable Monitor findBestMonitor() { return this.screenManager.findBestMonitor(this); } public void updateRawMouseInput(boolean value) { InputConstants.updateRawMouseInput(this, value); } public void setWindowCloseCallback(Runnable task) { GLFWWindowCloseCallback prev = GLFW.glfwSetWindowCloseCallback((long)this.handle, id -> task.run()); if (prev != null) { prev.free(); } } public boolean isMinimized() { return this.minimized; } public void setAllowCursorChanges(boolean value) { this.allowCursorChanges = value; } public void selectCursor(CursorType cursor) { CursorType effectiveCursor; CursorType cursorType = effectiveCursor = this.allowCursorChanges ? cursor : CursorType.DEFAULT; if (this.currentCursor != effectiveCursor) { this.currentCursor = effectiveCursor; effectiveCursor.select(this); } } public float getAppropriateLineWidth() { return Math.max(2.5f, (float)this.getWidth() / 1920.0f * 2.5f); } public static class WindowInitFailed extends SilentInitException { private WindowInitFailed(String message) { super(message); } } }