GTK: Memory Settings
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/woodpecker Pipeline was successful Details

This commit is contained in:
Johannes Frohnmeyer 2023-01-29 12:39:46 +01:00
parent 5c9ce78ebf
commit eb9601d6cf
Signed by: Johannes
GPG Key ID: E76429612C2929F4
6 changed files with 156 additions and 24 deletions

View File

@ -5,8 +5,7 @@ import org.gtk.gtk.*;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.PropertyKey;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
import java.util.function.*;
public class IRow extends Box {
public IRow(@PropertyKey(resourceBundle = I18n.BUNDLE) String title, @PropertyKey(resourceBundle = I18n.BUNDLE) @Nullable String subtitle, Object... args) {
@ -30,51 +29,58 @@ public class IRow extends Box {
append(head);
}
public void setButton(@PropertyKey(resourceBundle = I18n.BUNDLE) String text, Button.Clicked action) {
firstChild.hexpand = true;
public Button setButton(@PropertyKey(resourceBundle = I18n.BUNDLE) String text, Button.Clicked action) {
Button btn = Button.newWithLabel(I18n.get(text));
btn.valign = Align.CENTER;
btn.halign = Align.END;
packSmallEnd(btn);
btn.onClicked(action);
append(btn);
return btn;
}
public DropDown setDropdown(String[] options, int defaultIndex, IntConsumer changed) {
firstChild.hexpand = true;
DropDown btn = new DropDown(new StringList(options), null);
btn.valign = Align.CENTER;
btn.halign = Align.END;
packSmallEnd(btn);
btn.selected = defaultIndex;
btn.onNotify("selected", pspec -> {
changed.accept(btn.selected);
});
btn.expression = new PropertyExpression(StringObject.type, null, "string");
append(btn);
return btn;
}
public Switch setCheckbox(boolean value, Consumer<Boolean> changed) {
firstChild.hexpand = true;
public Switch setSwitch(boolean value, Consumer<Boolean> changed) {
Switch btn = new Switch();
btn.valign = Align.CENTER;
btn.halign = Align.END;
packSmallEnd(btn);
btn.active = value;
btn.onStateSet(state -> {
changed.accept(state);
return false;
});
append(btn);
return btn;
}
public Entry setEntry(String value, Consumer<String> onChanged) {
public SpinButton setSpinButton(double value, double min, double max, double step, DoubleConsumer changed) {
SpinButton btn = SpinButton.newWithRange(min, max, step);
packSmallEnd(btn);
btn.value = value;
btn.onValueChanged(() -> changed.accept(btn.value));
return btn;
}
public Entry setEntry(String value, Consumer<String> changed) {
Entry entry = new Entry();
entry.text = value == null ? "" : value;
entry.hexpand = true;
entry.valign = Align.CENTER;
entry.halign = Align.FILL;
entry.onChanged(() -> onChanged.accept(entry.text));
entry.onChanged(() -> changed.accept(entry.text));
append(entry);
return entry;
}
private void packSmallEnd(Widget widget) {
firstChild.hexpand = true;
widget.valign = Align.CENTER;
widget.halign = Align.END;
append(widget);
}
}

View File

@ -0,0 +1,103 @@
package io.gitlab.jfronny.inceptum.gtk.util;
import io.gitlab.jfronny.commons.LazySupplier;
import io.gitlab.jfronny.commons.OSUtils;
import io.gitlab.jfronny.inceptum.common.Utils;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
public class Memory {
public static final long KB = 1024;
public static final long MB = KB * 1024;
public static final long GB = MB * 1024;
private static final MI impl = switch (OSUtils.TYPE) {
case LINUX -> new LinuxMI();
case WINDOWS -> new WindowsMI();
case MAC_OS -> new MacOsMI();
};
private static final LazySupplier<Long> totalMemory = new LazySupplier<>(impl::getTotalMemory);
public static long getMaxMBForInstance() {
return Math.max(totalMemory.get() / MB - 1024, 1024);
}
private interface MI {
long getTotalMemory();
}
private static class LinuxMI implements MI {
@Override
public long getTotalMemory() {
try (Stream<String> stream = Files.lines(Path.of("/proc/meminfo"))) {
var memTotal = stream
.filter(s -> s.startsWith("MemTotal:"))
.map(s -> s.substring("MemTotal:".length()))
.map(String::trim)
.findFirst();
if (memTotal.isPresent()) {
return parseDecimalMemorySizeToBinary(memTotal.get());
} else {
Utils.LOGGER.error("Could not find total memory");
return 32 * GB;
}
} catch (IOException e) {
Utils.LOGGER.error("Could not get total memory", e);
return 32 * GB;
}
}
// Taken from oshi
private static final Pattern BYTES_PATTERN = Pattern.compile("(\\d+) ?([kKMGT]?B?).*");
private static final Pattern WHITESPACES = Pattern.compile("\\s+");
private static long parseDecimalMemorySizeToBinary(String size) {
String[] mem = WHITESPACES.split(size);
if (mem.length < 2) {
// If no spaces, use regexp
Matcher matcher = BYTES_PATTERN.matcher(size.trim());
if (matcher.find() && matcher.groupCount() == 2) {
mem = new String[2];
mem[0] = matcher.group(1);
mem[1] = matcher.group(2);
}
}
long capacity = parseLongOrDefault(mem[0], 0L);
if (mem.length == 2 && mem[1].length() > 1) {
switch (mem[1].charAt(0)) {
case 'T' -> capacity <<= 40;
case 'G' -> capacity <<= 30;
case 'M' -> capacity <<= 20;
case 'K', 'k' -> capacity <<= 10;
default -> {}
}
}
return capacity;
}
private static long parseLongOrDefault(String s, long defaultLong) {
try {
return Long.parseLong(s);
} catch (NumberFormatException e) {
return defaultLong;
}
}
}
private static class WindowsMI implements MI {
@Override
public long getTotalMemory() {
return 32 * GB; // This is currently unsupported, but any implementations by Windows user using panama are welcome
}
}
private static class MacOsMI implements MI {
@Override
public long getTotalMemory() {
return 32 * GB; // This is currently unsupported, but any implementations by MacOS user using panama are welcome
}
}
}

View File

@ -23,7 +23,7 @@ public class LauncherSettingsWindow extends Window {
{
IRow row = new IRow("settings.snapshots", "settings.snapshots.subtitle");
listBox.append(row);
row.setCheckbox(InceptumConfig.snapshots, b -> {
row.setSwitch(InceptumConfig.snapshots, b -> {
InceptumConfig.snapshots = b;
InceptumConfig.saveConfig();
});

View File

@ -2,11 +2,11 @@ package io.gitlab.jfronny.inceptum.gtk.window.edit;
import io.github.jwharm.javagi.GErrorException;
import io.gitlab.jfronny.commons.ArgumentsTokenizer;
import io.gitlab.jfronny.commons.OSUtils;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.inceptum.common.*;
import io.gitlab.jfronny.inceptum.gtk.control.ILabel;
import io.gitlab.jfronny.inceptum.gtk.util.I18n;
import io.gitlab.jfronny.inceptum.gtk.util.Memory;
import io.gitlab.jfronny.inceptum.gtk.window.InstanceSettingsWindow;
import io.gitlab.jfronny.inceptum.launcher.api.FabricMetaApi;
import io.gitlab.jfronny.inceptum.launcher.api.McApi;
@ -78,7 +78,7 @@ public class GeneralTab extends SettingsTab {
var fabricRow = section.row("instance.settings.general.game.fabric.enabled", "instance.settings.general.game.fabric.enabled.subtitle");
var loaderRow = section.row("instance.settings.general.game.fabric.version", "instance.settings.general.game.fabric.version.subtitle");
loaderRow.visible = instance.isFabric;
ref.fabricEnabled = fabricRow.setCheckbox(instance.isFabric, bl -> {
ref.fabricEnabled = fabricRow.setSwitch(instance.isFabric, bl -> {
if (bl) {
if (ref.fabricVersions != null && ref.fabricVersions.length != 0 && ref.defaultFabric != null) {
instance.meta.gameVersion = GameVersionParser.createVersionWithFabric(instance.gameVersion, ref.defaultFabric);
@ -151,7 +151,22 @@ public class GeneralTab extends SettingsTab {
});
row.append(btn);
}
//TODO minMem/maxMem (slider?)
{
var row = section.row("instance.settings.general.game.memory.min", "instance.settings.general.game.memory.min.subtitle");
row.setSpinButton(instance.meta.minMem == null ? 512 : instance.meta.minMem / Memory.MB, 512, Memory.maxMBForInstance, 128, v -> {
instance.meta.minMem = (long) (v * Memory.MB);
if (instance.meta.minMem == Memory.GB / 2) instance.meta.minMem = null;
instance.writeMeta();
});
}
{
var row = section.row("instance.settings.general.game.memory.max", "instance.settings.general.game.memory.max.subtitle");
row.setSpinButton(instance.meta.maxMem == null ? 1024 : instance.meta.maxMem / Memory.MB, 1024, Memory.maxMBForInstance, 128, v -> {
instance.meta.maxMem = (long) (v * Memory.MB);
if (instance.meta.maxMem == Memory.GB) instance.meta.maxMem = null;
instance.writeMeta();
});
}
});
section("instance.settings.general.args", section -> {
if (instance.meta.arguments == null) instance.meta.arguments = new InstanceMeta.Arguments(List.of(), List.of(), List.of());

View File

@ -90,4 +90,8 @@ instance.settings.general.game.fabric.enabled.subtitle=Whether the Fabric Loader
instance.settings.general.game.fabric.version=Fabric Version
instance.settings.general.game.fabric.version.subtitle=Version of the Fabric Loader to use
instance.settings.general.game.java=Java
instance.settings.general.game.java.subtitle=The path of the custom Java binary to use (or empty for default)
instance.settings.general.game.java.subtitle=The path of the custom Java binary to use (or empty for default)
instance.settings.general.game.memory.min=Minimum Memory
instance.settings.general.game.memory.min.subtitle=The minimum amount of Memory Minecraft will allocate (in MiB)
instance.settings.general.game.memory.max=Maximum Memory
instance.settings.general.game.memory.max.subtitle=The maximum amount of Memory Minecraft will allocate (in MiB)

View File

@ -90,4 +90,8 @@ instance.settings.general.game.fabric.enabled.subtitle=Ob Fabric-Loader f
instance.settings.general.game.fabric.version=Fabric-Version
instance.settings.general.game.fabric.version.subtitle=Zu verwendende Version des Fabric-Loaders
instance.settings.general.game.java=Java
instance.settings.general.game.java.subtitle=Pfad der Java-Binärdatei, die diese Instanz verwenden soll. Leer lassen um die Standard-Binärdatei zu nutzen.
instance.settings.general.game.java.subtitle=Pfad der Java-Binärdatei, die diese Instanz verwenden soll. Leer lassen um die Standard-Binärdatei zu nutzen.
instance.settings.general.game.memory.min=Minimale Speichernutzung
instance.settings.general.game.memory.min.subtitle=Die minimale Speichernutzung dieser Instanz (in MiB)
instance.settings.general.game.memory.max=Maximale Speichernutzung
instance.settings.general.game.memory.max.subtitle=Die maximale Speichernutzung dieser Instanz (in MiB)