├── .gitattributes ├── GitHub ├── logo.png ├── logo.psd └── settings.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src └── main │ ├── resources │ ├── assets │ │ └── resolutioncontrol │ │ │ ├── icon.png │ │ │ ├── icon.psd │ │ │ ├── textures │ │ │ └── gui │ │ │ │ ├── settings.png │ │ │ │ └── settings.psd │ │ │ └── lang │ │ │ └── en_us.json │ ├── resolutioncontrol.mixins.json │ └── fabric.mod.json │ └── java │ └── resolutioncontrol │ ├── util │ ├── Config.java │ ├── KeyBindingHandler.java │ └── ConfigHandler.java │ ├── mixin │ ├── MinecraftClientMixin.java │ ├── GameRendererMixin.java │ ├── WorldRendererMixin.java │ └── WindowMixin.java │ ├── compat │ └── modmenu │ │ └── ModMenuInfo.java │ ├── client │ └── gui │ │ └── screen │ │ └── SettingsScreen.java │ └── ResolutionControlMod.java ├── settings.gradle ├── .gitignore ├── gradle.properties ├── README.md ├── LICENSE ├── gradlew.bat └── gradlew /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /GitHub/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliand665/Resolution-Control/HEAD/GitHub/logo.png -------------------------------------------------------------------------------- /GitHub/logo.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliand665/Resolution-Control/HEAD/GitHub/logo.psd -------------------------------------------------------------------------------- /GitHub/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliand665/Resolution-Control/HEAD/GitHub/settings.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliand665/Resolution-Control/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/assets/resolutioncontrol/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliand665/Resolution-Control/HEAD/src/main/resources/assets/resolutioncontrol/icon.png -------------------------------------------------------------------------------- /src/main/resources/assets/resolutioncontrol/icon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliand665/Resolution-Control/HEAD/src/main/resources/assets/resolutioncontrol/icon.psd -------------------------------------------------------------------------------- /src/main/resources/assets/resolutioncontrol/textures/gui/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliand665/Resolution-Control/HEAD/src/main/resources/assets/resolutioncontrol/textures/gui/settings.png -------------------------------------------------------------------------------- /src/main/resources/assets/resolutioncontrol/textures/gui/settings.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliand665/Resolution-Control/HEAD/src/main/resources/assets/resolutioncontrol/textures/gui/settings.psd -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | jcenter() 4 | maven { 5 | name = 'Fabric' 6 | url = 'https://maven.fabricmc.net/' 7 | } 8 | gradlePluginPortal() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/resources/assets/resolutioncontrol/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "key.resolutioncontrol.settings": "Resolution Control Settings", 3 | "screen.resolutioncontrol.settings.title": "Resolution Control Settings", 4 | "screen.resolutioncontrol.settings.current": "Current Scale Factor: %sx" 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # idea 9 | 10 | .idea/ 11 | *.iml 12 | *.ipr 13 | *.iws 14 | 15 | # vscode 16 | 17 | .settings/ 18 | .vscode/ 19 | bin/ 20 | .classpath 21 | .project 22 | 23 | # fabric 24 | 25 | run/ 26 | 27 | # custom stuff 28 | 29 | decompiled/ 30 | -------------------------------------------------------------------------------- /src/main/java/resolutioncontrol/util/Config.java: -------------------------------------------------------------------------------- 1 | package resolutioncontrol.util; 2 | 3 | public final class Config { 4 | public static Config getInstance() { 5 | return ConfigHandler.instance.getConfig(); 6 | } 7 | 8 | public static int getScaleFactor() { 9 | return getInstance().scaleFactor; 10 | } 11 | 12 | public int scaleFactor = 1; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/resources/resolutioncontrol.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "package": "resolutioncontrol.mixin", 4 | "compatibilityLevel": "JAVA_8", 5 | "mixins": [ 6 | ], 7 | "client": [ 8 | "WindowMixin", 9 | "GameRendererMixin", 10 | "MinecraftClientMixin", 11 | "WorldRendererMixin" 12 | ], 13 | "injectors": { 14 | "defaultRequire": 1 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx1G 3 | 4 | # Fabric Properties 5 | # check these on https://fabricmc.net/use 6 | minecraft_version=1.16.4 7 | yarn_mappings=1.16.4+build.7 8 | loader_version=0.10.8 9 | 10 | # Mod Properties 11 | mod_version = 1.0.4 12 | maven_group = juliand665 13 | archives_base_name = resolution-control 14 | 15 | # Dependencies 16 | # https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api/ 17 | fabric_version=0.27.1+1.16 18 | 19 | developer_mode_version=1.0.15 20 | mod_menu_version=1.14.13+build.19 21 | -------------------------------------------------------------------------------- /src/main/java/resolutioncontrol/mixin/MinecraftClientMixin.java: -------------------------------------------------------------------------------- 1 | package resolutioncontrol.mixin; 2 | 3 | import net.minecraft.client.MinecraftClient; 4 | import net.minecraft.client.gl.Framebuffer; 5 | import org.spongepowered.asm.mixin.*; 6 | import resolutioncontrol.ResolutionControlMod; 7 | 8 | @Mixin(MinecraftClient.class) 9 | public abstract class MinecraftClientMixin implements ResolutionControlMod.MutableMinecraftClient { 10 | @Mutable 11 | @Final 12 | @Shadow 13 | private Framebuffer framebuffer; 14 | 15 | @Override 16 | public void setFramebuffer(Framebuffer framebuffer) { 17 | this.framebuffer = framebuffer; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/resolutioncontrol/compat/modmenu/ModMenuInfo.java: -------------------------------------------------------------------------------- 1 | package resolutioncontrol.compat.modmenu; 2 | 3 | import io.github.prospector.modmenu.api.ModMenuApi; 4 | import net.minecraft.client.gui.screen.Screen; 5 | import resolutioncontrol.ResolutionControlMod; 6 | import resolutioncontrol.client.gui.screen.SettingsScreen; 7 | 8 | import java.util.function.Function; 9 | 10 | public final class ModMenuInfo implements ModMenuApi { 11 | @Override 12 | public String getModId() { 13 | return ResolutionControlMod.MOD_ID; 14 | } 15 | 16 | @Override 17 | public Function getConfigScreenFactory() { 18 | return SettingsScreen::new; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/resolutioncontrol/util/KeyBindingHandler.java: -------------------------------------------------------------------------------- 1 | package resolutioncontrol.util; 2 | 3 | import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding; 4 | import net.fabricmc.fabric.api.event.client.ClientTickCallback; 5 | import net.minecraft.client.MinecraftClient; 6 | 7 | public abstract class KeyBindingHandler implements ClientTickCallback { 8 | private final FabricKeyBinding keyBinding; 9 | private boolean isHoldingKey = false; 10 | 11 | protected KeyBindingHandler(FabricKeyBinding keyBinding) { 12 | this.keyBinding = keyBinding; 13 | } 14 | 15 | @Override 16 | public final void tick(MinecraftClient e) { 17 | if (keyBinding.isPressed()) { 18 | if (!isHoldingKey) { 19 | handlePress(); 20 | } 21 | isHoldingKey = true; 22 | } else { 23 | isHoldingKey = false; 24 | } 25 | } 26 | 27 | public abstract void handlePress(); 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/resolutioncontrol/mixin/GameRendererMixin.java: -------------------------------------------------------------------------------- 1 | package resolutioncontrol.mixin; 2 | 3 | import net.minecraft.client.render.GameRenderer; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.Inject; 7 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 8 | import resolutioncontrol.ResolutionControlMod; 9 | 10 | @Mixin(GameRenderer.class) 11 | public abstract class GameRendererMixin { 12 | @Inject(at = @At("HEAD"), method = "renderWorld") 13 | private void onRenderWorldBegin(CallbackInfo callbackInfo) { 14 | ResolutionControlMod.getInstance().setShouldScale(true); 15 | } 16 | 17 | @Inject(at = @At("RETURN"), method = "renderWorld") 18 | private void onRenderWorldEnd(CallbackInfo callbackInfo) { 19 | ResolutionControlMod.getInstance().setShouldScale(false); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "resolutioncontrol", 4 | "version": "${version}", 5 | 6 | "name": "Resolution Control", 7 | "description": "Gives you more control over minecraft's render resolution.", 8 | "authors": [ 9 | "juliand665" 10 | ], 11 | "contact": { 12 | "homepage": "https://dapprgames.com", 13 | "sources": "https://github.com/juliand665/Resolution-Control", 14 | "issues": "https://github.com/juliand665/Resolution-Control/issues" 15 | }, 16 | 17 | "license": "MIT", 18 | "icon": "assets/resolutioncontrol/icon.png", 19 | 20 | "environment": "client", 21 | "entrypoints": { 22 | "main": [ 23 | "resolutioncontrol.ResolutionControlMod" 24 | ], 25 | "modmenu": [ 26 | "resolutioncontrol.compat.modmenu.ModMenuInfo" 27 | ] 28 | }, 29 | "mixins": [ 30 | "resolutioncontrol.mixins.json" 31 | ], 32 | 33 | "depends": { 34 | "fabricloader": ">=0.4.0", 35 | "fabric": "*" 36 | }, 37 | "suggests": { 38 | "flamingo": "*" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | # Resolution Control 6 | 7 | Resolution Control allows you to render Minecraft's 3D portion (i.e. the main game, but not the HUD/GUIs/etc.) at a lower resolution, using nearest-neighbor upsampling. It even has a snazzy settings screen (open by pressing `P` by default) allowing you to adjust this as you go. 8 | 9 | Download in [the releases section](https://github.com/juliand665/Resolution-Control/releases) or on [CurseForge](https://www.curseforge.com/minecraft/mc-mods/resolution-control). 10 | 11 | ![settings](GitHub/settings.png) 12 | 13 | --- 14 | 15 | I created this mod mainly because Minecraft 1.13 made it render at full- rather than half-resolution on retina monitors, causing it to run really badly on my computer. Any way of downscaling I tried resulted in linear upsampling (rather than the nearest-neighbor upsampling Minecraft used to have), which looked blurry and terrible. Enter **Resolution Control**, the answer to your prayers! 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Julian Dunskus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/java/resolutioncontrol/mixin/WorldRendererMixin.java: -------------------------------------------------------------------------------- 1 | package resolutioncontrol.mixin; 2 | 3 | import net.minecraft.client.gl.Framebuffer; 4 | import net.minecraft.client.render.WorldRenderer; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Shadow; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 10 | import resolutioncontrol.ResolutionControlMod; 11 | 12 | @Mixin(WorldRenderer.class) 13 | public abstract class WorldRendererMixin { 14 | @Shadow 15 | private Framebuffer entityOutlinesFramebuffer; 16 | 17 | @Inject(at = @At("RETURN"), method = "loadEntityOutlineShader") 18 | private void onLoadEntityOutlineShader(CallbackInfo callbackInfo) { 19 | ResolutionControlMod.getInstance().resize(entityOutlinesFramebuffer); 20 | } 21 | 22 | @Inject(at = @At("RETURN"), method = "onResized") 23 | private void onOnResized(CallbackInfo callbackInfo) { 24 | if (entityOutlinesFramebuffer == null) return; 25 | ResolutionControlMod.getInstance().resize(entityOutlinesFramebuffer); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/resolutioncontrol/util/ConfigHandler.java: -------------------------------------------------------------------------------- 1 | package resolutioncontrol.util; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import net.minecraft.client.MinecraftClient; 6 | import resolutioncontrol.ResolutionControlMod; 7 | 8 | import java.io.*; 9 | 10 | public final class ConfigHandler { 11 | public static final ConfigHandler instance = new ConfigHandler(); 12 | 13 | private static File configFile() { 14 | return new File(MinecraftClient.getInstance().runDirectory, "config/" + ResolutionControlMod.MOD_ID + ".json"); 15 | } 16 | 17 | private final Gson gson = new GsonBuilder().setPrettyPrinting().create(); 18 | private Config config = new Config(); 19 | 20 | public Config getConfig() { 21 | return config; 22 | } 23 | 24 | private ConfigHandler() { 25 | loadConfig(); 26 | } 27 | 28 | public void loadConfig() { 29 | File configFile = configFile(); 30 | if (!configFile.exists()) { 31 | config = new Config(); 32 | saveConfig(); 33 | } 34 | 35 | try (FileReader reader = new FileReader(configFile)) { 36 | config = gson.fromJson(reader, Config.class); 37 | } catch (IOException e) { 38 | System.err.println("Could not load config file at " + configFile.getAbsolutePath()); 39 | e.printStackTrace(); 40 | } 41 | } 42 | 43 | public void saveConfig() { 44 | File configFile = configFile(); 45 | configFile.getParentFile().mkdirs(); 46 | 47 | try (final Writer writer = new FileWriter(configFile)) { 48 | writer.write(gson.toJson(config)); 49 | } catch (IOException e) { 50 | System.err.println("Could not save config file at " + configFile.getAbsolutePath()); 51 | e.printStackTrace(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/resolutioncontrol/mixin/WindowMixin.java: -------------------------------------------------------------------------------- 1 | package resolutioncontrol.mixin; 2 | 3 | import net.minecraft.client.util.Window; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.Inject; 7 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 8 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 9 | import resolutioncontrol.ResolutionControlMod; 10 | 11 | @Mixin(Window.class) 12 | public abstract class WindowMixin { 13 | @Inject(at = @At("RETURN"), method = "getFramebufferWidth", cancellable = true) 14 | private void getFramebufferWidth(CallbackInfoReturnable callbackInfo) { 15 | callbackInfo.setReturnValue(scale(callbackInfo.getReturnValueI())); 16 | } 17 | 18 | @Inject(at = @At("RETURN"), method = "getFramebufferHeight", cancellable = true) 19 | private void getFramebufferHeight(CallbackInfoReturnable callbackInfo) { 20 | callbackInfo.setReturnValue(scale(callbackInfo.getReturnValueI())); 21 | } 22 | 23 | private int scale(int value) { 24 | int scaleFactor = ResolutionControlMod.getInstance().getCurrentScaleFactor(); 25 | return (int) Math.ceil(1d * value / scaleFactor); 26 | } 27 | 28 | @Inject(at = @At("RETURN"), method = "getScaleFactor", cancellable = true) 29 | private void getScaleFactor(CallbackInfoReturnable callbackInfo) { 30 | callbackInfo.setReturnValue(callbackInfo.getReturnValueD() / ResolutionControlMod.getInstance().getCurrentScaleFactor()); 31 | } 32 | 33 | @Inject(at = @At("RETURN"), method = "onFramebufferSizeChanged") 34 | private void onFramebufferSizeChanged(CallbackInfo callbackInfo) { 35 | ResolutionControlMod.getInstance().onResolutionChanged(); 36 | } 37 | 38 | @Inject(at = @At("RETURN"), method = "updateFramebufferSize") 39 | private void onUpdateFramebufferSize(CallbackInfo callbackInfo) { 40 | ResolutionControlMod.getInstance().onResolutionChanged(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /src/main/java/resolutioncontrol/client/gui/screen/SettingsScreen.java: -------------------------------------------------------------------------------- 1 | package resolutioncontrol.client.gui.screen; 2 | 3 | import com.mojang.blaze3d.platform.GlStateManager; 4 | import net.minecraft.client.MinecraftClient; 5 | import net.minecraft.client.gui.screen.Screen; 6 | import net.minecraft.client.gui.widget.ButtonWidget; 7 | import net.minecraft.client.resource.language.I18n; 8 | import net.minecraft.client.util.math.MatrixStack; 9 | import net.minecraft.text.LiteralText; 10 | import net.minecraft.text.Text; 11 | import net.minecraft.text.TranslatableText; 12 | import net.minecraft.util.Identifier; 13 | import org.jetbrains.annotations.Nullable; 14 | import resolutioncontrol.ResolutionControlMod; 15 | 16 | 17 | public final class SettingsScreen extends Screen { 18 | private static final Identifier backgroundTexture = ResolutionControlMod.identifier("textures/gui/settings.png"); 19 | 20 | private static Text text(String path, Object... args) { 21 | return new TranslatableText("screen." + ResolutionControlMod.MOD_ID + ".settings." + path, args); 22 | } 23 | 24 | private final int containerWidth = 192; 25 | private final int containerHeight = 128; 26 | 27 | private final ResolutionControlMod mod = ResolutionControlMod.getInstance(); 28 | 29 | @Nullable 30 | private final Screen parent; 31 | 32 | private ButtonWidget increaseButton; 33 | private ButtonWidget decreaseButton; 34 | private ButtonWidget doneButton; 35 | 36 | private int centerX; 37 | private int centerY; 38 | private int startX; 39 | private int startY; 40 | 41 | public SettingsScreen(Screen parent) { 42 | super(text("title")); 43 | 44 | this.parent = parent; 45 | } 46 | 47 | public SettingsScreen() { 48 | this(MinecraftClient.getInstance().currentScreen); 49 | } 50 | 51 | @Override 52 | protected void init() { 53 | super.init(); 54 | 55 | centerX = width / 2; 56 | centerY = height / 2; 57 | startX = centerX - containerWidth / 2; 58 | startY = centerY - containerHeight / 2; 59 | 60 | int buttonSize = 20; 61 | int buttonOffset = buttonSize / 2; 62 | int buttonY = centerY + 5 - buttonSize / 2; 63 | 64 | decreaseButton = new ButtonWidget( 65 | centerX - buttonOffset - buttonSize / 2, buttonY, 66 | buttonSize, buttonSize, 67 | new LiteralText("-"), 68 | button -> changeScaleFactor(-1)); 69 | addButton(decreaseButton); 70 | 71 | increaseButton = new ButtonWidget( 72 | centerX + buttonOffset - buttonSize / 2, buttonY, 73 | buttonSize, buttonSize, 74 | new LiteralText("+"), 75 | button -> changeScaleFactor(+1) 76 | ); 77 | addButton(increaseButton); 78 | 79 | doneButton = new ButtonWidget( 80 | centerX - 100 / 2, startY + containerHeight - 10 - 20, 81 | 100, buttonSize, 82 | new TranslatableText("gui.done"), 83 | button -> client.openScreen(parent) 84 | ); 85 | addButton(doneButton); 86 | 87 | updateButtons(); 88 | } 89 | 90 | @Override 91 | public void render(MatrixStack matrices, int mouseX, int mouseY, float time) { 92 | assert client != null; 93 | 94 | if (client.world == null) { 95 | renderBackgroundTexture(0); 96 | } 97 | 98 | GlStateManager.enableAlphaTest(); 99 | client.getTextureManager().bindTexture(backgroundTexture); 100 | GlStateManager.color4f(1, 1, 1, 1); 101 | 102 | int textureWidth = 256; 103 | int textureHeight = 192; 104 | drawTexture( 105 | matrices, 106 | centerX - textureWidth / 2, centerY - textureHeight / 2, 107 | 0, 0, 108 | textureWidth, textureHeight 109 | ); 110 | 111 | drawCenteredString(matrices, getTitle().getString(), centerX, startY + 10, 0x404040); 112 | 113 | Text scaleFactor = text("current", mod.getScaleFactor()); 114 | drawCenteredString(matrices, scaleFactor.getString(), centerX, centerY - 20, 0x000000); 115 | 116 | super.render(matrices, mouseX, mouseY, time); // buttons 117 | } 118 | 119 | private void drawCenteredString(MatrixStack matrices, String text, int x, int y, int color) { 120 | textRenderer.draw(matrices, text, x - textRenderer.getWidth(text) / 2, y, color); 121 | } 122 | 123 | private void changeScaleFactor(int change) { 124 | int scaleFactor = mod.getScaleFactor() + change; 125 | 126 | if (scaleFactor < 1) { 127 | scaleFactor = 1; 128 | } 129 | mod.setScaleFactor(scaleFactor); 130 | 131 | updateButtons(); 132 | } 133 | 134 | private void updateButtons() { 135 | decreaseButton.active = mod.getScaleFactor() > 1; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/resolutioncontrol/ResolutionControlMod.java: -------------------------------------------------------------------------------- 1 | package resolutioncontrol; 2 | 3 | import net.fabricmc.api.ModInitializer; 4 | import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding; 5 | import net.fabricmc.fabric.api.client.keybinding.KeyBindingRegistry; 6 | import net.fabricmc.fabric.api.event.client.ClientTickCallback; 7 | import net.minecraft.client.MinecraftClient; 8 | import net.minecraft.client.gl.Framebuffer; 9 | import net.minecraft.client.util.InputUtil; 10 | import net.minecraft.client.util.Window; 11 | import net.minecraft.util.Identifier; 12 | import org.jetbrains.annotations.Nullable; 13 | import org.lwjgl.glfw.GLFW; 14 | import resolutioncontrol.client.gui.screen.SettingsScreen; 15 | import resolutioncontrol.util.*; 16 | 17 | public class ResolutionControlMod implements ModInitializer { 18 | public static final String MOD_ID = "resolutioncontrol"; 19 | 20 | public static Identifier identifier(String path) { 21 | return new Identifier(MOD_ID, path); 22 | } 23 | 24 | private static MinecraftClient client = MinecraftClient.getInstance(); 25 | 26 | private static ResolutionControlMod instance; 27 | 28 | public static ResolutionControlMod getInstance() { 29 | return instance; 30 | } 31 | 32 | private static FabricKeyBinding settingsKeyBinding = FabricKeyBinding.Builder.create( 33 | identifier("settings"), 34 | InputUtil.Type.KEYSYM, 35 | GLFW.GLFW_KEY_O, 36 | "key.categories.misc" 37 | ).build(); 38 | 39 | private boolean shouldScale = false; 40 | 41 | @Nullable 42 | private Framebuffer framebuffer; 43 | 44 | @Nullable 45 | private Framebuffer clientFramebuffer; 46 | 47 | @Override 48 | public void onInitialize() { 49 | // This code runs as soon as Minecraft is in a mod-load-ready state. 50 | // However, some things (like resources) may still be uninitialized. 51 | // Proceed with mild caution. 52 | instance = this; 53 | 54 | KeyBindingRegistry.INSTANCE.register(settingsKeyBinding); 55 | 56 | ClientTickCallback.EVENT.register(new KeyBindingHandler(settingsKeyBinding) { 57 | @Override 58 | public void handlePress() { 59 | client.openScreen(new SettingsScreen()); 60 | } 61 | }); 62 | } 63 | 64 | public void setShouldScale(boolean shouldScale) { 65 | if (shouldScale == this.shouldScale) return; 66 | 67 | if (getScaleFactor() == 1) return; 68 | 69 | Window window = getWindow(); 70 | if (framebuffer == null) { 71 | this.shouldScale = true; // so we get the right dimensions 72 | framebuffer = new Framebuffer( 73 | window.getFramebufferWidth(), 74 | window.getFramebufferHeight(), 75 | true, 76 | MinecraftClient.IS_SYSTEM_MAC 77 | ); 78 | } 79 | 80 | this.shouldScale = shouldScale; 81 | 82 | client.getProfiler().swap(shouldScale ? "startScaling" : "finishScaling"); 83 | 84 | // swap out framebuffers as needed 85 | boolean shouldUpdateViewport = true; 86 | if (shouldScale) { 87 | clientFramebuffer = client.getFramebuffer(); 88 | setClientFramebuffer(framebuffer); 89 | framebuffer.beginWrite(shouldUpdateViewport); 90 | // nothing on the client's framebuffer yet 91 | } else { 92 | setClientFramebuffer(clientFramebuffer); 93 | client.getFramebuffer().beginWrite(shouldUpdateViewport); 94 | framebuffer.draw( 95 | window.getFramebufferWidth(), 96 | window.getFramebufferHeight() 97 | ); 98 | } 99 | 100 | client.getProfiler().swap("level"); 101 | } 102 | 103 | public int getScaleFactor() { 104 | return Config.getScaleFactor(); 105 | } 106 | 107 | public void setScaleFactor(int scaleFactor) { 108 | if (scaleFactor == Config.getScaleFactor()) return; 109 | 110 | Config.getInstance().scaleFactor = scaleFactor; 111 | 112 | updateFramebufferSize(); 113 | 114 | ConfigHandler.instance.saveConfig(); 115 | } 116 | 117 | public int getCurrentScaleFactor() { 118 | return shouldScale ? Config.getScaleFactor() : 1; 119 | } 120 | 121 | public void onResolutionChanged() { 122 | updateFramebufferSize(); 123 | } 124 | 125 | private void updateFramebufferSize() { 126 | if (framebuffer == null) return; 127 | 128 | if (getScaleFactor() != 1) { 129 | // resize if not unused 130 | resize(framebuffer); 131 | } 132 | 133 | resize(client.worldRenderer.getEntityOutlinesFramebuffer()); 134 | } 135 | 136 | public void resize(Framebuffer framebuffer) { 137 | boolean prev = shouldScale; 138 | shouldScale = true; 139 | framebuffer.resize( 140 | getWindow().getFramebufferWidth(), 141 | getWindow().getFramebufferHeight(), 142 | MinecraftClient.IS_SYSTEM_MAC 143 | ); 144 | shouldScale = prev; 145 | } 146 | 147 | private Window getWindow() { 148 | return client.getWindow(); 149 | } 150 | 151 | private void setClientFramebuffer(Framebuffer framebuffer) { 152 | ((MutableMinecraftClient) client).setFramebuffer(framebuffer); 153 | } 154 | 155 | public interface MutableMinecraftClient { 156 | void setFramebuffer(Framebuffer framebuffer); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | --------------------------------------------------------------------------------