--- /dev/null
+.idea/
+*.iml
+*.iws
+out/
+.idea_modules/
+.DS_Store
+/target/
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>de.unixwork</groupId>
+ <artifactId>rssreader</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <packaging>pom</packaging>
+
+ <name>rssreader</name>
+ <url>http://unixwork.de/</url>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <maven.compiler.release>24</maven.compiler.release>
+ </properties>
+
+ <modules>
+ <module>ui-java</module>
+ <module>ui-kotlin</module>
+ <module>rss-application</module>
+ </modules>
+
+ <dependencyManagement>
+ <dependencies>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ </dependencies>
+
+ <build>
+ <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
+ <plugins>
+
+ </plugins>
+ </pluginManagement>
+ </build>
+</project>
--- /dev/null
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>de.unixwork</groupId>
+ <artifactId>rss-application</artifactId>
+ <version>1.0-SNAPSHOT</version>
+
+ <parent>
+ <artifactId>rssreader</artifactId>
+ <groupId>de.unixwork</groupId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <kotlin.code.style>official</kotlin.code.style>
+ <kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
+ </properties>
+
+ <repositories>
+ <repository>
+ <id>mavenCentral</id>
+ <url>https://repo1.maven.org/maven2/</url>
+ </repository>
+ </repositories>
+
+ <build>
+ <sourceDirectory>src/main/kotlin</sourceDirectory>
+ <testSourceDirectory>src/test/kotlin</testSourceDirectory>
+ <plugins>
+ <plugin>
+ <groupId>org.jetbrains.kotlin</groupId>
+ <artifactId>kotlin-maven-plugin</artifactId>
+ <version>2.1.20</version>
+ <executions>
+ <execution>
+ <id>compile</id>
+ <phase>compile</phase>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>test-compile</id>
+ <phase>test-compile</phase>
+ <goals>
+ <goal>test-compile</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.22.2</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <version>2.22.2</version>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>1.6.0</version>
+ <configuration>
+ <mainClass>MainKt</mainClass>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.jetbrains.kotlin</groupId>
+ <artifactId>kotlin-test-junit5</artifactId>
+ <version>2.1.20</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter</artifactId>
+ <version>5.10.0</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jetbrains.kotlin</groupId>
+ <artifactId>kotlin-stdlib</artifactId>
+ <version>2.1.20</version>
+ </dependency>
+
+ <dependency>
+ <groupId>de.unixwork</groupId>
+ <artifactId>ui-kotlin</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+
+</project>
--- /dev/null
+package de.unixwork
+
+//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
+// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
+fun main() {
+ val name = "Kotlin"
+ //TIP Press <shortcut actionId="ShowIntentionActions"/> with your caret at the highlighted text
+ // to see how IntelliJ IDEA suggests fixing it.
+ println("Hello, " + name + "!")
+
+ for (i in 1..5) {
+ //TIP Press <shortcut actionId="Debug"/> to start debugging your code. We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
+ // for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>.
+ println("i = $i")
+ }
+}
\ No newline at end of file
--- /dev/null
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>de.unixwork</groupId>
+ <artifactId>ui-java</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>24</source>
+ <target>24</target>
+ <compilerArgs>
+ <arg>--enable-preview</arg>
+ </compilerArgs>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>3.1.0</version> <!-- Or newer -->
+ <executions>
+ <execution>
+ <goals>
+ <goal>java</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <mainClass>de.unixwork.ui.demo.Main</mainClass>
+ <classpathScope>test</classpathScope>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <parent>
+ <artifactId>rssreader</artifactId>
+ <groupId>de.unixwork</groupId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+
+ <properties>
+ <maven.compiler.source>24</maven.compiler.source>
+ <maven.compiler.target>24</maven.compiler.target>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+
+</project>
--- /dev/null
+package de.unixwork.ui;
+
+public interface Application {
+
+ void startup();
+
+ default void shutdown() {
+
+ }
+}
--- /dev/null
+package de.unixwork.ui;
+
+import java.lang.foreign.*;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+public class Toolkit {
+ private static Toolkit instance;
+
+ private Application app;
+
+ // used for all strings and other memory, that is expected to be const
+ // and the UI toolkit does not create copies
+ private final Arena staticArena = Arena.ofShared();
+
+ private Linker linker;
+
+ private SymbolLookup lib;
+ private MethodHandle mainFunc;
+
+ private Toolkit(String appName) {
+ // load shared library
+ System.loadLibrary("uitk");
+ linker = Linker.nativeLinker();
+ lib = SymbolLookup.loaderLookup();
+
+ // get init function
+ MemorySegment ui_init_addr = lib.find("ui_init").orElseThrow();
+ FunctionDescriptor ui_init_sig = FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.JAVA_INT, ValueLayout.ADDRESS);
+ MethodHandle ui_init = linker.downcallHandle(ui_init_addr, ui_init_sig);
+
+ // create static/const C string
+ MemorySegment appNameCStr = staticArena.allocateFrom(appName);
+ try {
+ ui_init.invoke(appNameCStr, 0, MemorySegment.NULL);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+
+ initFunctions();
+
+ UiObjectFuncs.init(linker, lib);
+ }
+
+ private void initFunctions() {
+ FunctionDescriptor sigv_v = FunctionDescriptor.ofVoid(); // void func(void)
+
+ MemorySegment ui_main_addr = lib.find("ui_main").orElseThrow();
+ mainFunc = linker.downcallHandle(ui_main_addr, sigv_v);
+ }
+
+ private static Toolkit getInstance() {
+ if(Toolkit.instance == null) {
+ Toolkit.init("app1");
+ }
+ return Toolkit.instance;
+ }
+
+ public static void init(String appName) {
+ if(Toolkit.instance != null) {
+ return;
+ }
+ Toolkit.instance = new Toolkit(appName);
+ }
+
+ private static void onStartup(MemorySegment unused1, MemorySegment unused2) {
+ Toolkit toolkit = Toolkit.getInstance();
+ if(toolkit.app != null) {
+ toolkit.app.startup();
+ }
+ }
+
+ private static void onShutdown(MemorySegment unused1, MemorySegment unused2) {
+ Toolkit toolkit = Toolkit.getInstance();
+ if(toolkit.app != null) {
+ toolkit.app.shutdown();
+ }
+ }
+
+ private static void onOpen(MemorySegment unused1, MemorySegment unused2) {
+ Toolkit toolkit = Toolkit.getInstance();
+ if(toolkit.app != null) {
+ // TODO
+ }
+ }
+
+ public static void runApplication(Application app) {
+ Toolkit toolkit = Toolkit.getInstance();
+ toolkit.app = app;
+
+ // create method handles for native functions
+ FunctionDescriptor handlerSig = FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.ADDRESS);
+ MethodHandle onstartup = toolkit.linker.downcallHandle(toolkit.lib.find("ui_onstartup").orElseThrow(), handlerSig);
+ MethodHandle onshutdown = toolkit.linker.downcallHandle(toolkit.lib.find("ui_onexit").orElseThrow(), handlerSig);
+ MethodHandle onopen = toolkit.linker.downcallHandle(toolkit.lib.find("ui_onopen").orElseThrow(), handlerSig);
+
+ // get java wrapper method handles
+ MethodHandle onstartupCallback = null;
+ MethodHandle onshutdownCallback = null;
+ MethodHandle onopenCallback = null;
+ try {
+ onstartupCallback = MethodHandles.lookup().findStatic(
+ Toolkit.class,
+ "onStartup",
+ MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class));
+ onshutdownCallback = MethodHandles.lookup().findStatic(
+ Toolkit.class,
+ "onShutdown",
+ MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class));
+ onopenCallback = MethodHandles.lookup().findStatic(
+ Toolkit.class,
+ "onOpen",
+ MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class));
+ } catch (NoSuchMethodException | IllegalAccessException e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+
+ try (Arena arena = Arena.ofConfined()) {
+ // create native function pointers for java methods
+ FunctionDescriptor callbackDescriptor = FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.ADDRESS);
+ MemorySegment onStartupFunc = toolkit.linker.upcallStub(
+ onstartupCallback,
+ callbackDescriptor,
+ arena);
+ MemorySegment onShutdownFunc = toolkit.linker.upcallStub(
+ onshutdownCallback,
+ callbackDescriptor,
+ arena);
+ MemorySegment onOpenFunc = toolkit.linker.upcallStub(
+ onopenCallback,
+ callbackDescriptor,
+ arena);
+
+ // register java application callbacks
+ onstartup.invoke(onStartupFunc, MemorySegment.NULL);
+ onshutdown.invoke(onShutdownFunc, MemorySegment.NULL);
+ onopen.invoke(onOpenFunc, MemorySegment.NULL);
+
+ // run ui_main(), which invokes the application callback functions
+ Toolkit.mainloop();
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /*
+ * usually this should not be called directly, use runApplication instead
+ */
+ public static void mainloop() {
+ try {
+ Toolkit.getInstance().mainFunc.invoke();
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
--- /dev/null
+package de.unixwork.ui;
+
+import java.lang.foreign.MemorySegment;
+
+public class UiObject {
+ MemorySegment ptr;
+
+ UiObject(MemorySegment ptr) {
+ this.ptr = ptr;
+ }
+
+ public static UiObject createWindow(String title) {
+ return UiObjectFuncs.instance.window(title);
+ }
+
+ public void show() {
+ UiObjectFuncs.instance.show(this);
+ }
+}
--- /dev/null
+package de.unixwork.ui;
+
+import java.lang.foreign.*;
+import java.lang.invoke.MethodHandle;
+
+class UiObjectFuncs {
+ static UiObjectFuncs instance;
+
+ MethodHandle ui_show;
+ MethodHandle ui_window;
+
+ private UiObjectFuncs(Linker linker, SymbolLookup lib) {
+ // void* func(void*, void*)
+ FunctionDescriptor sigm_mm = FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.ADDRESS);
+ // void func(void*)
+ FunctionDescriptor sigv_m = FunctionDescriptor.ofVoid(ValueLayout.ADDRESS);
+
+ MemorySegment ui_show_addr = lib.find("ui_show").orElseThrow();
+ MemorySegment ui_window_addr = lib.find("ui_window").orElseThrow();
+
+ ui_show = linker.downcallHandle(ui_show_addr, sigv_m);
+ ui_window = linker.downcallHandle(ui_window_addr, sigm_mm);
+ }
+
+ // must be called by the Toolkit constructor
+ static void init(Linker linker, SymbolLookup lib) {
+ UiObjectFuncs.instance = new UiObjectFuncs(linker, lib);
+ }
+
+ UiObject window(String title) {
+ MemorySegment obj = null;
+ try (Arena arena = Arena.ofConfined()) {
+ MemorySegment cstr = arena.allocateFrom(title);
+ obj = (MemorySegment) ui_window.invoke(cstr, MemorySegment.NULL);
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+
+ return new UiObject(obj);
+ }
+
+ void show(UiObject obj) {
+ try {
+ ui_show.invoke(obj.ptr);
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
--- /dev/null
+package de.unixwork.ui.demo;
+
+import de.unixwork.ui.*;
+
+public class Main implements Application{
+ public void startup() {
+ UiObject window = UiObject.createWindow("Test Window");
+ window.show();
+ }
+
+ public static void main(String[] args) {
+ System.out.println("UI Demo");
+ Toolkit.init("testapp");
+
+ Toolkit.runApplication(new Main());
+ }
+}
--- /dev/null
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>de.unixwork</groupId>
+ <artifactId>ui-kotlin</artifactId>
+ <version>1.0-SNAPSHOT</version>
+
+ <parent>
+ <artifactId>rssreader</artifactId>
+ <groupId>de.unixwork</groupId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <kotlin.code.style>official</kotlin.code.style>
+ <kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
+ </properties>
+
+ <repositories>
+ <repository>
+ <id>mavenCentral</id>
+ <url>https://repo1.maven.org/maven2/</url>
+ </repository>
+ </repositories>
+
+ <build>
+ <sourceDirectory>src/main/kotlin</sourceDirectory>
+ <testSourceDirectory>src/test/kotlin</testSourceDirectory>
+ <plugins>
+ <plugin>
+ <groupId>org.jetbrains.kotlin</groupId>
+ <artifactId>kotlin-maven-plugin</artifactId>
+ <version>2.1.20</version>
+ <executions>
+ <execution>
+ <id>compile</id>
+ <phase>compile</phase>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>test-compile</id>
+ <phase>test-compile</phase>
+ <goals>
+ <goal>test-compile</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.22.2</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <version>2.22.2</version>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.jetbrains.kotlin</groupId>
+ <artifactId>kotlin-test-junit5</artifactId>
+ <version>2.1.20</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter</artifactId>
+ <version>5.10.0</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jetbrains.kotlin</groupId>
+ <artifactId>kotlin-stdlib</artifactId>
+ <version>2.1.20</version>
+ </dependency>
+
+ <dependency>
+ <groupId>de.unixwork</groupId>
+ <artifactId>ui-java</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+
+</project>