diff --git a/build.gradle.kts b/build.gradle.kts index ee33813..7586e7b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,8 +12,7 @@ allprojects { val lwjglVersion by extra("3.3.1") val imguiVersion by extra("1.86.4") val jfCommonsVersion by extra("1.0-SNAPSHOT") -val gsonCompileVersion by extra("1.1-SNAPSHOT") -val manifoldVersion by extra("2022.1.27") +val gsonCompileVersion by extra("1.2-SNAPSHOT") val jlhttpVersion by extra("2.6") val flavorProp: String by extra(prop("flavor", "custom")) diff --git a/buildSrc/src/main/kotlin/inceptum.application-standalone-conventions.gradle.kts b/buildSrc/src/main/kotlin/inceptum.application-standalone.gradle.kts similarity index 93% rename from buildSrc/src/main/kotlin/inceptum.application-standalone-conventions.gradle.kts rename to buildSrc/src/main/kotlin/inceptum.application-standalone.gradle.kts index 0a671b3..b983f76 100644 --- a/buildSrc/src/main/kotlin/inceptum.application-standalone-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/inceptum.application-standalone.gradle.kts @@ -3,7 +3,7 @@ import de.undercouch.gradle.tasks.download.Download plugins { application - id("inceptum.java-conventions") + id("inceptum.java") id("com.github.johnrengelman.shadow") id("de.undercouch.download") } @@ -43,4 +43,8 @@ val nativeExe by tasks.registering(FileOutput::class) { if (rootProject.extra["flavor"] == "windows") { tasks.build.get().dependsOn(nativeExe) +} + +tasks.runShadow { + workingDir = rootProject.projectDir } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/inceptum.application-conventions.gradle.kts b/buildSrc/src/main/kotlin/inceptum.application.gradle.kts similarity index 67% rename from buildSrc/src/main/kotlin/inceptum.application-conventions.gradle.kts rename to buildSrc/src/main/kotlin/inceptum.application.gradle.kts index 14d891e..5aacf60 100644 --- a/buildSrc/src/main/kotlin/inceptum.application-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/inceptum.application.gradle.kts @@ -1,6 +1,6 @@ plugins { application - id("inceptum.java-conventions") + id("inceptum.java") } publishing { @@ -9,4 +9,6 @@ publishing { from(components["java"]) } } -} \ No newline at end of file +} + +tasks.run.get().workingDir = rootProject.projectDir \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/inceptum.gson-compile.gradle.kts b/buildSrc/src/main/kotlin/inceptum.gson-compile.gradle.kts index 12f1dfa..8076362 100644 --- a/buildSrc/src/main/kotlin/inceptum.gson-compile.gradle.kts +++ b/buildSrc/src/main/kotlin/inceptum.gson-compile.gradle.kts @@ -1,7 +1,7 @@ import org.gradle.kotlin.dsl.extra plugins { - id("inceptum.library-conventions") + id("inceptum.library") } dependencies { diff --git a/buildSrc/src/main/kotlin/inceptum.java-conventions.gradle.kts b/buildSrc/src/main/kotlin/inceptum.java.gradle.kts similarity index 100% rename from buildSrc/src/main/kotlin/inceptum.java-conventions.gradle.kts rename to buildSrc/src/main/kotlin/inceptum.java.gradle.kts diff --git a/buildSrc/src/main/kotlin/inceptum.library-conventions.gradle.kts b/buildSrc/src/main/kotlin/inceptum.library.gradle.kts similarity index 80% rename from buildSrc/src/main/kotlin/inceptum.library-conventions.gradle.kts rename to buildSrc/src/main/kotlin/inceptum.library.gradle.kts index 076fea0..7a9a04d 100644 --- a/buildSrc/src/main/kotlin/inceptum.library-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/inceptum.library.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("inceptum.java-conventions") + id("inceptum.java") } publishing { diff --git a/buildSrc/src/main/kotlin/inceptum.manifold.gradle.kts b/buildSrc/src/main/kotlin/inceptum.manifold.gradle.kts index 846e3b4..6846553 100644 --- a/buildSrc/src/main/kotlin/inceptum.manifold.gradle.kts +++ b/buildSrc/src/main/kotlin/inceptum.manifold.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("inceptum.java-conventions") + id("inceptum.java") id("jf.manifold") } diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 601468a..f4eb463 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -2,7 +2,7 @@ import io.gitlab.jfronny.scripts.* import javax.lang.model.element.Modifier plugins { - id("inceptum.library-conventions") + id("inceptum.library") id("jf.codegen") id("inceptum.gson-compile") id("inceptum.manifold") diff --git a/common/src/main/java/io/gitlab/jfronny/inceptum/common/InceptumConfig.java b/common/src/main/java/io/gitlab/jfronny/inceptum/common/InceptumConfig.java index 53b704c..bb983d1 100644 --- a/common/src/main/java/io/gitlab/jfronny/inceptum/common/InceptumConfig.java +++ b/common/src/main/java/io/gitlab/jfronny/inceptum/common/InceptumConfig.java @@ -1,5 +1,8 @@ package io.gitlab.jfronny.inceptum.common; +import gsoncompile.extensions.io.gitlab.jfronny.inceptum.common.InceptumConfig.GC_InceptumConfig; +import io.gitlab.jfronny.gson.compile.annotations.GComment; +import io.gitlab.jfronny.gson.compile.annotations.GSerializable; import io.gitlab.jfronny.gson.stream.*; import io.gitlab.jfronny.inceptum.common.model.inceptum.UpdateChannel; @@ -7,14 +10,23 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +@GSerializable(configure = GsonPreset.Config.class, isStatic = true) public class InceptumConfig { + @GComment("Whether to show snapshots in the version selector for new instances") public static boolean snapshots = false; + @GComment("Whether to launch the GUI in dark mode\nConfigurable in Settings->Dark Theme") public static boolean darkTheme = false; + @GComment("Whether the GTK UI should default to a list view instead of a grid") public static boolean listView = false; + @GComment("Whether to require an account to launch the game\nIntended to allow running the game from USB sticks on constrained networks") public static boolean enforceAccount = true; + @GComment("The currently selected account\nUsed to launch the game") public static String lastAccount = null; + @GComment("The last name used for an offline session") public static String offlineAccountLastName = null; + @GComment("The update channel. Either \"CI\" or \"Stable\"\nI personally recommend the CI channel as it gets the latest fixes and features quicker") public static UpdateChannel channel = UpdateChannel.Stable; + @GComment("The author name to add to packs where the metadata format requires specifying one") public static String authorName = "Inceptum"; public static void load() throws IOException { @@ -31,78 +43,14 @@ public class InceptumConfig { saveConfig(); } } - try (JsonReader jr = new JsonReader(Files.newBufferedReader(MetaHolder.CONFIG_PATH))) { - GsonPreset.Config.configure(jr); - jr.beginObject(); - while (jr.peek() != JsonToken.END_OBJECT) { - String name = null; - try { - name = jr.nextName(); - switch (name) { - case "snapshots" -> snapshots = jr.nextBoolean(); - case "darkTheme" -> darkTheme = jr.nextBoolean(); - case "listView" -> listView = jr.nextBoolean(); - case "enforceAccount" -> enforceAccount = jr.nextBoolean(); - case "lastAccount" -> lastAccount = nullableString(jr); - case "offlineAccountLastName" -> offlineAccountLastName = nullableString(jr); - case "channel" -> { - try { - channel = UpdateChannel.valueOf(jr.nextString()); - } catch (IllegalArgumentException e) { - Utils.LOGGER.error("Could not read channel", e); - } - } - case "authorName" -> authorName = jr.nextString(); - default -> { - Utils.LOGGER.error("Unexpected entry name: " + name); - jr.skipValue(); - } - } - } catch (Throwable t) { - if (name == null) Utils.LOGGER.error("Could not read config entry", t); - else Utils.LOGGER.error("Could not read config entry: " + name, t); - return; - } - } - jr.endObject(); - } + GC_InceptumConfig.read(MetaHolder.CONFIG_PATH); } public static void saveConfig() { - try (JsonWriter jw = new JsonWriter(Files.newBufferedWriter(MetaHolder.CONFIG_PATH))) { - GsonPreset.Config.configure(jw); - jw.beginObject() - .comment("Whether to show snapshots in the version selector for new instances") - .name("snapshots").value(snapshots) - .comment("Whether to launch the GUI in dark mode") - .comment("Configurable in Settings->Dark Theme") - .name("darkTheme").value(darkTheme) - .comment("Whether the GTK UI should default to a list view instead of a grid") - .name("listView").value(listView) - .comment("Whether to require an account to launch the game") - .comment("Intended to allow running the game from USB sticks on constrained networks") - .name("enforceAccount").value(enforceAccount) - .comment("The currently selected account") - .comment("Used to launch the game") - .name("lastAccount").value(lastAccount) - .comment("The last name used for an offline session") - .name("offlineAccountLastName").value(offlineAccountLastName) - .comment("The update channel. Either \"CI\" or \"Stable\"") - .comment("I personally recommend the CI channel as it gets the latest fixes and features quicker") - .name("channel").value(channel.toString()) - .comment("The author name to add to packs where the metadata format requires specifying one") - .name("authorName").value(authorName) - .endObject(); + try { + GC_InceptumConfig.write(MetaHolder.CONFIG_PATH); } catch (IOException e) { Utils.LOGGER.error("Could not save config", e); } } - - private static String nullableString(JsonReader jr) throws IOException { - if (jr.peek() == JsonToken.NULL) { - jr.nextNull(); - return null; - } - return jr.nextString(); - } } diff --git a/launcher-cli/build.gradle.kts b/launcher-cli/build.gradle.kts index ccf3058..cc57ee7 100644 --- a/launcher-cli/build.gradle.kts +++ b/launcher-cli/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("inceptum.application-conventions") + id("inceptum.application") id("inceptum.manifold") } diff --git a/launcher-dist/build.gradle.kts b/launcher-dist/build.gradle.kts index a9b2bc4..962ea50 100644 --- a/launcher-dist/build.gradle.kts +++ b/launcher-dist/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("inceptum.application-standalone-conventions") + id("inceptum.application-standalone") } application { diff --git a/launcher-gtk/build.gradle.kts b/launcher-gtk/build.gradle.kts index 378a794..50a08f4 100644 --- a/launcher-gtk/build.gradle.kts +++ b/launcher-gtk/build.gradle.kts @@ -1,6 +1,7 @@ plugins { - id("inceptum.application-conventions") + id("inceptum.application") id("com.github.johnrengelman.shadow") + id("jf.manifold") } application { @@ -8,12 +9,21 @@ application { } repositories { - maven { url = uri("https://jitpack.io") } + mavenLocal() + maven { url = uri("https://maven.frohnmeyer-wds.de/java-gi") } } dependencies { - implementation("com.github.bailuk:java-gtk:0.2") + implementation("io.github.jwharm.javagi:gtk4:0.3-SNAPSHOT") + implementation("io.github.jwharm.javagi:glib:0.3-SNAPSHOT") implementation(project(":launcher")) } -tasks.runShadow.get().workingDir = rootProject.projectDir \ No newline at end of file +tasks.compileJava { + options.compilerArgs.add("--enable-preview") +} + +tasks.runShadow { + workingDir = rootProject.projectDir + jvmArgs!!.addAll(listOf("--enable-preview", "--enable-native-access=ALL-UNNAMED")) +} diff --git a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/GtkEnvBackend.java b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/GtkEnvBackend.java index 89fed20..f6e5217 100644 --- a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/GtkEnvBackend.java +++ b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/GtkEnvBackend.java @@ -1,8 +1,6 @@ package io.gitlab.jfronny.inceptum.gtk; -import ch.bailu.gtk.GTK; -import ch.bailu.gtk.gtk.*; -import ch.bailu.gtk.type.Str; +import org.gtk.gtk.*; import io.gitlab.jfronny.commons.StringFormatter; import io.gitlab.jfronny.inceptum.common.Utils; import io.gitlab.jfronny.inceptum.gtk.util.I18n; @@ -46,19 +44,19 @@ public enum GtkEnvBackend implements LauncherEnv.EnvBackend { //TODO test //TODO run on main thread GtkMain.schedule(() -> { Dialog dialog = new Dialog(); - if (dialogParent != null) dialog.setTransientFor(dialogParent); - dialog.setModal(GTK.TRUE); - if (dialogParent != null) dialog.setDestroyWithParent(GTK.TRUE); - dialog.setTitle(new Str(prompt)); - Box box = dialog.getContentArea(); - box.append(new Label(new Str(details))); + if (dialogParent != null) dialog.transientFor = dialogParent; + dialog.modal = true; + if (dialogParent != null) dialog.destroyWithParent = true; + dialog.title = prompt; + Box box = dialog.contentArea; + box.append(new Label(details)); Entry entry = new Entry(); - Editable entryEditable = new Editable(entry.cast()); - entryEditable.setText(new Str(defaultValue)); + Editable entryEditable = Editable.castFrom(entry); + entryEditable.text = defaultValue; box.append(entry); - dialog.addButton(I18n.str("ok"), ResponseType.OK); - dialog.addButton(I18n.str("cancel"), ResponseType.CANCEL); - dialog.onResponse(processResponses(dialog, () -> ok.accept(entryEditable.getText().toString()), cancel)); + dialog.addButton(I18n.get("ok"), ResponseType.OK.getValue()); + dialog.addButton(I18n.get("cancel"), ResponseType.CANCEL.getValue()); + dialog.onResponse(processResponses(dialog, () -> ok.accept(entryEditable.text.toString()), cancel)); dialog.show(); }); } @@ -68,28 +66,28 @@ public enum GtkEnvBackend implements LauncherEnv.EnvBackend { //TODO test //TODO } - private void simpleDialog(String markup, String title, int type, int buttons, Runnable ok, Runnable cancel) { + private void simpleDialog(String markup, String title, MessageType type, ButtonsType buttons, Runnable ok, Runnable cancel) { GtkMain.schedule(() -> { - MessageDialog dialog = new MessageDialog(dialogParent, DialogFlags.MODAL | DialogFlags.DESTROY_WITH_PARENT, type, buttons, null); - dialog.setTitle(new Str(title)); - dialog.setMarkup(new Str(markup)); + MessageDialog dialog = new MessageDialog(dialogParent, DialogFlags.MODAL.or(DialogFlags.DESTROY_WITH_PARENT), type, buttons, null); + dialog.title = title; + dialog.markup = markup; dialog.onResponse(processResponses(dialog, ok, cancel)); dialog.show(); }); } - private Dialog.OnResponse processResponses(Dialog dialog, @Nullable Runnable ok, @Nullable Runnable cancel) { - return response_id -> { - switch (response_id) { - case ResponseType.OK -> { + private Dialog.Response processResponses(Dialog dialog, @Nullable Runnable ok, @Nullable Runnable cancel) { + return ($, response_id) -> { + switch (ResponseType.of(response_id)) { + case OK -> { dialog.close(); if (ok != null) ok.run(); } - case ResponseType.CLOSE, ResponseType.CANCEL -> { + case CLOSE, CANCEL -> { dialog.close(); if (cancel != null) cancel.run(); } - case ResponseType.DELETE_EVENT -> dialog.destroy(); + case DELETE_EVENT -> dialog.destroy(); default -> Utils.LOGGER.error("Unexpected response type: " + response_id); } }; diff --git a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/GtkMain.java b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/GtkMain.java index 67e16d4..e1a6cf5 100644 --- a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/GtkMain.java +++ b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/GtkMain.java @@ -1,11 +1,8 @@ package io.gitlab.jfronny.inceptum.gtk; -import ch.bailu.gtk.GTK; -import ch.bailu.gtk.gio.ApplicationFlags; -import ch.bailu.gtk.glib.Glib; -import ch.bailu.gtk.gtk.Application; -import ch.bailu.gtk.type.Str; -import ch.bailu.gtk.type.Strs; +import org.gtk.gio.ApplicationFlags; +import org.gtk.glib.GLib; +import org.gtk.gtk.Application; import io.gitlab.jfronny.inceptum.common.*; import io.gitlab.jfronny.inceptum.gtk.window.MainWindow; import io.gitlab.jfronny.inceptum.launcher.LauncherEnv; @@ -14,7 +11,7 @@ import java.io.IOException; import java.util.*; public class GtkMain { - public static final Str ID = new Str("io.gitlab.jfronny.inceptum"); + public static final String ID = "io.gitlab.jfronny.inceptum"; private static final Queue SCHEDULED = new ArrayDeque<>(); public static void main(String[] args) throws IOException { @@ -37,11 +34,11 @@ public class GtkMain { public static int showGui(String[] args) throws IOException { var app = new Application(ID, ApplicationFlags.FLAGS_NONE); - app.onActivate(() -> { + app.onActivate($ -> { GtkMenubar.create(app); var window = new MainWindow(app); window.show(); - Glib.idleAdd(user_data -> { + GLib.idleAdd(data -> { Runnable r; while ((r = SCHEDULED.poll()) != null) { try { @@ -50,14 +47,15 @@ public class GtkMain { Utils.LOGGER.error("Could not run scheduled task", t); } } - return GTK.TRUE; + return true; }, null); GtkEnvBackend.INSTANCE.dialogParent = window; - window.onCloseRequest(() -> { + window.onCloseRequest($1 -> { GtkEnvBackend.INSTANCE.dialogParent = null; - return GTK.FALSE; + app.quit(); + return false; }); }); - return app.run(args.length, new Strs(args)); + return app.run(args.length, args); } } \ No newline at end of file diff --git a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/GtkMenubar.java b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/GtkMenubar.java index 8843e09..49fd9c0 100644 --- a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/GtkMenubar.java +++ b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/GtkMenubar.java @@ -1,6 +1,6 @@ package io.gitlab.jfronny.inceptum.gtk; -import ch.bailu.gtk.gtk.Application; +import org.gtk.gtk.Application; import io.gitlab.jfronny.commons.ref.R; import io.gitlab.jfronny.inceptum.common.Utils; import io.gitlab.jfronny.inceptum.gtk.menu.MenuBuilder; @@ -63,11 +63,11 @@ public class GtkMenubar { } InstanceLauncher.launchClient(instance); }; - if (instance.isSetupLocked()) { + if (instance.isSetupLocked) { LauncherEnv.showError(I18n.get("instance.launch.locked.setup"), I18n.get("instance.launch.locked")); return; } - if (instance.isRunningLocked()) { + if (instance.isRunningLocked) { LauncherEnv.showOkCancel(I18n.get("instance.launch.locked.running"), I18n.get("instance.launch.locked"), () -> { new Thread(launch).start(); //TODO loom }, R::nop); @@ -84,9 +84,9 @@ public class GtkMenubar { accountsMenu.button("manage", () -> { //TODO UI to add/remove accounts }); - List accounts = new ArrayList<>(AccountManager.getAccounts()); + List accounts = new ArrayList<>(AccountManager.accounts); accounts.add(null); - accountsMenu.literalRadio("account", accounts.get(AccountManager.getSelectedIndex()), accounts, (i, acc) -> { + accountsMenu.literalRadio("account", accounts.get(AccountManager.selectedIndex), accounts, (i, acc) -> { if (acc == null) return I18n.get("account.none"); return acc.minecraftUsername; }, AccountManager::switchAccount); diff --git a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/TestStart.java b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/TestStart.java index 144aa7b..3b67525 100644 --- a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/TestStart.java +++ b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/TestStart.java @@ -1,34 +1,31 @@ package io.gitlab.jfronny.inceptum.gtk; -import ch.bailu.gtk.GTK; -import ch.bailu.gtk.gio.ApplicationFlags; -import ch.bailu.gtk.gtk.*; -import ch.bailu.gtk.type.Str; -import ch.bailu.gtk.type.Strs; +import org.gtk.gio.ApplicationFlags; +import org.gtk.gtk.*; import io.gitlab.jfronny.commons.ref.R; public class TestStart { public static void main(String[] args) { var app = new Application(GtkMain.ID, ApplicationFlags.FLAGS_NONE); - app.onActivate(() -> { - var button = Button.newWithLabelButton(new Str("Test")); + app.onActivate($ -> { + var button = Button.newWithLabel("Test"); button.onClicked(TestStart::test); var window = new ApplicationWindow(app); window.setDefaultSize(200, 50); - window.setTitle(new Str("Inceptum")); - window.setChild(button); + window.title = "Inceptum"; + window.child = button; window.show(); GtkEnvBackend.INSTANCE.dialogParent = window; - window.onCloseRequest(() -> { + window.onCloseRequest($1 -> { GtkEnvBackend.INSTANCE.dialogParent = null; - return GTK.FALSE; + return false; }); }); - System.exit(app.run(args.length, new Strs(args))); + System.exit(app.run(args.length, args)); } - private static void test() { + private static void test(Button $) { GtkEnvBackend backend = GtkEnvBackend.INSTANCE; backend.getInput("Ae", "IoU\naee", "Def", R::nop, R::nop); diff --git a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/callback/DisposeCallback.java b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/callback/DisposeCallback.java new file mode 100644 index 0000000..ee0c5d6 --- /dev/null +++ b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/callback/DisposeCallback.java @@ -0,0 +1,24 @@ +package io.gitlab.jfronny.inceptum.gtk.callback; + +import io.github.jwharm.javagi.CallbackGenerator; +import io.github.jwharm.javagi.Interop; +import org.jetbrains.annotations.ApiStatus; + +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemoryAddress; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; + +public interface DisposeCallback { + void upcall(MemoryAddress pointer); + + @ApiStatus.Internal + FunctionDescriptor DESCRIPTOR = FunctionDescriptor.ofVoid(Interop.valueLayout.ADDRESS); + @ApiStatus.Internal + MethodHandle HANDLE = CallbackGenerator.getHandle(MethodHandles.lookup(), DisposeCallback.class, DESCRIPTOR); + + default MemoryAddress toCallback() { + return Linker.nativeLinker().upcallStub(HANDLE.bindTo(this), DESCRIPTOR, Interop.getScope()).address(); + } +} diff --git a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/callback/GetItemCallback.java b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/callback/GetItemCallback.java new file mode 100644 index 0000000..c839cb1 --- /dev/null +++ b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/callback/GetItemCallback.java @@ -0,0 +1,24 @@ +package io.gitlab.jfronny.inceptum.gtk.callback; + +import io.github.jwharm.javagi.CallbackGenerator; +import io.github.jwharm.javagi.Interop; +import org.jetbrains.annotations.ApiStatus; + +import java.lang.foreign.Addressable; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemoryAddress; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; + +@FunctionalInterface +public interface GetItemCallback { + Addressable upcall(MemoryAddress inst, int position); + + @ApiStatus.Internal FunctionDescriptor DESCRIPTOR = FunctionDescriptor.of(Interop.valueLayout.ADDRESS, Interop.valueLayout.ADDRESS, Interop.valueLayout.C_INT); + @ApiStatus.Internal MethodHandle HANDLE = CallbackGenerator.getHandle(MethodHandles.lookup(), GetItemCallback.class, DESCRIPTOR); + + default MemoryAddress toCallback() { + return Linker.nativeLinker().upcallStub(HANDLE.bindTo(this), DESCRIPTOR, Interop.getScope()).address(); + } +} diff --git a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/callback/GetItemTypeCallback.java b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/callback/GetItemTypeCallback.java new file mode 100644 index 0000000..d8be6fc --- /dev/null +++ b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/callback/GetItemTypeCallback.java @@ -0,0 +1,24 @@ +package io.gitlab.jfronny.inceptum.gtk.callback; + +import io.github.jwharm.javagi.CallbackGenerator; +import io.github.jwharm.javagi.Interop; +import org.jetbrains.annotations.ApiStatus; + +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemoryAddress; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; + +public interface GetItemTypeCallback { + long upcall(MemoryAddress address); + + @ApiStatus.Internal + FunctionDescriptor DESCRIPTOR = FunctionDescriptor.of(Interop.valueLayout.C_LONG, Interop.valueLayout.ADDRESS); + @ApiStatus.Internal + MethodHandle HANDLE = CallbackGenerator.getHandle(MethodHandles.lookup(), GetItemTypeCallback.class, DESCRIPTOR); + + default MemoryAddress toCallback() { + return Linker.nativeLinker().upcallStub(HANDLE.bindTo(this), DESCRIPTOR, Interop.getScope()).address(); + } +} diff --git a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/callback/GetNItemsCallback.java b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/callback/GetNItemsCallback.java new file mode 100644 index 0000000..39b1dcc --- /dev/null +++ b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/callback/GetNItemsCallback.java @@ -0,0 +1,24 @@ +package io.gitlab.jfronny.inceptum.gtk.callback; + +import io.github.jwharm.javagi.CallbackGenerator; +import io.github.jwharm.javagi.Interop; +import org.jetbrains.annotations.ApiStatus; + +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemoryAddress; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; + +public interface GetNItemsCallback { + int upcall(MemoryAddress inst); + + @ApiStatus.Internal + FunctionDescriptor DESCRIPTOR = FunctionDescriptor.of(Interop.valueLayout.C_INT, Interop.valueLayout.ADDRESS); + @ApiStatus.Internal + MethodHandle HANDLE = CallbackGenerator.getHandle(MethodHandles.lookup(), GetNItemsCallback.class, DESCRIPTOR); + + default MemoryAddress toCallback() { + return Linker.nativeLinker().upcallStub(HANDLE.bindTo(this), DESCRIPTOR, Interop.getScope()).address(); + } +} diff --git a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/callback/GetPropertyCallback.java b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/callback/GetPropertyCallback.java new file mode 100644 index 0000000..16445a2 --- /dev/null +++ b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/callback/GetPropertyCallback.java @@ -0,0 +1,36 @@ +package io.gitlab.jfronny.inceptum.gtk.callback; + +import io.github.jwharm.javagi.CallbackGenerator; +import io.github.jwharm.javagi.Interop; +import io.github.jwharm.javagi.Marshal; +import io.github.jwharm.javagi.Ownership; +import org.gtk.gobject.GiObject; +import org.gtk.gobject.ParamSpec; +import org.gtk.gobject.Value; +import org.jetbrains.annotations.ApiStatus; + +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemoryAddress; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; + +public interface GetPropertyCallback { + void invoke(GiObject object, int propertyId, Value value, ParamSpec paramSpec); + + default void upcall(MemoryAddress object, int propertyId, MemoryAddress value, MemoryAddress pspec) { + invoke(GiObject.fromAddress.marshal(object, Ownership.NONE), + propertyId, + Value.fromAddress.marshal(value, Ownership.NONE), + ParamSpec.fromAddress.marshal(pspec, Ownership.NONE)); + } + + @ApiStatus.Internal + FunctionDescriptor DESCRIPTOR = FunctionDescriptor.ofVoid(Interop.valueLayout.ADDRESS, Interop.valueLayout.C_INT, Interop.valueLayout.ADDRESS, Interop.valueLayout.ADDRESS); + @ApiStatus.Internal + MethodHandle HANDLE = CallbackGenerator.getHandle(MethodHandles.lookup(), GetPropertyCallback.class, DESCRIPTOR); + + default MemoryAddress toCallback() { + return Linker.nativeLinker().upcallStub(HANDLE.bindTo(this), DESCRIPTOR, Interop.getScope()).address(); + } +} diff --git a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/callback/SetPropertyCallback.java b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/callback/SetPropertyCallback.java new file mode 100644 index 0000000..39e1e7a --- /dev/null +++ b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/callback/SetPropertyCallback.java @@ -0,0 +1,36 @@ +package io.gitlab.jfronny.inceptum.gtk.callback; + +import io.github.jwharm.javagi.CallbackGenerator; +import io.github.jwharm.javagi.Interop; +import io.github.jwharm.javagi.Marshal; +import io.github.jwharm.javagi.Ownership; +import org.gtk.gobject.GiObject; +import org.gtk.gobject.ParamSpec; +import org.gtk.gobject.Value; +import org.jetbrains.annotations.ApiStatus; + +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemoryAddress; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; + +public interface SetPropertyCallback { + void invoke(GiObject object, int propertyId, Value value, ParamSpec paramSpec); + + default void upcall(MemoryAddress object, int propertyId, MemoryAddress value, MemoryAddress pspec) { + invoke(GiObject.fromAddress.marshal(object, Ownership.NONE), + propertyId, + Value.fromAddress.marshal(value, Ownership.NONE), + ParamSpec.fromAddress.marshal(pspec, Ownership.NONE)); + } + + @ApiStatus.Internal + FunctionDescriptor DESCRIPTOR = FunctionDescriptor.ofVoid(Interop.valueLayout.ADDRESS, Interop.valueLayout.C_INT, Interop.valueLayout.ADDRESS, Interop.valueLayout.ADDRESS); + @ApiStatus.Internal + MethodHandle HANDLE = CallbackGenerator.getHandle(MethodHandles.lookup(), SetPropertyCallback.class, DESCRIPTOR); + + default MemoryAddress toCallback() { + return Linker.nativeLinker().upcallStub(HANDLE.bindTo(this), DESCRIPTOR, Interop.getScope()).address(); + } +} diff --git a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/control/InstanceGridEntryFactory.java b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/control/InstanceGridEntryFactory.java index 5bd8196..9769d76 100644 --- a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/control/InstanceGridEntryFactory.java +++ b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/control/InstanceGridEntryFactory.java @@ -1,11 +1,10 @@ package io.gitlab.jfronny.inceptum.gtk.control; -import ch.bailu.gtk.GTK; -import ch.bailu.gtk.bridge.ListIndex; -import ch.bailu.gtk.gtk.*; -import ch.bailu.gtk.pango.EllipsizeMode; -import ch.bailu.gtk.type.Str; +import io.gitlab.jfronny.inceptum.gtk.util.ListIndex; +import org.gtk.gtk.*; import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance; +import org.pango.EllipsizeMode; +import org.pango.WrapMode; import java.util.List; @@ -13,24 +12,24 @@ public class InstanceGridEntryFactory extends SignalListItemFactory { public InstanceGridEntryFactory(List instanceList) { super(); //TODO better design - onSetup(item -> { + onSetup(($, item) -> { var box = new Box(Orientation.VERTICAL, 5); var thumbnail = new InstanceThumbnail(); box.append(thumbnail); - var label = new Label(Str.NULL); + var label = new Label(null); label.setSizeRequest(192, -1); - label.setMaxWidthChars(20); - label.setJustify(Justification.CENTER); - label.setHalign(Align.START); - label.setHexpand(GTK.TRUE); - label.setValign(Align.CENTER); - label.setEllipsize(EllipsizeMode.MIDDLE); - label.setLines(3); - label.setWrap(GTK.TRUE); - label.setWrapMode(WrapMode.WORD_CHAR); - label.setMarginTop(10); + label.maxWidthChars = 20; + label.justify = Justification.CENTER; + label.halign = Align.START; + label.hexpand = true; + label.valign = Align.CENTER; + label.ellipsize = EllipsizeMode.MIDDLE; + label.lines = 3; + label.wrap = true; + label.wrapMode = WrapMode.WORD_CHAR; + label.marginTop = 10; box.append(label); // Label label = new Label(Str.NULL); // label.setXalign(0); @@ -50,11 +49,11 @@ public class InstanceGridEntryFactory extends SignalListItemFactory { // openDir.setHasTooltip(GTK.TRUE); // box.append(openDir); - item.setChild(box); + ListItem.castFrom(item).setChild(box); //TODO server launch with network-server-symbolic //TODO kill current instance }); - onBind(item -> { + onBind(($, item) -> { // Label label = new Label(item.getChild().getFirstChild().cast()); // Button launch = new Button(label.getNextSibling().cast()); // Button openDir = new Button(launch.getNextSibling().cast()); @@ -62,13 +61,16 @@ public class InstanceGridEntryFactory extends SignalListItemFactory { // label.setText(new Str(instance.toString())); // launch.onClicked(() -> GtkMenubar.launch(instance)); // openDir.onClicked(() -> Utils.openFile(instance.path().toFile())); - Box box = new Box(item.getChild().cast()); - InstanceThumbnail thumbnail = new InstanceThumbnail(box.getFirstChild().cast()); - Label label = new Label(thumbnail.getNextSibling().cast()); - Instance instance = instanceList.get(ListIndex.toIndex(item)); + ListItem li = ListItem.castFrom(item); + + Box box = Box.castFrom(li.getChild()); + InstanceThumbnail thumbnail = InstanceThumbnail.castFrom(box.firstChild); + Label label = Label.castFrom(thumbnail.nextSibling); + + Instance instance = instanceList.get(ListIndex.toIndex(li)); thumbnail.bind(instance); - label.setText(new Str(instance.toString())); + label.text = instance.toString(); //TODO right click menu + double click action //TODO edit button document-edit-symbolic -> edit-delete-symbolic, edit-copy-symbolic }); diff --git a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/control/InstanceListEntryFactory.java b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/control/InstanceListEntryFactory.java index ac6824d..1493f33 100644 --- a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/control/InstanceListEntryFactory.java +++ b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/control/InstanceListEntryFactory.java @@ -1,10 +1,8 @@ package io.gitlab.jfronny.inceptum.gtk.control; -import ch.bailu.gtk.GTK; -import ch.bailu.gtk.adw.ActionRow; -import ch.bailu.gtk.bridge.ListIndex; -import ch.bailu.gtk.gtk.*; -import ch.bailu.gtk.type.Str; +import io.gitlab.jfronny.inceptum.gtk.util.ListIndex; +import org.gnome.adw.ActionRow; +import org.gtk.gtk.*; import io.gitlab.jfronny.inceptum.common.Utils; import io.gitlab.jfronny.inceptum.gtk.GtkMenubar; import io.gitlab.jfronny.inceptum.gtk.util.I18n; @@ -15,44 +13,46 @@ import java.util.List; public class InstanceListEntryFactory extends SignalListItemFactory { public InstanceListEntryFactory(List instanceList) { super(); - onSetup(item -> { + onSetup(($, item) -> { var thumbnail = new InstanceThumbnail(); - thumbnail.setName(new Str("inceptum-thumbnail")); + thumbnail.name = "inceptum-thumbnail"; var launch = new Button(); - launch.setName(new Str("inceptum-launch")); - launch.setIconName(new Str("computer-symbolic")); - launch.setTooltipText(I18n.str("instance.launch")); - launch.setHasTooltip(GTK.TRUE); + launch.name = "inceptum-launch"; + launch.iconName = "computer-symbolic"; + launch.tooltipText = I18n.get("instance.launch"); + launch.hasTooltip = true; var openDir = new Button(); - openDir.setName(new Str("inceptum-open-dir")); - openDir.setIconName(new Str("folder-symbolic")); - openDir.setTooltipText(I18n.str("instance.directory")); - openDir.setHasTooltip(GTK.TRUE); + openDir.name = "inceptum-open-dir"; + openDir.iconName = "folder-symbolic"; + openDir.tooltipText = I18n.get("instance.directory"); + openDir.hasTooltip = true; var row = new ActionRow(); - row.setName(new Str("inceptum-row")); - row.setActivatableWidget(launch); + row.name = "inceptum-row"; + row.activatableWidget = launch; row.addPrefix(thumbnail); row.addSuffix(launch); row.addSuffix(openDir); - item.setChild(row); + ListItem.castFrom(item).setChild(row); //TODO server launch with network-server-symbolic //TODO kill current instance }); - onBind(item -> { - Instance instance = instanceList.get(ListIndex.toIndex(item)); - ActionRow row = new ActionRow(item.getChild().cast()); - Box prefixes = new Box(row.getFirstChild().getFirstChild().cast()); - Box suffixes = new Box(row.getFirstChild().getLastChild().cast()); + onBind(($, item) -> { + ListItem li = ListItem.castFrom(item); - InstanceThumbnail thumbnail = new InstanceThumbnail(prefixes.getFirstChild().cast()); - Button launch = new Button(suffixes.getFirstChild().cast()); - Button openDir = new Button(launch.getNextSibling().cast()); - row.setTitle(new Str(instance.toString())); + Instance instance = instanceList.get(ListIndex.toIndex(li)); + ActionRow row = ActionRow.castFrom(li.child); + Box prefixes = Box.castFrom(row.firstChild.firstChild); + Box suffixes = Box.castFrom(row.firstChild.lastChild); + + InstanceThumbnail thumbnail = InstanceThumbnail.castFrom(prefixes.firstChild); + Button launch = Button.castFrom(suffixes.firstChild); + Button openDir = Button.castFrom(launch.nextSibling); + row.title = instance.toString(); // InstanceThumbnail thumbnail = new InstanceThumbnail(row.getFirstChild().cast()); // Label label = new Label(thumbnail.getNextSibling().cast()); @@ -61,8 +61,8 @@ public class InstanceListEntryFactory extends SignalListItemFactory { thumbnail.bind(instance); // label.setText(new Str(instance.toString())); - launch.onClicked(() -> GtkMenubar.launch(instance)); - openDir.onClicked(() -> Utils.openFile(instance.path().toFile())); + launch.onClicked($1 -> GtkMenubar.launch(instance)); + openDir.onClicked($1 -> Utils.openFile(instance.path().toFile())); //TODO why the hell does this crash the VM? //TODO GestureClick.setButton(GDK_BUTTON_SECONDARY) diff --git a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/control/InstanceThumbnail.java b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/control/InstanceThumbnail.java index 08601d5..455351b 100644 --- a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/control/InstanceThumbnail.java +++ b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/control/InstanceThumbnail.java @@ -1,16 +1,30 @@ package io.gitlab.jfronny.inceptum.gtk.control; -import ch.bailu.gtk.gtk.*; -import ch.bailu.gtk.type.*; +import io.github.jwharm.javagi.Ownership; import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance; +import org.gtk.gobject.GObject; +import org.gtk.gobject.TypeInstance; +import org.gtk.gtk.Image; +import org.gtk.gtk.Spinner; +import org.gtk.gtk.Stack; + +import java.lang.foreign.Addressable; public class InstanceThumbnail extends Stack { - private static final Str SPINNER = new Str("spinner"); - private static final Str IMAGE = new Str("image"); - private static final Str GENERIC = new Str("generic"); + private static final String SPINNER = "spinner"; + private static final String IMAGE = "image"; + private static final String GENERIC = "generic"; - public InstanceThumbnail(CPointer pointer) { - super(pointer); + private InstanceThumbnail(Addressable address, Ownership ownership) { + super(address, ownership); + } + + public static InstanceThumbnail castFrom(org.gtk.gobject.GiObject gobject) { + if (GObject.typeCheckInstanceIsA(TypeInstance.fromAddress.marshal(gobject.handle(), Ownership.NONE), Stack.getType())) { + return new InstanceThumbnail(gobject.handle(), gobject.yieldOwnership()); + } else { + throw new ClassCastException("Object type is not an instance of GtkStack"); + } } public InstanceThumbnail() { @@ -18,26 +32,26 @@ public class InstanceThumbnail extends Stack { var spinner = new Spinner(); var image = new Image(); var generic = new Image(); - spinner.setName(SPINNER); - image.setName(IMAGE); - generic.setName(GENERIC); - generic.setFromIconName(new Str("media-playback-start-symbolic")); //TODO better default icon + spinner.name = SPINNER; + image.name = IMAGE; + generic.name = GENERIC; + generic.setFromIconName("media-playback-start-symbolic"); //TODO better default icon addNamed(spinner, SPINNER); addNamed(image, IMAGE); addNamed(generic, GENERIC); } public void bind(Instance entry) { - var spinner = new Spinner(getChildByName(SPINNER).cast()); - var image = new Image(getChildByName(IMAGE).cast()); //TODO - var generic = new Image(getChildByName(GENERIC).cast()); + var spinner = Spinner.castFrom(getChildByName(SPINNER)); + var image = Image.castFrom(getChildByName(IMAGE)); //TODO + var generic = Image.castFrom(getChildByName(GENERIC)); //TODO mark instance being played - if (entry.isSetupLocked()) { - setVisibleChild(spinner); + if (entry.isSetupLocked) { + visibleChild = spinner; } else if (false) { // if the instance has an image, load the image data and set it as the visible child - setVisibleChild(image); + visibleChild = image; } else { - setVisibleChild(generic); + visibleChild = generic; } } } diff --git a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/menu/ButtonItem.java b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/menu/ButtonItem.java index f46cb33..dc47559 100644 --- a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/menu/ButtonItem.java +++ b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/menu/ButtonItem.java @@ -1,6 +1,6 @@ package io.gitlab.jfronny.inceptum.gtk.menu; -import ch.bailu.gtk.gio.SimpleAction; +import org.gtk.gio.SimpleAction; public class ButtonItem extends MenuItem { public ButtonItem(SimpleAction action) { diff --git a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/menu/MenuBuilder.java b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/menu/MenuBuilder.java index fce545a..9a3c2e2 100644 --- a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/menu/MenuBuilder.java +++ b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/menu/MenuBuilder.java @@ -1,19 +1,19 @@ package io.gitlab.jfronny.inceptum.gtk.menu; -import ch.bailu.gtk.GTK; -import ch.bailu.gtk.gio.MenuItem; -import ch.bailu.gtk.gio.*; -import ch.bailu.gtk.glib.Variant; -import ch.bailu.gtk.glib.VariantType; -import ch.bailu.gtk.gtk.Application; -import ch.bailu.gtk.gtk.PopoverMenu; -import ch.bailu.gtk.type.Pointer; -import ch.bailu.gtk.type.Str; import io.gitlab.jfronny.commons.throwable.ThrowingRunnable; import io.gitlab.jfronny.inceptum.common.Utils; import io.gitlab.jfronny.inceptum.gtk.util.I18n; +import org.gtk.gio.MenuItem; +import org.gtk.gio.*; +import org.gtk.glib.Variant; +import org.gtk.glib.VariantType; +import org.gtk.gtk.Application; +import org.gtk.gtk.PopoverMenu; -import java.util.*; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -22,13 +22,13 @@ public class MenuBuilder { private static Menu getRootMenu(Application app) { synchronized (LOCK) { - var currentMenu = app.getMenubar(); - if (currentMenu.equals(Pointer.NULL)) { + var currentMenu = app.menubar; + if (currentMenu == null) { var menu = new Menu(); - app.setMenubar(menu); + app.menubar = menu; return menu; } else { - return new Menu(currentMenu.cast()); + return Menu.castFrom(currentMenu); } } } @@ -42,10 +42,6 @@ public class MenuBuilder { this(app, getRootMenu(app), ""); } - public MenuBuilder(Application app, Menu menu, String prefix) { - this(new ActionMap(app.cast()), menu, prefix); - } - public MenuBuilder(ActionMap map, Menu menu, String prefix) { if (!prefix.isEmpty() && !prefix.endsWith(".")) prefix += "."; this.map = Objects.requireNonNull(map); @@ -59,29 +55,29 @@ public class MenuBuilder { public ButtonItem literalButton(String internalName, String label, ThrowingRunnable onClick) { internalName = prefix + internalName; - SimpleAction sAct = new SimpleAction(new Str(internalName), null); + SimpleAction sAct = new SimpleAction(internalName, null); addAction(internalName, sAct); - sAct.onActivate(v -> { + sAct.onActivate(($, variant) -> { try { onClick.run(); } catch (Throwable e) { Utils.LOGGER.error("Could not execute action", e); } }); - menu.appendItem(new MenuItem(new Str(label), new Str("app." + internalName))); + menu.appendItem(new MenuItem(label, "app." + internalName)); return new ButtonItem(sAct); } public ToggleItem toggle(String name, boolean initial, Consumer action) { name = prefix + name; - SimpleAction sAct = SimpleAction.newStatefulSimpleAction(new Str(name), null, Variant.newBooleanVariant(GTK.is(initial))); - Action rAct = addAction(name, sAct); - sAct.onActivate(parameter -> { - boolean state = !GTK.is(rAct.getState().getBoolean()); - sAct.setState(Variant.newBooleanVariant(GTK.is(state))); + SimpleAction sAct = SimpleAction.newStateful(name, null, Variant.newBoolean(initial)); + addAction(name, sAct); + sAct.onActivate(($, variant) -> { + boolean state = !sAct.getState().getBoolean(); + sAct.state = Variant.newBoolean(state); action.accept(state); }); - menu.appendItem(new MenuItem(I18n.str("menu." + name), new Str("app." + name))); + menu.appendItem(new MenuItem(I18n.get("menu." + name), "app." + name)); return new ToggleItem(sAct); } @@ -92,15 +88,15 @@ public class MenuBuilder { public RadioItem literalRadio(String name, T initial, List options, BiFunction stringifier, Consumer action) { Objects.requireNonNull(options); name = prefix + name; - SimpleAction sAct = SimpleAction.newStatefulSimpleAction(new Str(name), new VariantType(new Str("i")), Variant.newInt32Variant(options.indexOf(initial))); + SimpleAction sAct = SimpleAction.newStateful(name, new VariantType("i"), Variant.newInt32(options.indexOf(initial))); addAction(name, sAct); - sAct.onActivate(parameter -> { - sAct.setState(parameter); - action.accept(options.get(parameter.getInt32())); + sAct.onActivate(($, variant) -> { + sAct.state = variant; + action.accept(options.get(variant.getInt32())); }); int i = 0; for (T option : options) { - menu.appendItem(new MenuItem(new Str(stringifier.apply(i, option)), new Str("app." + name + "(" + i + ")"))); + menu.appendItem(new MenuItem(stringifier.apply(i, option), "app." + name + "(" + i + ")")); i++; } return new RadioItem<>(sAct, options); @@ -109,23 +105,21 @@ public class MenuBuilder { public MenuBuilder submenu(String name) { name = prefix + name; Menu submenu = new Menu(); - menu.appendSubmenu(I18n.str("menu." + name), submenu); + menu.appendSubmenu(I18n.get("menu." + name), submenu); return new MenuBuilder(map, submenu, name); } public void clear() { menu.removeAll(); refs.forEach((name, action) -> { - map.removeAction(new Str(name)); + map.removeAction(name); }); refs.clear(); } - private Action addAction(String name, SimpleAction action) { - Action rAct = new Action(action.cast()); - map.addAction(rAct); - refs.put(name, rAct); - return rAct; + private void addAction(String name, SimpleAction action) { + map.addAction(action); + refs.put(name, action); } public Menu getMenu() { @@ -133,6 +127,6 @@ public class MenuBuilder { } public PopoverMenu asPopover() { - return PopoverMenu.newFromModelPopoverMenu(menu); + return PopoverMenu.newFromModel(menu); } } diff --git a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/menu/MenuItem.java b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/menu/MenuItem.java index ca3bd84..b864fd6 100644 --- a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/menu/MenuItem.java +++ b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/menu/MenuItem.java @@ -1,23 +1,19 @@ package io.gitlab.jfronny.inceptum.gtk.menu; -import ch.bailu.gtk.GTK; -import ch.bailu.gtk.gio.Action; -import ch.bailu.gtk.gio.SimpleAction; +import org.gtk.gio.SimpleAction; public abstract class MenuItem { - protected final SimpleAction sAction; - protected final Action action; + protected final SimpleAction action; public MenuItem(SimpleAction action) { - this.sAction = action; - this.action = new Action(action.cast()); + this.action = action; } public boolean getEnabled() { - return GTK.is(action.getEnabled()); + return action.getEnabled(); } public void setEnabled(boolean enabled) { - sAction.setEnabled(GTK.is(enabled)); + action.enabled = enabled; } } diff --git a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/menu/RadioItem.java b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/menu/RadioItem.java index f413978..2d9e447 100644 --- a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/menu/RadioItem.java +++ b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/menu/RadioItem.java @@ -1,7 +1,7 @@ package io.gitlab.jfronny.inceptum.gtk.menu; -import ch.bailu.gtk.gio.SimpleAction; -import ch.bailu.gtk.glib.Variant; +import org.gtk.gio.SimpleAction; +import org.gtk.glib.Variant; import java.util.List; @@ -14,7 +14,7 @@ public class RadioItem extends MenuItem { } public void setSelected(T selected) { - sAction.setState(Variant.newInt32Variant(options.indexOf(selected))); + action.setState(Variant.newInt32(options.indexOf(selected))); } public T getSelected() { diff --git a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/menu/ToggleItem.java b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/menu/ToggleItem.java index 61ce129..bc1f5d9 100644 --- a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/menu/ToggleItem.java +++ b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/menu/ToggleItem.java @@ -1,8 +1,7 @@ package io.gitlab.jfronny.inceptum.gtk.menu; -import ch.bailu.gtk.GTK; -import ch.bailu.gtk.gio.SimpleAction; -import ch.bailu.gtk.glib.Variant; +import org.gtk.gio.SimpleAction; +import org.gtk.glib.Variant; public class ToggleItem extends MenuItem { public ToggleItem(SimpleAction action) { @@ -10,11 +9,11 @@ public class ToggleItem extends MenuItem { } public boolean getState() { - return GTK.is(action.getState().getBoolean()); + return action.getState().getBoolean(); } public void setState(boolean state) { - sAction.setState(Variant.newBooleanVariant(GTK.is(state))); + action.state = Variant.newBoolean(state); } public boolean toggle() { diff --git a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/util/Dbg.java b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/util/Dbg.java index 029c86b..32747f2 100644 --- a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/util/Dbg.java +++ b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/util/Dbg.java @@ -1,31 +1,23 @@ package io.gitlab.jfronny.inceptum.gtk.util; -import ch.bailu.gtk.gtk.Widget; -import ch.bailu.gtk.type.Pointer; - -import java.util.Arrays; -import java.util.stream.Collectors; +import org.gtk.gtk.Widget; public class Dbg { public static String inspect(Widget ptr) { - if (ptr.isNull()) return ""; + if (ptr == null) return ""; StringBuilder sb = new StringBuilder(); inspect(ptr, sb, ""); return sb.toString(); } private static void inspect(Widget ptr, StringBuilder bld, String indent) { - bld.append(indent).append("<").append(ptr.getName().toString()).append("#").append(ptr.getCPointer()).append("> ") - .append(Arrays.stream(Pointer.toJnaPointer(ptr.getCssClasses().getCPointer()).getPointerArray(0)) - .map(p -> p.getString(0)) - .collect(Collectors.joining(", "))); - ptr = ptr.getFirstChild(); - if (ptr.isNotNull()) { - while (ptr.isNotNull()) { - bld.append("\n"); - inspect(ptr, bld, indent + " "); - ptr = ptr.getNextSibling(); - } + bld.append(indent).append("<").append(ptr.name).append("#").append(ptr.handle()).append("> ") + .append(ptr.getCssClasses().get()); + ptr = ptr.firstChild; + while (ptr != null) { + bld.append("\n"); + inspect(ptr, bld, indent + " "); + ptr = ptr.nextSibling; } } } diff --git a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/util/I18n.java b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/util/I18n.java index 8e6d0b7..453fa11 100644 --- a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/util/I18n.java +++ b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/util/I18n.java @@ -1,6 +1,5 @@ package io.gitlab.jfronny.inceptum.gtk.util; -import ch.bailu.gtk.type.Str; import org.jetbrains.annotations.PropertyKey; import java.util.ResourceBundle; @@ -16,12 +15,4 @@ public class I18n { public static String get(@PropertyKey(resourceBundle = BUNDLE) String key, Object... args) { return String.format(bundle.getString(key), args); } - - public static Str str(@PropertyKey(resourceBundle = BUNDLE) String key) { - return new Str(get(key)); - } - - public static Str str(@PropertyKey(resourceBundle = BUNDLE) String key, Object... args) { - return new Str(get(key, args)); - } } diff --git a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/util/ListIndex.java b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/util/ListIndex.java new file mode 100644 index 0000000..6c70bd1 --- /dev/null +++ b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/util/ListIndex.java @@ -0,0 +1,188 @@ +package io.gitlab.jfronny.inceptum.gtk.util; + +import io.github.jwharm.javagi.*; +import io.gitlab.jfronny.inceptum.gtk.GtkMain; +import io.gitlab.jfronny.inceptum.gtk.callback.*; +import org.gtk.gio.ListModel; +import org.gtk.gio.ListModelInterface; +import org.gtk.glib.Type; +import org.gtk.gobject.*; +import org.gtk.gtk.*; + +import java.lang.foreign.*; +import java.lang.invoke.VarHandle; + +public class ListIndex extends GiObject { + private static final int PROP_ITEM_TYPE = 1; + private static final String PROP_NAME = "item-type"; + private static final String TYPE_NAME = "ListIndex"; + private static final Type PARENT_TYPE = GiObject.getType(); + + private static final MemoryLayout memoryLayout = MemoryLayout.structLayout( + GiObject.getMemoryLayout().withName("parent_instance"), + Interop.valueLayout.C_INT.withName("index"), + Interop.valueLayout.C_INT.withName("size") + ).withName(TYPE_NAME); + + private static Type type; + public static Type getType() { + if (type == null) { + // Register the new gtype + type = GObject.typeRegisterStaticSimple( + PARENT_TYPE, + TYPE_NAME, + (short) ObjectClass.getMemoryLayout().byteSize(), + classInit, + (short) memoryLayout.byteSize(), + instanceInit, + TypeFlags.NONE + ); + GObject.typeAddInterfaceStatic(type, ListModel.getType(), InterfaceInfo.builder() + .setInterfaceInit(interfaceInit) + .setInterfaceData(null) + .setInterfaceFinalize(null) + .build()); + } + return type; + } + + private static final VarHandle index = memoryLayout.varHandle(MemoryLayout.PathElement.groupElement("index")); + private static final VarHandle size = memoryLayout.varHandle(MemoryLayout.PathElement.groupElement("size")); + + public static ListIndex castFrom(GiObject gobject) { + if (GObject.typeCheckInstanceIsA(TypeInstance.fromAddress.marshal(gobject.handle(), Ownership.NONE), getType())) { + return new ListIndex(gobject.handle(), gobject.yieldOwnership()); + } else { + throw new ClassCastException("Object type is not an instance of ListIndex"); + } + } + + public static final Marshal fromAddress = (input, ownership) -> input.equals(MemoryAddress.NULL) ? null : new ListIndex(input, ownership); + protected ListIndex(Addressable address, Ownership ownership) { + super(address, ownership); + } + + public ListIndex(int size) { + this(); + setSize(size); + } + + public ListIndex() { + super(new GiObject(getType(), PROP_NAME, getType()).handle(), + Ownership.FULL); + } + + private static final InstanceInitFunc instanceInit = (instance, gClass) -> fromAddress.marshal(instance.handle(), Ownership.NONE).initInstance(); + + private void initInstance() { + setIndex(0); + setSize(0); + } + + private static TypeClass parentClass = null; + private static final DisposeCallback instanceDispose = pointer -> { + if (parentClass == null) System.out.println("ListIndex::instanceDispose (no parent)"); + else { + InteropException ie = new InteropException("Could not dispose ListIndex"); + GtkMain.schedule(() -> { + try { + var func = (MemoryAddress) ObjectClass.getMemoryLayout() + .varHandle(MemoryLayout.PathElement.groupElement("dispose")) + .get(MemorySegment.ofAddress(parentClass.handle().address(), ObjectClass.getMemoryLayout().byteSize(), Interop.getScope())); + var linked = Linker.nativeLinker().downcallHandle(func, DisposeCallback.DESCRIPTOR); + linked.invoke(pointer); + } catch (Throwable e) { + throw (InteropException) ie.initCause(e); + } + }); + } + }; + + private static final SetPropertyCallback setProperty = (object, propertyId, value, paramSpec) -> { + if (propertyId != PROP_ITEM_TYPE) System.out.println("ListIndex::setProperty (unknown property)"); + }; + + private static final GetPropertyCallback getProperty = (object, propertyId, value, paramSpec) -> { + if (propertyId == PROP_ITEM_TYPE) value.setGtype(getType()); + else System.out.println("ListIndex::getProperty (unknown property)"); + }; + + private static final ClassInitFunc classInit = (klass, data) -> { + System.out.println("ListIndex::classInit"); + parentClass = klass.peekParent(); + ObjectClass objectClass = ObjectClass.fromAddress.marshal(GObject.typeCheckClassCast(klass, PARENT_TYPE).handle(), Ownership.NONE); + objectClass.setDispose(instanceDispose.toCallback()); + objectClass.setGetProperty(getProperty.toCallback()); + objectClass.setSetProperty(setProperty.toCallback()); + + ParamSpec paramType = GObject.paramSpecGtype(PROP_NAME, "", "", PARENT_TYPE, + ParamFlags.CONSTRUCT + .or(ParamFlags.READWRITE, + ParamFlags.STATIC_NAME, + ParamFlags.STATIC_NICK, + ParamFlags.STATIC_BLURB) + ); + + objectClass.installProperty(PROP_ITEM_TYPE, paramType); + }; + + private static final GetItemTypeCallback getItemType = address -> { + System.out.println("ListIndex::getItemType"); + return getType().getValue(); + }; + private static final GetNItemsCallback getNItems = address -> fromAddress.marshal(address, Ownership.NONE).getSize(); + private static final GetItemCallback getItem = (inst, position) -> { + ListIndex item = fromAddress.marshal(inst, Ownership.NONE).getItem(position); + if (item == null) return MemoryAddress.NULL; + return item.handle(); + }; + + public ListIndex getItem(int position) { + if (position >= getSize() || position <= -1) return null; + ListIndex result = new ListIndex(getSize()); + result.setIndex(position); + return result; + } + + private static final InterfaceInitFunc interfaceInit = (iface, data) -> { + System.out.println("ListIndex::interfaceInit"); + ListModelInterface lmi = ListModelInterface.fromAddress.marshal(iface.handle(), Ownership.NONE); + lmi.setGetItem(getItem.toCallback()); + lmi.setGetNItems(getNItems.toCallback()); + lmi.setGetItemType(getItemType.toCallback()); + }; + + public int getIndex() { + return (int) ListIndex.index.get(MemorySegment.ofAddress((MemoryAddress) handle(), memoryLayout.byteSize(), Interop.getScope())); + } + + public void setIndex(int index) { + ListIndex.index.set(MemorySegment.ofAddress((MemoryAddress) handle(), memoryLayout.byteSize(), Interop.getScope()), index); + } + + public int getSize() { + return (int) ListIndex.size.get(MemorySegment.ofAddress((MemoryAddress) handle(), memoryLayout.byteSize(), Interop.getScope())); + } + + public void setSize(int size) { + int oldSize = getSize(); + ListIndex.size.set(MemorySegment.ofAddress((MemoryAddress) handle(), memoryLayout.byteSize(), Interop.getScope()), size); + asListModel().itemsChanged(0, oldSize, size); + } + + public ListModel asListModel() { + return ListModel.castFrom(this); + } + + public SingleSelection inSingleSelection() { + return new SingleSelection(asListModel()); + } + + public SelectionModel inSelectionModel() { + return SelectionModel.castFrom(inSingleSelection()); + } + + public static int toIndex(ListItem item) { + return castFrom(item.getItem()).getIndex(); + } +} diff --git a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/window/AboutWindow.java b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/window/AboutWindow.java index bb69169..fef9f70 100644 --- a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/window/AboutWindow.java +++ b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/window/AboutWindow.java @@ -1,25 +1,24 @@ package io.gitlab.jfronny.inceptum.gtk.window; -import ch.bailu.gtk.gtk.AboutDialog; -import ch.bailu.gtk.gtk.License; -import ch.bailu.gtk.type.Str; +import org.gtk.gtk.AboutDialog; +import org.gtk.gtk.License; import io.gitlab.jfronny.inceptum.common.BuildMetadata; import io.gitlab.jfronny.inceptum.gtk.util.I18n; public class AboutWindow extends AboutDialog { public AboutWindow() { - setProgramName(new Str("Inceptum")); - setCopyright(new Str("Copyright (C) 2021 JFronny")); - setVersion(new Str(BuildMetadata.VERSION)); - setLicenseType(License.MIT_X11); - setLicense(I18n.str("about.license")); - setWebsiteLabel(I18n.str("about.contact")); - setWebsite(new Str("https://jfronny.gitlab.io/contact.html")); + programName = "Inceptum"; + copyright = "Copyright (C) 2021 JFronny"; + version = BuildMetadata.VERSION; + licenseType = License.MIT_X11; + license = I18n.get("about.license"); + websiteLabel = I18n.get("about.contact"); + website = "https://jfronny.gitlab.io/contact.html"; if (!BuildMetadata.IS_PUBLIC) { - setComments(I18n.str("about.unsupported-build")); + comments = I18n.get("about.unsupported-build"); } int vm = Runtime.version().feature(); - setSystemInformation(I18n.str(BuildMetadata.VM_VERSION == vm ? "about.jvm" : "about.jvm.unsupported", vm)); + systemInformation = I18n.get(BuildMetadata.VM_VERSION == vm ? "about.jvm" : "about.jvm.unsupported", vm); //TODO setLogo } diff --git a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/window/MainWindow.java b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/window/MainWindow.java index 2ff30af..fcba8c2 100644 --- a/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/window/MainWindow.java +++ b/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/window/MainWindow.java @@ -1,13 +1,11 @@ package io.gitlab.jfronny.inceptum.gtk.window; -import ch.bailu.gtk.GTK; -import ch.bailu.gtk.adw.Clamp; -import ch.bailu.gtk.adw.StatusPage; -import ch.bailu.gtk.bridge.ListIndex; -import ch.bailu.gtk.gio.Menu; -import ch.bailu.gtk.glib.Glib; -import ch.bailu.gtk.gtk.*; -import ch.bailu.gtk.type.Str; +import io.gitlab.jfronny.inceptum.gtk.util.ListIndex; +import org.gnome.adw.Clamp; +import org.gnome.adw.StatusPage; +import org.gtk.gio.Menu; +import org.gtk.glib.GLib; +import org.gtk.gtk.*; import io.gitlab.jfronny.inceptum.common.*; import io.gitlab.jfronny.inceptum.gtk.GtkMenubar; import io.gitlab.jfronny.inceptum.gtk.control.InstanceGridEntryFactory; @@ -40,24 +38,24 @@ public class MainWindow extends ApplicationWindow { HeaderBar header = new HeaderBar(); Button newButton = new Button(); - newButton.setIconName(new Str("list-add-symbolic")); - newButton.onClicked(NewInstanceWindow::createAndShow); + newButton.setIconName("list-add-symbolic"); + newButton.onClicked($ -> NewInstanceWindow.createAndShow()); MenuButton accountsButton = new MenuButton(); - accountsButton.setIconName(new Str("avatar-default-symbolic")); + accountsButton.setIconName("avatar-default-symbolic"); accountsButton.setPopover(GtkMenubar.accountsMenu.asPopover()); listButton = new Button(); - listButton.setIconName(new Str("view-list-symbolic")); - listButton.onClicked(() -> { + listButton.setIconName("view-list-symbolic"); + listButton.onClicked($ -> { InceptumConfig.listView = true; InceptumConfig.saveConfig(); generateWindowBody(); }); gridButton = new Button(); - gridButton.setIconName(new Str("view-grid-symbolic")); - gridButton.onClicked(() -> { + gridButton.setIconName("view-grid-symbolic"); + gridButton.onClicked($ -> { InceptumConfig.listView = false; InceptumConfig.saveConfig(); generateWindowBody(); @@ -70,7 +68,7 @@ public class MainWindow extends ApplicationWindow { uiMenu.button("preferences", () -> {}); //TODO preferences UI inspired by boxes uiMenu.button("about", AboutWindow::createAndShow); MenuButton menuButton = new MenuButton(); - menuButton.setIconName(new Str("open-menu-symbolic")); + menuButton.setIconName("open-menu-symbolic"); menuButton.setPopover(uiMenu.asPopover()); header.packStart(newButton); @@ -84,26 +82,27 @@ public class MainWindow extends ApplicationWindow { instanceListIndex = new ListIndex(); listView = new Clamp(); - listView.setMaximumSize(900); - listView.setChild(new ListView(instanceListIndex.inSelectionModel(), new InstanceListEntryFactory(instanceList))); + listView.maximumSize = 900; + listView.child = new ListView(instanceListIndex.inSelectionModel(), new InstanceListEntryFactory(instanceList)); gridView = new GridView(instanceListIndex.inSelectionModel(), new InstanceGridEntryFactory(instanceList)); empty = new StatusPage(); - empty.setTitle(I18n.str("main.empty.title")); - empty.setDescription(I18n.str("main.empty.description")); + empty.title = I18n.get("main.empty.title"); + empty.description = I18n.get("main.empty.description"); //TODO empty.setIconName(new Str()); stack = new Stack(); stack.addChild(listView); stack.addChild(gridView); + stack.addChild(empty); ScrolledWindow scroll = new ScrolledWindow(); scroll.setPolicy(PolicyType.NEVER, PolicyType.AUTOMATIC); - scroll.setChild(stack); + scroll.child = stack; setDefaultSize(360, 720); - setTitle(new Str("Inceptum")); - setTitlebar(header); - setShowMenubar(GTK.FALSE); - setChild(scroll); + title = "Inceptum"; + titlebar = header; + showMenubar = false; + child = scroll; generateWindowBody(); //TODO DropTarget to add mods/instances @@ -118,7 +117,7 @@ public class MainWindow extends ApplicationWindow { private void setupDirWatcher() throws IOException { //TODO test (including after lock state change) WatchService ws = FileSystems.getDefault().newWatchService(); MetaHolder.INSTANCE_DIR.register(ws, ENTRY_MODIFY, ENTRY_CREATE, ENTRY_DELETE); - int source = Glib.idleAdd(user_data -> { + int source = GLib.idleAdd(data -> { //TODO watch instance dirs for locks WatchKey key = ws.poll(); boolean instancesChanged = false; @@ -131,29 +130,29 @@ public class MainWindow extends ApplicationWindow { } } if (instancesChanged) generateWindowBody(); - return GTK.TRUE; + return true; }, null); - onCloseRequest(() -> { + onCloseRequest($ -> { try { ws.close(); } catch (IOException ignored) { } - Glib.sourceRemove(source); - return GTK.FALSE; + GLib.sourceRemove(source); + return false; }); } private void generateWindowBody() { - if (listButton != null) listButton.setVisible(GTK.is(!InceptumConfig.listView)); - if (gridButton != null) gridButton.setVisible(GTK.is(InceptumConfig.listView)); + if (listButton != null) listButton.visible = !InceptumConfig.listView; + if (gridButton != null) gridButton.visible = InceptumConfig.listView; try { instanceList.clear(); instanceList.addAll(InstanceList.ordered()); instanceListIndex.setSize(instanceList.size()); - if (InstanceList.isEmpty()) stack.setVisibleChild(empty); - else if (InceptumConfig.listView) stack.setVisibleChild(listView); - else stack.setVisibleChild(gridView); + if (InstanceList.isEmpty) stack.visibleChild = empty; + else if (InceptumConfig.listView) stack.visibleChild = listView; + else stack.visibleChild = gridView; } catch (IOException e) { Utils.LOGGER.error("Could not generate window body", e); } diff --git a/launcher-gtk/src/main/resources/inceptum.properties b/launcher-gtk/src/main/resources/inceptum.properties index 6638c22..09f5c08 100644 --- a/launcher-gtk/src/main/resources/inceptum.properties +++ b/launcher-gtk/src/main/resources/inceptum.properties @@ -28,6 +28,6 @@ instance.launch.locked.running=This instance is currently running.\ Click OK to start a second instance of the game.\ Please be aware that doing so is unsupported and WILL cause issues! instance.launch=Launch -main.empty.title=# Welcome to Inceptum +main.empty.title=Welcome to Inceptum main.empty.description=To get started, create (or import) a new instance using the + button instance.directory=Open Directory diff --git a/launcher-gtk/src/main/resources/inceptum_de.properties b/launcher-gtk/src/main/resources/inceptum_de.properties index a4c5e86..cce35e1 100644 --- a/launcher-gtk/src/main/resources/inceptum_de.properties +++ b/launcher-gtk/src/main/resources/inceptum_de.properties @@ -28,6 +28,6 @@ instance.launch.locked.running=Diese Instanz l Bestätigen sie den Start mit OK, um sie ein zweites mal zu starten.\ Bitte seien sie sich bewusst, dass dies zu Problemen führen wird und NICHT UNTERSÜTZT ist. instance.launch=Starten -main.empty.title=# Willkommen bei Inceptum +main.empty.title=Willkommen bei Inceptum main.empty.description=Importiere oder erstelle um anzufangen eine Instanz mit dem + instance.directory=Verzeichnis öffnen diff --git a/launcher-imgui/build.gradle.kts b/launcher-imgui/build.gradle.kts index 94294e8..020271d 100644 --- a/launcher-imgui/build.gradle.kts +++ b/launcher-imgui/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("inceptum.application-conventions") + id("inceptum.application") id("inceptum.manifold") } diff --git a/launcher/build.gradle.kts b/launcher/build.gradle.kts index 9b4bc1d..191ed55 100644 --- a/launcher/build.gradle.kts +++ b/launcher/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("inceptum.library-conventions") + id("inceptum.library") id("inceptum.gson-compile") id("inceptum.manifold") } diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/gson/MicrosoftAccountAdapter.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/gson/MicrosoftAccountAdapter.java index 6b97e3b..83c51a7 100644 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/gson/MicrosoftAccountAdapter.java +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/gson/MicrosoftAccountAdapter.java @@ -8,7 +8,7 @@ import io.gitlab.jfronny.inceptum.launcher.api.account.MicrosoftAccount; import java.io.IOException; public class MicrosoftAccountAdapter { - public static void write(JsonWriter writer, MicrosoftAccount value) throws IOException { + public static void write(MicrosoftAccount value, JsonWriter writer) throws IOException { GC_MicrosoftAccountMeta.write(value == null ? null : value.toMeta(), writer); } diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/gson/MinecraftArgumentAdapter.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/gson/MinecraftArgumentAdapter.java index a26e567..b93902c 100644 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/gson/MinecraftArgumentAdapter.java +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/gson/MinecraftArgumentAdapter.java @@ -11,7 +11,7 @@ import java.util.List; import java.util.Set; public class MinecraftArgumentAdapter { - public static void write(JsonWriter writer, MinecraftArgument rules) throws IOException { + public static void write(MinecraftArgument rules, JsonWriter writer) throws IOException { throw new UnsupportedOperationException(); } diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/gson/ModMetaSourcesAdapter.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/gson/ModMetaSourcesAdapter.java index b6da9dd..4e2893a 100644 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/gson/ModMetaSourcesAdapter.java +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/gson/ModMetaSourcesAdapter.java @@ -3,22 +3,22 @@ package io.gitlab.jfronny.inceptum.launcher.gson; import gsoncompile.extensions.io.gitlab.jfronny.inceptum.launcher.system.source.ModSource.GC_ModSource; import io.gitlab.jfronny.gson.stream.JsonReader; import io.gitlab.jfronny.gson.stream.JsonWriter; -import io.gitlab.jfronny.inceptum.launcher.model.inceptum.ModMeta$Sources; +import io.gitlab.jfronny.inceptum.launcher.model.inceptum.ModMeta.Sources; import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource; import java.io.IOException; import java.util.Optional; public class ModMetaSourcesAdapter { - public static void write(JsonWriter writer, ModMeta$Sources value) throws IOException { + public static void write(Sources value, JsonWriter writer) throws IOException { writer.beginArray(); for (ModSource source : value.keySet()) GC_ModSource.write(source, writer); writer.endArray(); } - public static ModMeta$Sources read(JsonReader reader) throws IOException { + public static Sources read(JsonReader reader) throws IOException { reader.beginArray(); - ModMeta$Sources sources = new ModMeta$Sources(); + Sources sources = new Sources(); while (reader.hasNext()) { sources.put(GC_ModSource.read(reader), Optional.empty()); } diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/gson/ModSourceAdapter.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/gson/ModSourceAdapter.java index 23bba6e..4234b3c 100644 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/gson/ModSourceAdapter.java +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/gson/ModSourceAdapter.java @@ -10,7 +10,7 @@ import java.util.LinkedHashSet; import java.util.Set; public class ModSourceAdapter { - public static void write(JsonWriter writer, ModSource src) throws IOException { + public static void write(ModSource src, JsonWriter writer) throws IOException { writer.beginObject(); if (src instanceof ModrinthModSource mo) { writer.name("type").value("modrinth") @@ -22,7 +22,7 @@ public class ModSourceAdapter { .name("dependencies"); writer.beginArray(); for (ModSource dependency : di.dependencies) { - write(writer, dependency); + write(dependency, writer); } writer.endArray(); } else if (src instanceof CurseforgeModSource cu) { diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/gson/RulesAdapter.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/gson/RulesAdapter.java index 6d35173..437a09d 100644 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/gson/RulesAdapter.java +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/gson/RulesAdapter.java @@ -9,7 +9,7 @@ import io.gitlab.jfronny.inceptum.launcher.model.mojang.Rules; import java.io.IOException; public class RulesAdapter { - public static void write(JsonWriter writer, Rules rules) throws IOException { + public static void write(Rules rules, JsonWriter writer) throws IOException { throw new UnsupportedOperationException(); } diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/model/inceptum/ModMeta$Sources.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/model/inceptum/ModMeta$Sources.java deleted file mode 100644 index e0cc080..0000000 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/model/inceptum/ModMeta$Sources.java +++ /dev/null @@ -1,79 +0,0 @@ -package io.gitlab.jfronny.inceptum.launcher.model.inceptum; - -import io.gitlab.jfronny.gson.compile.annotations.GSerializable; -import io.gitlab.jfronny.inceptum.common.GsonPreset; -import io.gitlab.jfronny.inceptum.launcher.gson.ModMetaSourcesAdapter; -import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.*; - -@GSerializable(with = ModMetaSourcesAdapter.class, configure = GsonPreset.Config.class) -public class ModMeta$Sources implements Map> { - private Map> delegate = new LinkedHashMap<>(); - - @Override - public int size() { - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public boolean containsKey(Object o) { - return delegate.containsKey(o); - } - - @Override - public boolean containsValue(Object o) { - return delegate.containsValue(o); - } - - @Override - public Optional get(Object o) { - return delegate[o]; - } - - @Nullable - @Override - public Optional put(ModSource modSource, Optional modSource2) { - return delegate.put(modSource, modSource2); - } - - @Override - public Optional remove(Object o) { - return delegate.remove(o); - } - - @Override - public void putAll(@NotNull Map> map) { - delegate.putAll(map); - } - - @Override - public void clear() { - delegate.clear(); - } - - @NotNull - @Override - public Set keySet() { - return delegate.keySet(); - } - - @NotNull - @Override - public Collection> values() { - return delegate.values(); - } - - @NotNull - @Override - public Set>> entrySet() { - return delegate.entrySet(); - } -} diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/model/inceptum/ModMeta.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/model/inceptum/ModMeta.java index ba19cb2..26b6747 100644 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/model/inceptum/ModMeta.java +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/model/inceptum/ModMeta.java @@ -1,14 +1,19 @@ package io.gitlab.jfronny.inceptum.launcher.model.inceptum; import io.gitlab.jfronny.commons.HashUtils; +import io.gitlab.jfronny.commons.data.MutCollection; +import io.gitlab.jfronny.commons.data.delegate.DelegateMap; import io.gitlab.jfronny.gson.compile.annotations.GPrefer; import io.gitlab.jfronny.gson.compile.annotations.GSerializable; import io.gitlab.jfronny.inceptum.common.GsonPreset; import io.gitlab.jfronny.inceptum.common.Utils; import io.gitlab.jfronny.inceptum.launcher.api.CurseforgeApi; import io.gitlab.jfronny.inceptum.launcher.api.ModrinthApi; +import io.gitlab.jfronny.inceptum.launcher.gson.ModMetaSourcesAdapter; import io.gitlab.jfronny.inceptum.launcher.model.curseforge.response.FingerprintMatchesResponse; -import io.gitlab.jfronny.inceptum.launcher.system.source.*; +import io.gitlab.jfronny.inceptum.launcher.system.source.CurseforgeModSource; +import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource; +import io.gitlab.jfronny.inceptum.launcher.system.source.ModrinthModSource; import org.jetbrains.annotations.Nullable; import java.io.IOException; @@ -17,16 +22,24 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.Optional; @GSerializable(configure = GsonPreset.Config.class) public record ModMeta( - ModMeta$Sources sources, //key: source, value: update + Sources sources, //key: source, value: update String sha1, Long murmur2, List dependents, // by file name List dependencies, // by file name boolean explicit ) { + @GSerializable(with = ModMetaSourcesAdapter.class, configure = GsonPreset.Config.class) + public static class Sources extends DelegateMap> { + public Sources() { + super(MutCollection.mapOf()); + } + } + @GPrefer public ModMeta {} @@ -43,7 +56,7 @@ public record ModMeta( } } return new ModMeta( - new ModMeta$Sources(), + new Sources(), sha1, murmur2, new ArrayList<>(), @@ -54,7 +67,7 @@ public record ModMeta( public static ModMeta of(String sha1, Long murmur2, @Nullable ModSource knownSource, String gameVersion) { ModMeta res = new ModMeta( - new ModMeta$Sources(), + new Sources(), sha1, murmur2, new ArrayList<>(), diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/instance/InstanceList.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/instance/InstanceList.java index d51cd11..4407791 100644 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/instance/InstanceList.java +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/instance/InstanceList.java @@ -19,7 +19,7 @@ public class InstanceList { public static void reset() { synchronized (metas) { - for (var entry : metas) { + for (var entry : metas.entrySet()) { try { entry.value.close(); } catch (IOException e) { diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/FileScanTask.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/FileScanTask.java index 1e6acbd..0c5f66f 100644 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/FileScanTask.java +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/FileScanTask.java @@ -6,6 +6,7 @@ import io.gitlab.jfronny.gson.JsonParseException; import io.gitlab.jfronny.inceptum.common.Utils; import io.gitlab.jfronny.inceptum.launcher.model.fabric.FabricModJson; import io.gitlab.jfronny.inceptum.launcher.model.inceptum.ModMeta; +import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance; import io.gitlab.jfronny.inceptum.launcher.system.instance.Mod; import io.gitlab.jfronny.inceptum.launcher.system.instance.ModPath; import io.gitlab.jfronny.inceptum.launcher.system.mds.noop.NoopMod; @@ -26,75 +27,53 @@ public record FileScanTask(ProtoInstance instance, Path file, BiConsumer void evaluateSources(Path modFile, MetadataRef ref) throws IOException, TEx { + private void discover(Path jarPath, Path imodPath) throws IOException, URISyntaxException { + boolean managed = false; + ModMeta meta; + if (Files.exists(imodPath)) meta = GC_ModMeta.read(imodPath); + else { + meta = ModMeta.of(jarPath); + GC_ModMeta.write(meta, imodPath); + } boolean modified = false; - if (ref.meta.initialize(gameVersion)) { - GC_ModMeta.write(ref.meta, ref.imodPath); + if (meta.initialize(gameVersion)) { + GC_ModMeta.write(meta, imodPath); modified = true; } ModSource selectedSource = null; - for (ModSource source : ref.meta.sources.keySet()) { + for (ModSource source : meta.sources.keySet()) { source.getUpdate(gameVersion); if (!Files.exists(source.jarPath)) source.download(); selectedSource = source; } if (selectedSource != null) { - if (Files.exists(modFile)) { - Files.delete(modFile); - Path newImod = ref.imodPath.parent.resolve(selectedSource.shortName + ModPath.EXT_IMOD); - Files.move(ref.imodPath, newImod); - ref.imodPath = newImod; + if (Files.exists(jarPath)) { + Files.delete(jarPath); + Path newImod = imodPath.parent.resolve(selectedSource.shortName + ModPath.EXT_IMOD); + Files.move(imodPath, newImod); + imodPath = newImod; modified = true; } - ref.jarPath = selectedSource.jarPath; - } - if (modified) ref.update(); - } + jarPath = selectedSource.jarPath; + managed = true; + } else if (!Files.exists(jarPath)) throw new IOException("Mod has no jar and no sources"); + if (modified) meta = GC_ModMeta.read(imodPath); - private @Nullable FabricModJson getFmj(Path modJarDefault, ModMeta md) throws IOException, URISyntaxException { - if (!Files.exists(modJarDefault)) { - if (md.sources.isEmpty()) { - throw new FileNotFoundException("Mod " + modJarDefault.fileName.toString() + " doesn't specify a source and has no file"); - } - modJarDefault = List.copyOf(md.sources.keySet())[0].jarPath; - } - try (FileSystem fs = Utils.openZipFile(modJarDefault, false)) { + FabricModJson fmj; + try (FileSystem fs = Utils.openZipFile(jarPath, false)) { Path fmjPath = fs.getPath("fabric.mod.json"); - if (!Files.exists(fmjPath)) return null; - return GC_FabricModJson.read(fmjPath); - } - } - - private static class MetadataRef { - public MetadataRef(Path imodPath, @Nullable Function defaultMeta) throws IOException { - this.imodPath = imodPath; - if (!Files.exists(imodPath) && defaultMeta != null) GC_ModMeta.write(defaultMeta.apply(imodPath), imodPath); - if (Files.exists(imodPath)) update(); + if (Files.exists(fmjPath)) fmj = GC_FabricModJson.read(fmjPath); + else fmj = null; } - public Path imodPath; - public Path jarPath; // filled in from evaluateSources - public ModMeta meta; - - public void update() throws IOException { - this.meta = GC_ModMeta.read(imodPath); - } + discovered.accept(imodPath, new MdsMod(instance, imodPath, jarPath, managed, meta, fmj)); } } diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/ModsDirScannerImpl.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/ModsDirScannerImpl.java index 4a4ce33..b3dfe60 100644 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/ModsDirScannerImpl.java +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/ModsDirScannerImpl.java @@ -131,8 +131,7 @@ class ModsDirScannerImpl implements ModsDirScanner { if (!key.reset()) Utils.LOGGER.warn("Could not reset config watch key"); } JFiles.listTo(instance.modsDir, path -> { - if (!descriptions.containsKey(path)) - toScan.add(path); + if (!descriptions.containsKey(path)) toScan.add(path); }); } for (Path p : toScan) { diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/setup/steps/DownloadAssetsStep.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/setup/steps/DownloadAssetsStep.java index d0cb9f5..ae4c71f 100644 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/setup/steps/DownloadAssetsStep.java +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/setup/steps/DownloadAssetsStep.java @@ -27,7 +27,7 @@ public class DownloadAssetsStep implements Step { info.setState("Downloading asset: " + entry.key); McApi.downloadAsset(entry.value, fPath); } - } catch (URISyntaxException e) { + } catch (Throwable e) { throw new IOException("Could not download assets", e); } } diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/util/ProcessUtils.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/util/ProcessUtils.java index 9ac15d5..34502c0 100644 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/util/ProcessUtils.java +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/util/ProcessUtils.java @@ -8,12 +8,12 @@ import java.io.InputStreamReader; public class ProcessUtils { public static boolean isProcessAlive(String pid) { - return isProcessIdRunning(pid, OSUtils.TYPE == OSUtils.Type.WINDOWS - ? "cmd /c tasklist /FI \"PID eq " + pid + "\"" - : "ps -p " + pid); + if (OSUtils.TYPE == OSUtils.Type.WINDOWS) + return isProcessIdRunning(pid, "tasklist", "/FI", "PID eq " + pid); + return isProcessIdRunning(pid, "ps", "-p", pid); } - private static boolean isProcessIdRunning(String pid, String command) { + private static boolean isProcessIdRunning(String pid, String... command) { try { Runtime rt = Runtime.getRuntime(); Process pr = rt.exec(command); diff --git a/launchwrapper/build.gradle.kts b/launchwrapper/build.gradle.kts index f6b897e..98c9cfc 100644 --- a/launchwrapper/build.gradle.kts +++ b/launchwrapper/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("inceptum.library-conventions") + id("inceptum.library") } java { diff --git a/packaging/arch-linux b/packaging/arch-linux index 11fb5ad..28770e5 160000 --- a/packaging/arch-linux +++ b/packaging/arch-linux @@ -1 +1 @@ -Subproject commit 11fb5ad2ad266bac007e7b11178fc3585cac45b9 +Subproject commit 28770e5269128412d8d51a03aa6c072a8eff10cb diff --git a/wrapper/build.gradle.kts b/wrapper/build.gradle.kts index 663f282..171c923 100644 --- a/wrapper/build.gradle.kts +++ b/wrapper/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("inceptum.application-standalone-conventions") + id("inceptum.application-standalone") } application {