506 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			506 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package com.mojang.blaze3d.platform;
 | |
| 
 | |
| import com.mojang.blaze3d.TracyFrameCapture;
 | |
| 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.fabricmc.api.EnvType;
 | |
| import net.fabricmc.api.Environment;
 | |
| 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.jetbrains.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.GLFWImage;
 | |
| import org.lwjgl.glfw.GLFWWindowCloseCallback;
 | |
| import org.lwjgl.glfw.GLFWImage.Buffer;
 | |
| import org.lwjgl.system.MemoryStack;
 | |
| import org.lwjgl.system.MemoryUtil;
 | |
| import org.lwjgl.util.tinyfd.TinyFileDialogs;
 | |
| import org.slf4j.Logger;
 | |
| 
 | |
| @Environment(EnvType.CLIENT)
 | |
| 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 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 int guiScale;
 | |
| 	private String errorSection = "";
 | |
| 	private boolean dirty;
 | |
| 	private boolean vsync;
 | |
| 	private boolean iconified;
 | |
| 	private boolean minimized;
 | |
| 
 | |
| 	public Window(
 | |
| 		WindowEventHandler eventHandler, ScreenManager screenManager, DisplayData displayData, @Nullable String preferredFullscreenVideoMode, String title
 | |
| 	) {
 | |
| 		this.screenManager = screenManager;
 | |
| 		this.setBootErrorCallback();
 | |
| 		this.setErrorSection("Pre startup");
 | |
| 		this.eventHandler = eventHandler;
 | |
| 		Optional<VideoMode> optional = VideoMode.read(preferredFullscreenVideoMode);
 | |
| 		if (optional.isPresent()) {
 | |
| 			this.preferredFullscreenVideoMode = optional;
 | |
| 		} else if (displayData.fullscreenWidth().isPresent() && displayData.fullscreenHeight().isPresent()) {
 | |
| 			this.preferredFullscreenVideoMode = Optional.of(
 | |
| 				new VideoMode(displayData.fullscreenWidth().getAsInt(), displayData.fullscreenHeight().getAsInt(), 8, 8, 8, 60)
 | |
| 			);
 | |
| 		} else {
 | |
| 			this.preferredFullscreenVideoMode = Optional.empty();
 | |
| 		}
 | |
| 
 | |
| 		this.actuallyFullscreen = this.fullscreen = displayData.isFullscreen();
 | |
| 		Monitor monitor = 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(139265, 196609);
 | |
| 		GLFW.glfwWindowHint(139275, 221185);
 | |
| 		GLFW.glfwWindowHint(139266, 3);
 | |
| 		GLFW.glfwWindowHint(139267, 2);
 | |
| 		GLFW.glfwWindowHint(139272, 204801);
 | |
| 		GLFW.glfwWindowHint(139270, 1);
 | |
| 		this.window = GLFW.glfwCreateWindow(this.width, this.height, title, this.fullscreen && monitor != null ? monitor.getMonitor() : 0L, 0L);
 | |
| 		if (monitor != null) {
 | |
| 			VideoMode videoMode = monitor.getPreferredVidMode(this.fullscreen ? this.preferredFullscreenVideoMode : Optional.empty());
 | |
| 			this.windowedX = this.x = monitor.getX() + videoMode.getWidth() / 2 - this.width / 2;
 | |
| 			this.windowedY = this.y = monitor.getY() + videoMode.getHeight() / 2 - this.height / 2;
 | |
| 		} else {
 | |
| 			int[] is = new int[1];
 | |
| 			int[] js = new int[1];
 | |
| 			GLFW.glfwGetWindowPos(this.window, is, js);
 | |
| 			this.windowedX = this.x = is[0];
 | |
| 			this.windowedY = this.y = js[0];
 | |
| 		}
 | |
| 
 | |
| 		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);
 | |
| 		GLFW.glfwSetWindowIconifyCallback(this.window, this::onIconify);
 | |
| 	}
 | |
| 
 | |
| 	public static String getPlatform() {
 | |
| 		int i = GLFW.glfwGetPlatform();
 | |
| 
 | |
| 		return switch (i) {
 | |
| 			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)", i);
 | |
| 		};
 | |
| 	}
 | |
| 
 | |
