532 lines
20 KiB
Java
532 lines
20 KiB
Java
/*
|
|
* 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<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 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<VideoMode> 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 -> "<error>";
|
|
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<Integer, String> 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<IoSupplier<InputStream>> iconStreams = iconSet.getStandardIcons(resources);
|
|
ArrayList<ByteBuffer> allocatedBuffers = new ArrayList<ByteBuffer>(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<VideoMode> getPreferredFullscreenVideoMode() {
|
|
return this.preferredFullscreenVideoMode;
|
|
}
|
|
|
|
public void setPreferredFullscreenVideoMode(Optional<VideoMode> 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);
|
|
}
|
|
}
|
|
}
|
|
|