| 	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 memoryStack = MemoryStack.stackPush()) {
 | |
| 			PointerBuffer pointerBuffer = memoryStack.mallocPointer(1);
 | |
| 			int i = GLFW.glfwGetError(pointerBuffer);
 | |
| 			if (i != 0) {
 | |
| 				long l = pointerBuffer.get();
 | |
| 				String string = l == 0L ? "" : MemoryUtil.memUTF8(l);
 | |
| 				errorConsumer.accept(i, string);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void setIcon(PackResources packResources, IconSet iconSet) throws IOException {
 | |
| 		int i = GLFW.glfwGetPlatform();
 | |
| 		switch (i) {
 | |
| 			case 393217:
 | |
| 			case 393220:
 | |
| 				List<IoSupplier<InputStream>> list = iconSet.getStandardIcons(packResources);
 | |
| 				List<ByteBuffer> list2 = new ArrayList(list.size());
 | |
| 
 | |
| 				try (MemoryStack memoryStack = MemoryStack.stackPush()) {
 | |
| 					Buffer buffer = GLFWImage.malloc(list.size(), memoryStack);
 | |
| 
 | |
| 					for (int j = 0; j < list.size(); j++) {
 | |
| 						try (NativeImage nativeImage = NativeImage.read((InputStream)((IoSupplier)list.get(j)).get())) {
 | |
| 							ByteBuffer byteBuffer = MemoryUtil.memAlloc(nativeImage.getWidth() * nativeImage.getHeight() * 4);
 | |
| 							list2.add(byteBuffer);
 | |
| 							byteBuffer.asIntBuffer().put(nativeImage.getPixelsABGR());
 | |
| 							buffer.position(j);
 | |
| 							buffer.width(nativeImage.getWidth());
 | |
| 							buffer.height(nativeImage.getHeight());
 | |
| 							buffer.pixels(byteBuffer);
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 					GLFW.glfwSetWindowIcon(this.window, buffer.position(0));
 | |
| 					break;
 | |
| 				} finally {
 | |
| 					list2.forEach(MemoryUtil::memFree);
 | |
| 				}
 | |
| 			case 393218:
 | |
| 				MacosUtil.loadIcon(iconSet.getMacIcon(packResources));
 | |
| 			case 393219:
 | |
| 			case 393221:
 | |
| 				break;
 | |
| 			default:
 | |
| 				LOGGER.warn("Not setting icon for unrecognized platform: {}", i);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void setErrorSection(String errorSection) {
 | |
| 		this.errorSection = errorSection;
 | |
| 	}
 | |
| 
 | |
| 	private void setBootErrorCallback() {
 | |
| 		GLFW.glfwSetErrorCallback(Window::bootCrash);
 | |
| 	}
 | |
| 
 | |
| 	private static void bootCrash(int error, long description) {
 | |
| 		String string = "GLFW error " + error + ": " + MemoryUtil.memUTF8(description);
 | |
| 		TinyFileDialogs.tinyfd_messageBox(
 | |
| 			"Minecraft", string + ".\n\nPlease make sure you have up-to-date drivers (see aka.ms/mcdriver for instructions).", "ok", "error", false
 | |
| 		);
 | |
| 		throw new Window.WindowInitFailed(string);
 | |
| 	}
 | |
| 
 | |
| 	public void defaultErrorCallback(int error, long description) {
 | |
| 		RenderSystem.assertOnRenderThread();
 | |
| 		String string = MemoryUtil.memUTF8(description);
 | |
| 		LOGGER.error("########## GL ERROR ##########");
 | |
| 		LOGGER.error("@ {}", this.errorSection);
 | |
| 		LOGGER.error("{}: {}", error, string);
 | |
| 	}
 | |
| 
 | |
| 	public void setDefaultErrorCallback() {
 | |
| 		GLFWErrorCallback gLFWErrorCallback = GLFW.glfwSetErrorCallback(this.defaultErrorCallback);
 | |
| 		if (gLFWErrorCallback != null) {
 | |
| 			gLFWErrorCallback.free();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void updateVsync(boolean vsync) {
 | |
| 		RenderSystem.assertOnRenderThread();
 | |
| 		this.vsync = vsync;
 | |
| 		GLFW.glfwSwapInterval(vsync ? 1 : 0);
 | |
| 	}
 | |
| 
 | |
| 	public void close() {
 | |
| 		RenderSystem.assertOnRenderThread();
 | |
| 		Callbacks.glfwFreeCallbacks(this.window);
 | |
| 		this.defaultErrorCallback.close();
 | |
| 		GLFW.glfwDestroyWindow(this.window);
 | |
| 		GLFW.glfwTerminate();
 | |
| 	}
 | |
| 
 | |
| 	private void onMove(long window, int x, int y) {
 | |
| 		this.x = x;
 | |
| 		this.y = y;
 | |
| 	}
 | |
| 
 | |
| 	private void onFramebufferResize(long window, int framebufferWidth, int framebufferHeight) {
 | |
| 		if (window == this.window) {
 | |
| 			int i = this.getWidth();
 | |
| 			int j = this.getHeight();
 | |
| 			if (framebufferWidth != 0 && framebufferHeight != 0) {
 | |
| 				this.minimized = false;
 | |
| 				this.framebufferWidth = framebufferWidth;
 | |
| 				this.framebufferHeight = framebufferHeight;
 | |
| 				if (this.getWidth() != i || this.getHeight() != j) {
 | |
| 					try {
 | |
| 						this.eventHandler.resizeDisplay();
 | |
| 					} catch (Exception var10) {
 | |
| 						CrashReport crashReport = CrashReport.forThrowable(var10, "Window resize");
 | |
| 						CrashReportCategory crashReportCategory = crashReport.addCategory("Window Dimensions");
 | |
| 						crashReportCategory.setDetail("Old", i + "x" + j);
 | |
| 						crashReportCategory.setDetail("New", framebufferWidth + "x" + framebufferHeight);
 | |
| 						throw new ReportedException(crashReport);
 | |
| 					}
 | |
| 				}
 | |
| 			} else {
 | |
| 				this.minimized = true;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private void refreshFramebufferSize() {
 | |
| 		int[] is = new int[1];
 | |
| 		int[] js = new int[1];
 | |
| 		GLFW.glfwGetFramebufferSize(this.window, is, js);
 | |
| 		this.framebufferWidth = is[0] > 0 ? is[0] : 1;
 | |
| 		this.framebufferHeight = js[0] > 0 ? js[0] : 1;
 | |
| 	}
 | |
| 
 | |
| 	private void onResize(long window, int width, int height) {
 | |
| 		this.width = width;
 | |
| 		this.height = height;
 | |
| 	}
 | |
| 
 | |
| 	private void onFocus(long window, boolean hasFocus) {
 | |
| 		if (window == this.window) {
 | |
| 			this.eventHandler.setWindowActive(hasFocus);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @param cursorEntered {@code true} if the cursor entered the window, {@code false} if the cursor left
 | |
| 	 */
 | |
| 	private void onEnter(long window, boolean cursorEntered) {
 | |
| 		if (cursorEntered) {
 | |
| 			this.eventHandler.cursorEntered();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private void onIconify(long window, boolean iconified) {
 | |
| 		this.iconified = iconified;
 | |
| 	}
 | |
| 
 | |
| 	public void updateDisplay(@Nullable TracyFrameCapture tracyFrameCapture) {
 | |
| 		RenderSystem.flipFrame(this.window, 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 bl = !preferredFullscreenVideoMode.equals(this.preferredFullscreenVideoMode);
 | |
| 		this.preferredFullscreenVideoMode = preferredFullscreenVideoMode;
 | |
| 		if (bl) {
 | |
| 			this.dirty = true;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void changeFullscreenVideoMode() {
 | |
| 		if (this.fullscreen && this.dirty) {
 | |
| 			this.dirty = false;
 | |
| 			this.setMode();
 | |
| 			this.eventHandler.resizeDisplay();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private void setMode() {
 | |
| 		boolean bl = GLFW.glfwGetWindowMonitor(this.window) != 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.window);
 | |
| 				}
 | |
| 
 | |
| 				VideoMode videoMode = monitor.getPreferredVidMode(this.preferredFullscreenVideoMode);
 | |
| 				if (!bl) {
 | |
| 					this.windowedX = this.x;
 | |
| 					this.windowedY = this.y;
 | |
| 					this.windowedWidth = this.width;
 | |
| 					this.windowedHeight = this.height;
 | |
| 				}
 | |
| 
 | |
| 				this.x = 0;
 | |
| 				this.y = 0;
 | |
| 				this.width = videoMode.getWidth();
 | |
| 				this.height = videoMode.getHeight();
 | |
| 				GLFW.glfwSetWindowMonitor(this.window, monitor.getMonitor(), this.x, this.y, this.width, this.height, videoMode.getRefreshRate());
 | |
| 				if (MacosUtil.IS_MACOS) {
 | |
| 					MacosUtil.clearResizableBit(this.window);
 | |
| 				}
 | |
| 			}
 | |
| 		} 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;
 | |
| 	}
 | |
| 
 | |
| 	public void setWindowed(int windowedWidth, int windowedHeight) {
 | |
| 		this.windowedWidth = windowedWidth;
 | |
| 		this.windowedHeight = windowedHeight;
 | |
| 		this.fullscreen = false;
 | |
| 		this.setMode();
 | |
| 	}
 | |
| 
 | |
| 	private void updateFullscreen(boolean vsyncEnabled, @Nullable TracyFrameCapture tracyFrameCapture) {
 | |
| 		RenderSystem.assertOnRenderThread();
 | |
| 
 | |
| 		try {
 | |
| 			this.setMode();
 | |
| 			this.eventHandler.resizeDisplay();
 | |
| 			this.updateVsync(vsyncEnabled);
 | |
| 			this.updateDisplay(tracyFrameCapture);
 | |
| 		} catch (Exception var4) {
 | |
| 			LOGGER.error("Couldn't toggle fullscreen", (Throwable)var4);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public int calculateScale(int guiScale, boolean forceUnicode) {
 | |
| 		int i = 1;
 | |
| 
 | |
| 		while (
 | |
| 			i != guiScale
 | |
| 				&& i < this.framebufferWidth
 | |
| 				&& i < this.framebufferHeight
 | |
| 				&& this.framebufferWidth / (i + 1) >= 320
 | |
| 				&& this.framebufferHeight / (i + 1) >= 240
 | |
| 		) {
 | |
| 			i++;
 | |
| 		}
 | |
| 
 | |
| 		if (forceUnicode && i % 2 != 0) {
 | |
| 			i++;
 | |
| 		}
 | |
| 
 | |
| 		return i;
 | |
| 	}
 | |
| 
 | |
| 	public void setGuiScale(int guiScale) {
 | |
| 		this.guiScale = guiScale;
 | |
| 		double d = guiScale;
 | |
| 		int i = (int)(this.framebufferWidth / d);
 | |
| 		this.guiScaledWidth = this.framebufferWidth / d > i ? i + 1 : i;
 | |
| 		int j = (int)(this.framebufferHeight / d);
 | |
| 		this.guiScaledHeight = this.framebufferHeight / d > j ? j + 1 : j;
 | |
| 	}
 | |
| 
 | |
| 	public void setTitle(String title) {
 | |
| 		GLFW.glfwSetWindowTitle(this.window, title);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Gets a pointer to the native window object that is passed to GLFW.
 | |
| 	 */
 | |
| 	public long getWindow() {
 | |
| 		return this.window;
 | |
| 	}
 | |
| 
 | |
| 	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 framebufferWidth) {
 | |
| 		this.framebufferWidth = framebufferWidth;
 | |
| 	}
 | |
| 
 | |
| 	public void setHeight(int framebufferHeight) {
 | |
| 		this.framebufferHeight = 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 int getGuiScale() {
 | |
| 		return this.guiScale;
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	public Monitor findBestMonitor() {
 | |
| 		return this.screenManager.findBestMonitor(this);
 | |
| 	}
 | |
| 
 | |
| 	public void updateRawMouseInput(boolean enableRawMouseMotion) {
 | |
| 		InputConstants.updateRawMouseInput(this.window, enableRawMouseMotion);
 | |
| 	}
 | |
| 
 | |
| 	public void setWindowCloseCallback(Runnable windowCloseCallback) {
 | |
| 		GLFWWindowCloseCallback gLFWWindowCloseCallback = GLFW.glfwSetWindowCloseCallback(this.window, l -> windowCloseCallback.run());
 | |
| 		if (gLFWWindowCloseCallback != null) {
 | |
| 			gLFWWindowCloseCallback.free();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public boolean isMinimized() {
 | |
| 		return this.minimized;
 | |
| 	}
 | |
| 
 | |
| 	@Environment(EnvType.CLIENT)
 | |
| 	public static class WindowInitFailed extends SilentInitException {
 | |
| 		WindowInitFailed(String string) {
 | |
| 			super(string);
 | |
| 		}
 | |
| 	}
 | |
| }
 |