├── .idea ├── .name ├── codeStyles │ └── codeStyleConfig.xml ├── vcs.xml ├── kotlinc.xml ├── php.xml ├── misc.xml └── inspectionProfiles │ └── Project_Default.xml ├── jitpack.yml ├── .travis.yml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gl ├── src │ ├── test │ │ ├── kotlin │ │ │ ├── uno │ │ │ │ ├── gln.kt │ │ │ │ └── helloWorld.kt │ │ │ ├── gln │ │ │ │ └── 01 clear.kt │ │ │ ├── tests │ │ │ │ ├── Clipboard.kt │ │ │ │ └── Cursor.kt │ │ │ └── examples │ │ │ │ └── gears.kt │ │ └── java │ │ │ └── HelloWorld.java │ └── main │ │ └── kotlin │ │ └── uno │ │ └── gl │ │ └── GlWindow.kt └── gl.gradle.kts ├── core ├── src │ ├── test │ │ ├── resources │ │ │ └── main │ │ │ │ ├── ProggyClean.ttf │ │ │ │ ├── shader.frag │ │ │ │ ├── shader.vert │ │ │ │ ├── semantic.glsl │ │ │ │ └── proggyClean_compressedBase85 │ │ ├── kotlin │ │ │ └── uno │ │ │ │ ├── bug.kt │ │ │ │ ├── caps.kt │ │ │ │ ├── testGli.kt │ │ │ │ ├── test.kt │ │ │ │ ├── ascii85.kt │ │ │ │ └── awe.kt │ │ └── java │ │ │ └── Test.java │ └── main │ │ └── kotlin │ │ ├── uno │ │ ├── glm │ │ │ └── ext.kt │ │ ├── glfw │ │ │ ├── GlfwWindowInterface.kt │ │ │ ├── InitHint.kt │ │ │ ├── monitor.kt │ │ │ ├── Joystick.kt │ │ │ ├── inputEnums.kt │ │ │ ├── helpers.kt │ │ │ └── Hints.kt │ │ ├── kotlin │ │ │ ├── kool.kt │ │ │ ├── speakablePointers.kt │ │ │ ├── Quadruple.kt │ │ │ ├── Quintuple.kt │ │ │ ├── result.kt │ │ │ └── util.kt │ │ ├── convert │ │ │ ├── decode85.kt │ │ │ └── ascii85.kt │ │ ├── time │ │ │ └── timer.kt │ │ └── buffer │ │ │ ├── multiple constuctors.kt │ │ │ └── advanced of.kt │ │ └── module-info └── core.gradle.kts ├── platform └── platform.gradle.kts ├── vk └── src │ └── main │ ├── java │ └── module-info │ └── kotlin │ └── uno │ └── vk │ └── glfw vk.kt ├── awt ├── src │ ├── main │ │ ├── java │ │ │ └── module-info │ │ └── kotlin │ │ │ └── uno │ │ │ └── awt │ │ │ ├── helpers.kt │ │ │ └── LwjglCanvas.kt │ └── test │ │ └── kotlin │ │ └── uno │ │ ├── jawt │ │ ├── JAWTDemo.java │ │ ├── JawtDemo.kt │ │ ├── HelloTinyFD.java │ │ ├── AbstractGears.kt │ │ └── LWJGLCanvas.java │ │ └── AwtTest.kt └── awt.gradle.kts ├── LICENSE ├── settings.gradle.kts ├── gradlew.bat ├── README.md ├── .github └── workflows │ └── build.yml ├── .gitignore └── gradlew /.idea/.name: -------------------------------------------------------------------------------- 1 | uno -------------------------------------------------------------------------------- /jitpack.yml: -------------------------------------------------------------------------------- 1 | jdk: 2 | - openjdk11 -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk11 -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-XX:MaxMetaspaceSize=1g 2 | org.gradle.daemon=false 3 | platformVersion=0.2.8+44 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kotlin-graphics/uno-sdk/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gl/src/test/kotlin/uno/gln.kt: -------------------------------------------------------------------------------- 1 | package uno 2 | 3 | import gln.identifiers.GlBuffer 4 | 5 | inline fun glGenBuffer(): GlBuffer = GlBuffer() -------------------------------------------------------------------------------- /core/src/test/resources/main/ProggyClean.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kotlin-graphics/uno-sdk/HEAD/core/src/test/resources/main/ProggyClean.ttf -------------------------------------------------------------------------------- /core/src/main/kotlin/uno/glm/ext.kt: -------------------------------------------------------------------------------- 1 | package uno.glm 2 | 3 | import glm_.L 4 | import java.math.BigDecimal 5 | 6 | 7 | fun Int.toBidDec() = BigDecimal.valueOf(L) -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /core/src/main/kotlin/uno/glfw/GlfwWindowInterface.kt: -------------------------------------------------------------------------------- 1 | package uno.glfw 2 | 3 | /** 4 | * Created by GBarbieri on 24.04.2017. 5 | */ 6 | 7 | // --- [ glfwCreateWindow ] --- 8 | 9 | interface GlfwWindowInterface { 10 | 11 | fun makeContextCurrent(current: Boolean) 12 | } -------------------------------------------------------------------------------- /core/src/test/kotlin/uno/bug.kt: -------------------------------------------------------------------------------- 1 | package uno 2 | 3 | import uno.glfw.GlfwWindow 4 | import uno.glfw.glfw 5 | 6 | fun main() { 7 | // init 8 | glfw.init("3.3") 9 | 10 | // The window handle 11 | val glfwWindow = GlfwWindow(1280, 720, "ImGui Lwjgl OpenGL3 example") 12 | } -------------------------------------------------------------------------------- /core/src/test/resources/main/shader.frag: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | #include semantic.glsl 4 | 5 | uniform sampler2D myTexture; 6 | 7 | in vec2 uv; 8 | 9 | layout (location = FRAG_COLOR) out vec4 outputColor; 10 | 11 | void main() 12 | { 13 | outputColor = texture(myTexture, uv); 14 | } -------------------------------------------------------------------------------- /platform/platform.gradle.kts: -------------------------------------------------------------------------------- 1 | 2 | plugins { 3 | `java-platform` 4 | } 5 | 6 | dependencies { 7 | // The platform declares constraints on all components that require alignment 8 | constraints { 9 | api(projects.awt) 10 | api(projects.core) 11 | api(projects.gl) 12 | } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip 6 | distributionSha256Sum=ff7bf6a86f09b9b2c40bb8f48b25fc19cf2b2664fd1d220cd7ab833ec758d0d7 -------------------------------------------------------------------------------- /core/src/test/resources/main/shader.vert: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | #include semantic.glsl 4 | 5 | layout (location = POSITION) in vec3 position; 6 | layout (location = TEXT_COORD) in vec2 texCoord; 7 | 8 | uniform mat4 matrix; 9 | 10 | out vec2 uv; 11 | 12 | void main() 13 | { 14 | uv = texCoord; 15 | gl_Position = matrix * vec4(position, 1); 16 | } -------------------------------------------------------------------------------- /vk/src/main/java/module-info: -------------------------------------------------------------------------------- 1 | module kotlin.graphics.uno.vk { 2 | 3 | requires kotlin.stdlib; 4 | 5 | requires org.lwjgl; 6 | requires org.lwjgl.vulkan; 7 | requires org.lwjgl.glfw; 8 | 9 | requires kotlin.graphics.uno.core; 10 | requires kotlin.graphics.vkk; 11 | // requires kotlin.graphics.kool; 12 | 13 | exports uno.vk; 14 | } -------------------------------------------------------------------------------- /awt/src/main/java/module-info: -------------------------------------------------------------------------------- 1 | module kotlin.graphics.uno.awt { 2 | 3 | requires kotlin.stdlib; 4 | 5 | requires kotlin.graphics.gln; 6 | requires kotlin.graphics.glm; 7 | requires kotlin.graphics.uno.core; 8 | requires kotlin.graphics.kool; 9 | 10 | requires org.lwjgl.glfw; 11 | requires org.lwjgl.opengl; 12 | requires org.lwjgl.jawt; 13 | 14 | requires java.desktop; 15 | 16 | exports uno.awt; 17 | } -------------------------------------------------------------------------------- /core/src/test/resources/main/semantic.glsl: -------------------------------------------------------------------------------- 1 | // Attributes 2 | #define POSITION 0 3 | #define TEXT_COORD 1 4 | #define COLOR 2 5 | #define NORMAL 3 6 | #define UV_RED 4 7 | #define UV_GREEN 5 8 | #define UV_BLUE 6 9 | 10 | // Outputs 11 | #define FRAG_COLOR 0 12 | 13 | precision highp float; 14 | precision highp int; 15 | layout (std140, column_major) uniform; 16 | //layout (std430, column_major) buffer; // no ssbo in <4.3 -------------------------------------------------------------------------------- /.idea/php.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | 10 | 12 | -------------------------------------------------------------------------------- /core/src/test/kotlin/uno/caps.kt: -------------------------------------------------------------------------------- 1 | package uno 2 | 3 | import io.kotest.core.spec.style.StringSpec 4 | import uno.glfw.GlfwWindow 5 | import uno.glfw.glfw 6 | 7 | class caps : StringSpec() { 8 | 9 | init { 10 | 11 | "caps" { 12 | 13 | // glfw.init() 14 | // 15 | // GlfwWindow(640, "title").apply { makeContextCurrent() } 16 | // 17 | // GL.createCapabilities() 18 | // 19 | // val caps = Caps(Caps.Profile.CORE) 20 | // 21 | // println() 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /core/src/main/kotlin/uno/kotlin/kool.kt: -------------------------------------------------------------------------------- 1 | package uno.kotlin 2 | 3 | import kool.BYTES 4 | import kool.Ptr 5 | import kool.toPtr 6 | import org.lwjgl.system.MemoryStack 7 | 8 | fun MemoryStack.ptrUByte(size: Int = 1): Ptr = nmalloc(UByte.BYTES, size shl 1).toPtr() 9 | fun MemoryStack.ptrUShort(size: Int = 1): Ptr = nmalloc(UShort.BYTES, size shl 1).toPtr() 10 | fun MemoryStack.ptrInt(size: Int = 1): Ptr = nmalloc(Int.BYTES, size shl 2).toPtr() 11 | fun MemoryStack.ptrLong(size: Int = 1): Ptr = nmalloc(Long.BYTES, size shl 3).toPtr() 12 | fun MemoryStack.ptrFloat(size: Int = 1): Ptr = nmalloc(Float.BYTES, size shl 2).toPtr() 13 | -------------------------------------------------------------------------------- /core/src/main/kotlin/uno/glfw/InitHint.kt: -------------------------------------------------------------------------------- 1 | package uno.glfw 2 | 3 | import glm_.i 4 | import org.lwjgl.glfw.GLFW.* 5 | 6 | class InitHint { 7 | 8 | var joystickHatButtons = true 9 | set(value) { 10 | glfwInitHint(GLFW_JOYSTICK_HAT_BUTTONS, value.i) 11 | field = value 12 | } 13 | 14 | var cocoaChdirResources = true 15 | set(value) { 16 | glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, value.i) 17 | field = value 18 | } 19 | 20 | var cocoaMenubar = true 21 | set(value) { 22 | glfwInitHint(GLFW_COCOA_MENUBAR, value.i) 23 | field = value 24 | } 25 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/module-info: -------------------------------------------------------------------------------- 1 | module kotlin.graphics.uno.core { 2 | 3 | requires kotlin.stdlib; 4 | 5 | requires java.desktop; 6 | 7 | requires org.lwjgl; 8 | 9 | requires kotlin.graphics.gln; 10 | // requires com.github.kotlin_graphics.gli; 11 | requires kotlin.graphics.glm; 12 | requires kotlin.graphics.kool; 13 | requires kotlin.graphics.unsigned; 14 | requires org.lwjgl.glfw; 15 | requires org.lwjgl.opengl; 16 | 17 | exports uno.buffer; 18 | exports uno.convert; 19 | exports uno.glfw; 20 | exports uno.glm; 21 | exports uno.kotlin; 22 | // exports uno.mousePole; 23 | exports uno.stb; 24 | exports uno.time; 25 | } -------------------------------------------------------------------------------- /core/src/test/java/Test.java: -------------------------------------------------------------------------------- 1 | import glm_.vec2.Vec2i; 2 | import org.lwjgl.system.MemoryUtil; 3 | import uno.glfw.GlfwWindow; 4 | import uno.glfw.glfw; 5 | 6 | 7 | public class Test { 8 | 9 | 10 | @org.junit.jupiter.api.Test 11 | void javaGlfwWindow() { 12 | glfw glfw = uno.glfw.glfw.INSTANCE; 13 | glfw.init(); 14 | GlfwWindow window = GlfwWindow.create(1280, 720, "Dear ImGui GLFW+OpenGL3 OpenGL example", MemoryUtil.NULL, null, new Vec2i(30)); 15 | assert(window.getHandle() != MemoryUtil.NULL); 16 | } 17 | // GlfwWindow w1 = GlfwWindow.Companion.(1280, 720, "Dear ImGui GLFW+OpenGL3 OpenGL example", MemoryUtil.NULL, null, new Vec2i(30)); 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/kotlin/uno/kotlin/speakablePointers.kt: -------------------------------------------------------------------------------- 1 | package uno.kotlin 2 | 3 | import glm_.has 4 | import kool.Ptr 5 | import kool.toPtr 6 | import kotlin.random.Random 7 | 8 | // TODO remove when kool is updated 9 | val Ptr<*>.speakable: String 10 | get() = formatUidDigit(adr) 11 | 12 | private val evenLetters = "bcdfghlmnprstwx" 13 | 14 | private val oddLetters = "aeiou" 15 | private fun formatUidDigit(n: ULong, level: Int = 0): String = when { 16 | n != 0uL -> { 17 | val letters = if (level has 1) oddLetters else evenLetters 18 | val base = letters.length.toULong() 19 | val s = formatUidDigit(n / base, level + 1) 20 | s + letters[(n % base).toInt()] 21 | } 22 | 23 | else -> "" 24 | } 25 | 26 | fun main() { 27 | 28 | for (i in 0..99) { 29 | val k = Random.nextLong(1000000, 2000000).toPtr() 30 | println("$k, ${k.speakable}") 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /core/src/main/kotlin/uno/convert/decode85.kt: -------------------------------------------------------------------------------- 1 | package uno.convert 2 | 3 | import glm_.i 4 | import glm_.uc 5 | 6 | fun decode85(src: String): CharArray { 7 | 8 | fun decode85Byte(c: Char) = (if (c >= '\\') c - 36 else c - 35).i 9 | 10 | var pSrc = 0 11 | val dst = CharArray(((src.length + 4) / 5) * 4) 12 | var pDst = 0 13 | 14 | while (pSrc < src.length - 1) { 15 | 16 | // if(pDst == 3036) 17 | // println("${src[pSrc]}, ${src[pSrc+1]}, ${src[pSrc+2]}, ${src[pSrc+3]}, ${src[pSrc+4]}") 18 | 19 | val tmp = decode85Byte(src[pSrc]) + 85 * (decode85Byte(src[pSrc + 1]) + 85 * (decode85Byte(src[pSrc + 2]) + 20 | 85 * (decode85Byte(src[pSrc + 3]) + 85 * decode85Byte(src[pSrc + 4])))) 21 | dst[pDst] = ((tmp ushr 0) and 0xFF).uc 22 | dst[pDst + 1] = ((tmp ushr 8) and 0xFF).uc 23 | dst[pDst + 2] = ((tmp ushr 16) and 0xFF).uc 24 | dst[pDst + 3] = ((tmp ushr 24) and 0xFF).uc // We can't assume little-endianness. 25 | pSrc += 5 26 | pDst += 4 27 | } 28 | return dst 29 | } -------------------------------------------------------------------------------- /gl/src/test/kotlin/gln/01 clear.kt: -------------------------------------------------------------------------------- 1 | package gln 2 | 3 | import glm_.f 4 | import glm_.i 5 | import gln.ClearBufferMask.Companion.COLOR_BUFFER_BIT 6 | import org.lwjgl.system.APIUtil 7 | import org.lwjgl.system.Library 8 | import org.lwjgl.system.windows.User32 9 | import uno.gl.GlWindow 10 | import uno.glfw.GlfwWindow 11 | import uno.glfw.glfw 12 | 13 | fun main() { 14 | 15 | val USER32 = Library.loadNative(User32::class.java, "org.lwjgl", "user32") 16 | 17 | val GetWindowLong = APIUtil.apiGetFunctionAddress(USER32, "GetWindowLong") 18 | println(GetWindowLong) 19 | 20 | glfw.init("3.3") 21 | Clear().run() 22 | } 23 | 24 | private class Clear : GlWindow(GlfwWindow(1280, 720, "[GLN] clear")) { 25 | 26 | init { 27 | init() 28 | sizeCB = { _, size -> gl.viewport(size) } 29 | } 30 | 31 | fun run() = loop { 32 | 33 | val sec = glfw.time.i 34 | 35 | gl.clearColor( 36 | (sec % 3 == 0).f, 37 | (sec % 3 == 1).f, 38 | (sec % 3 == 2).f, 0f) 39 | 40 | gl.clear(COLOR_BUFFER_BIT) 41 | } 42 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Giuseppe Barbieri 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | //fun prefix(prefix: String) { 2 | // rootProject.name = "$prefix-${rootProject.name}" 3 | // rootProject.children.forEach { it.name = "$prefix-${it.name}" } 4 | //} 5 | // 6 | //fun postfix(postfix: String) { 7 | // rootProject.name = "${rootProject.name}-$postfix" 8 | // rootProject.children.forEach { it.name = "${it.name}-$postfix" } 9 | //} 10 | 11 | pluginManagement { 12 | // repositories { 13 | // gradlePluginPortal() 14 | // maven("https://raw.githubusercontent.com/kotlin-graphics/mary/master") 15 | // } 16 | // includeBuild("../build-logic") 17 | // includeBuild("../magik") 18 | } 19 | 20 | for (module in listOf("core", "awt", "gl"/*, "vk"*/, "platform")) { 21 | include(module) 22 | project(":$module").buildFileName = "$module.gradle.kts" 23 | } 24 | 25 | rootProject.name = "uno" 26 | 27 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") 28 | 29 | //prefix("uno") 30 | //postfix("jdk8") 31 | 32 | gradle.rootProject { 33 | group = "kotlin.graphics" 34 | version = "0.7.21" 35 | } 36 | 37 | dependencyResolutionManagement { 38 | repositories { 39 | mavenCentral() 40 | maven("https://raw.githubusercontent.com/kotlin-graphics/mary/master") 41 | } 42 | } -------------------------------------------------------------------------------- /vk/src/main/kotlin/uno/vk/glfw vk.kt: -------------------------------------------------------------------------------- 1 | package uno.vk 2 | 3 | import kool.adr 4 | import kool.mInt 5 | import org.lwjgl.glfw.GLFWVulkan 6 | import org.lwjgl.system.MemoryUtil.* 7 | import uno.glfw.GlfwWindow 8 | import uno.glfw.glfw 9 | import uno.glfw.stak 10 | import vkk.VK_CHECK_RESULT 11 | import vkk.entities.VkSurfaceKHR 12 | import vkk.identifiers.Instance 13 | 14 | // --- [ glfwVulkanSupported ] --- 15 | val glfw.vulkanSupported: Boolean 16 | get() = GLFWVulkan.glfwVulkanSupported() 17 | 18 | // --- [ glfwGetRequiredInstanceExtensions ] --- 19 | val glfw.requiredInstanceExtensions: ArrayList 20 | get() = stak { 21 | val pCount = it.mInt() 22 | val ppNames = GLFWVulkan.nglfwGetRequiredInstanceExtensions(pCount.adr) 23 | val count = pCount[0] 24 | if (count == 0) arrayListOf() 25 | else { 26 | val pNames = memPointerBuffer(ppNames, count) 27 | val res = ArrayList(count) 28 | for (i in 0 until count) 29 | res += memASCII(pNames[i]) 30 | res 31 | } 32 | } 33 | 34 | // --- [ glfwCreateWindowSurface ] --- 35 | infix fun Instance.createSurface(window: GlfwWindow): VkSurfaceKHR = 36 | VkSurfaceKHR(stak.longAdr { VK_CHECK_RESULT(GLFWVulkan.nglfwCreateWindowSurface(adr, window.handle.value, NULL, it)) }) -------------------------------------------------------------------------------- /gl/gl.gradle.kts: -------------------------------------------------------------------------------- 1 | import magik.createGithubPublication 2 | import magik.github 3 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask 4 | import org.lwjgl.Lwjgl.Module.* 5 | import org.lwjgl.lwjgl 6 | 7 | plugins { 8 | kotlin("jvm") 9 | id("org.lwjgl.plugin") 10 | id("elect86.magik") 11 | `maven-publish` 12 | } 13 | 14 | group = rootProject.group 15 | version = rootProject.version 16 | 17 | dependencies { 18 | implementation(projects.core) 19 | 20 | api("kotlin.graphics:gln:0.5.32") 21 | lwjgl { implementation(glfw, jemalloc, opengl) } 22 | 23 | testImplementation("io.kotest:kotest-runner-junit5:5.5.5") 24 | testImplementation("io.kotest:kotest-assertions-core:5.5.5") 25 | } 26 | 27 | kotlin.jvmToolchain { languageVersion.set(JavaLanguageVersion.of(8)) } 28 | 29 | tasks { 30 | withType>().configureEach { compilerOptions.freeCompilerArgs.add("-opt-in=kotlin.RequiresOptIn") } 31 | test { useJUnitPlatform() } 32 | } 33 | 34 | publishing { 35 | publications { 36 | createGithubPublication { 37 | from(components["java"]) 38 | artifactId = "${rootProject.name}-${project.name}" 39 | suppressAllPomMetadataWarnings() 40 | } 41 | } 42 | repositories.github { domain = "kotlin-graphics/mary" } 43 | } 44 | 45 | java.withSourcesJar() -------------------------------------------------------------------------------- /core/core.gradle.kts: -------------------------------------------------------------------------------- 1 | import magik.createGithubPublication 2 | import magik.github 3 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask 4 | import org.lwjgl.Lwjgl.Module.glfw 5 | import org.lwjgl.Lwjgl.Module.jemalloc 6 | import org.lwjgl.lwjgl 7 | 8 | plugins { 9 | kotlin("jvm") 10 | id("org.lwjgl.plugin") 11 | id("elect86.magik") 12 | `maven-publish` 13 | } 14 | 15 | group = rootProject.group 16 | version = rootProject.version 17 | 18 | dependencies { 19 | implementation(kotlin("reflect")) 20 | 21 | api("kotlin.graphics:glm:0.9.9.1-11") 22 | lwjgl { implementation(glfw, jemalloc) } 23 | 24 | testImplementation("io.kotest:kotest-runner-junit5:5.5.5") 25 | testImplementation("io.kotest:kotest-assertions-core:5.5.5") 26 | } 27 | 28 | kotlin.jvmToolchain { languageVersion.set(JavaLanguageVersion.of(8)) } 29 | 30 | tasks { 31 | withType>().configureEach { compilerOptions.freeCompilerArgs.add("-opt-in=kotlin.RequiresOptIn") } 32 | test { useJUnitPlatform() } 33 | } 34 | 35 | publishing { 36 | publications { 37 | createGithubPublication { 38 | from(components["java"]) 39 | artifactId = "${rootProject.name}-${project.name}" 40 | suppressAllPomMetadataWarnings() 41 | } 42 | } 43 | repositories.github { domain = "kotlin-graphics/mary" } 44 | } 45 | 46 | java.withSourcesJar() -------------------------------------------------------------------------------- /awt/awt.gradle.kts: -------------------------------------------------------------------------------- 1 | import magik.createGithubPublication 2 | import magik.github 3 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask 4 | import org.lwjgl.Lwjgl.Module.* 5 | import org.lwjgl.lwjgl 6 | 7 | plugins { 8 | kotlin("jvm") 9 | id("org.lwjgl.plugin") 10 | id("elect86.magik") 11 | `maven-publish` 12 | } 13 | 14 | group = rootProject.group 15 | version = rootProject.version 16 | 17 | dependencies { 18 | implementation(projects.core) 19 | implementation(projects.gl) 20 | 21 | api("kotlin.graphics:gln:0.5.32") 22 | 23 | lwjgl { implementation(jawt, glfw, jemalloc, opengl) } 24 | 25 | testImplementation("io.kotest:kotest-runner-junit5:5.5.5") 26 | testImplementation("io.kotest:kotest-assertions-core:5.5.5") 27 | } 28 | 29 | kotlin.jvmToolchain { languageVersion.set(JavaLanguageVersion.of(8)) } 30 | 31 | tasks { 32 | withType>().configureEach { compilerOptions { freeCompilerArgs.add("-opt-in=kotlin.RequiresOptIn") } } 33 | test { useJUnitPlatform() } 34 | } 35 | 36 | publishing { 37 | publications { 38 | createGithubPublication { 39 | from(components["java"]) 40 | artifactId = "${rootProject.name}-${project.name}" 41 | suppressAllPomMetadataWarnings() 42 | } 43 | } 44 | repositories.github { domain = "kotlin-graphics/mary" } 45 | } 46 | 47 | java.withSourcesJar() -------------------------------------------------------------------------------- /core/src/main/kotlin/uno/kotlin/Quadruple.kt: -------------------------------------------------------------------------------- 1 | package uno.kotlin 2 | 3 | import java.io.Serializable 4 | 5 | /** 6 | * Created by GBarbieri on 06.03.2017. 7 | */ 8 | 9 | /** 10 | * Represents a group of four values 11 | * 12 | * There is no meaning attached to values in this class, it can be used for any purpose. 13 | * Quadruple exhibits value semantics, i.e. two quadruple are equal if all four components are equal. 14 | * An example of decomposing it into values: 15 | * 16 | * @param A type of the first value. 17 | * @param B type of the second value. 18 | * @param C type of the third value. 19 | * @param D type of the fourth value. 20 | * @property first First value. 21 | * @property second Second value. 22 | * @property third Third value. 23 | * @property fourth Fourth value. 24 | */ 25 | public data class Quadruple( 26 | public val first: A, 27 | public val second: B, 28 | public val third: C, 29 | public val fourth: D 30 | ) : Serializable { 31 | 32 | /** 33 | * Returns string representation of the [Quadruple] including its [first], [second], [third] and [fourth] values. 34 | */ 35 | public override fun toString(): String = "($first, $second, $third, $fourth)" 36 | } 37 | 38 | /** 39 | * Converts this quadruple into a list. 40 | */ 41 | public fun Quadruple.toList(): List = listOf(first, second, third, fourth) 42 | -------------------------------------------------------------------------------- /gl/src/test/kotlin/uno/helloWorld.kt: -------------------------------------------------------------------------------- 1 | package uno 2 | 3 | import io.kotest.core.spec.style.StringSpec 4 | import io.kotest.matchers.shouldBe 5 | import io.kotest.matchers.shouldNotBe 6 | import org.lwjgl.opengl.GL11.* 7 | import uno.gl.GlWindow 8 | import uno.glfw.GlfwWindow 9 | import uno.glfw.glfw 10 | import uno.kotlin.isNotCI 11 | 12 | class HelloWorld : StringSpec() { 13 | 14 | init { 15 | 16 | if (isNotCI) { 17 | 18 | "Hello World" { 19 | 20 | // init 21 | glfw.init("3.3") 22 | 23 | // The window handle 24 | val glfwWindow = GlfwWindow(1280, 720, "ImGui Lwjgl OpenGL3 example") 25 | GlWindow(glfwWindow).apply { 26 | 27 | pos = (glfw.primaryMonitor.videoMode.size - size) / 2 28 | 29 | init() 30 | 31 | // Set the clear color 32 | glClearColor(1f, 0f, 0f, 0f) 33 | 34 | val condition = { glfw.time < 0.1 } 35 | loop(condition) { 36 | glClear(GL_COLOR_BUFFER_BIT) 37 | } 38 | } 39 | } 40 | 41 | "equality" { 42 | val a = GlfwWindow(1L) 43 | val b = GlfwWindow(1L) 44 | a shouldBe b 45 | val c = GlfwWindow(2L) 46 | a shouldNotBe c 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/uno/kotlin/Quintuple.kt: -------------------------------------------------------------------------------- 1 | package uno.kotlin 2 | 3 | import java.io.Serializable 4 | 5 | /** 6 | * Created by GBarbieri on 06.03.2017. 7 | */ 8 | 9 | /** 10 | * Represents a group of five values 11 | * 12 | * There is no meaning attached to values in this class, it can be used for any purpose. 13 | * Quintuple exhibits value semantics, i.e. two quintuple are equal if all four components are equal. 14 | * An example of decomposing it into values: 15 | * 16 | * @param A type of the first value. 17 | * @param B type of the second value. 18 | * @param C type of the third value. 19 | * @param D type of the fourth value. 20 | * @param E type of the fifth value. 21 | * @property first First value. 22 | * @property second Second value. 23 | * @property third Third value. 24 | * @property fourth Fourth value. 25 | * @property fifth Fifth value. 26 | */ 27 | public data class Quintuple( 28 | public val first: A, 29 | public val second: B, 30 | public val third: C, 31 | public val fourth: D, 32 | public val fifth: E 33 | ) : Serializable { 34 | 35 | /** 36 | * Returns string representation of the [Quintuple] including its [first], [second], [third], [fourth] and [fifth] values. 37 | */ 38 | public override fun toString(): String = "($first, $second, $third, $fourth, $fifth)" 39 | } 40 | 41 | /** 42 | * Converts this quintuple into a list. 43 | */ 44 | public fun Quintuple.toList(): List = listOf(first, second, third, fourth, fifth) 45 | -------------------------------------------------------------------------------- /awt/src/test/kotlin/uno/jawt/JAWTDemo.java: -------------------------------------------------------------------------------- 1 | package uno.jawt; 2 | 3 | import org.lwjgl.glfw.*; 4 | import org.lwjgl.system.*; 5 | 6 | import javax.swing.*; 7 | import java.awt.*; 8 | import java.awt.event.*; 9 | import java.util.*; 10 | 11 | import static org.lwjgl.glfw.GLFW.*; 12 | 13 | /** GLFW canvas embedded in AWT using jawt. */ 14 | public final class JAWTDemo { 15 | 16 | private JAWTDemo() { 17 | } 18 | 19 | public static void main(String[] args) { 20 | if (Platform.get() == Platform.MACOSX) { 21 | throw new UnsupportedOperationException("This demo cannot run on macOS."); 22 | } 23 | 24 | GLFWErrorCallback.createPrint().set(); 25 | if (!glfwInit()) { 26 | throw new IllegalStateException("Unable to initialize glfw"); 27 | } 28 | 29 | LWJGLCanvas canvas = new LWJGLCanvas(); 30 | canvas.setSize(640, 480); 31 | 32 | JFrame frame = new JFrame("JAWT Demo"); 33 | 34 | frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); 35 | frame.addWindowListener(new WindowAdapter() { 36 | @Override 37 | public void windowClosed(WindowEvent e) { 38 | canvas.destroy(); 39 | } 40 | }); 41 | 42 | KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(e -> { 43 | if (e.getKeyCode() == KeyEvent.VK_ESCAPE && e.getID() == KeyEvent.KEY_PRESSED) { 44 | frame.dispose(); 45 | 46 | glfwTerminate(); 47 | Objects.requireNonNull(glfwSetErrorCallback(null)).free(); 48 | 49 | return true; 50 | } 51 | 52 | return false; 53 | }); 54 | 55 | frame.setLayout(new BorderLayout()); 56 | frame.add(canvas, BorderLayout.CENTER); 57 | frame.add(new JTextField(), BorderLayout.SOUTH); 58 | 59 | frame.pack(); 60 | frame.setVisible(true); 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/uno/kotlin/result.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UNCHECKED_CAST") 2 | 3 | package uno.kotlin 4 | 5 | /** https://gist.github.com/elizarov/03425be1c4209d59ad813ddccab29313 */ 6 | class Result private constructor(private val result: Any?) { 7 | // discovery 8 | 9 | val isFailure: Boolean 10 | get() = result is Failure 11 | val isSuccess: Boolean 12 | get() = result !is Failure 13 | 14 | // value retrieval 15 | 16 | fun get(): T = when (result) { 17 | is Failure -> throw result.exception 18 | else -> result as T 19 | } 20 | 21 | fun getOrNull(): T? = when (result) { 22 | is Failure -> null 23 | else -> result as T 24 | } 25 | 26 | inline fun getOrElse(default: () -> T): T = when { 27 | isFailure -> default() 28 | else -> value 29 | } 30 | 31 | // exception retrieval 32 | 33 | fun exceptionOrNull(): Throwable? = (result as? Failure)?.exception 34 | 35 | // companion with constructors 36 | 37 | companion object { 38 | fun success(value: T): Result = Result(value) 39 | fun failure(exception: Throwable) = Result(Failure(exception)) 40 | } 41 | 42 | // internal API for inline functions 43 | 44 | @PublishedApi 45 | internal val exception: Throwable 46 | get() = (result as Failure).exception 47 | @PublishedApi 48 | internal val value: T 49 | get() = result as T 50 | 51 | private class Failure(@JvmField val exception: Throwable) 52 | } 53 | 54 | inline fun resultOf(block: () -> T): Result = 55 | try { 56 | Result.success(block()) 57 | } catch (e: Throwable) { 58 | Result.failure(e) 59 | } 60 | 61 | // -- extensions --- 62 | 63 | // transformation 64 | 65 | inline fun Result.map(block: (T) -> U): Result = when { 66 | isFailure -> this as Result 67 | else -> resultOf { block(value) } 68 | } 69 | 70 | inline fun Result.handle(block: (Throwable) -> U): Result = when { 71 | isFailure -> resultOf { block(exception) } 72 | else -> this as Result 73 | } 74 | 75 | // "peek" onto value/exception and pipe 76 | 77 | inline fun Result.onFailure(block: (Throwable) -> Unit): Result { 78 | if (isFailure) block(exception) 79 | return this 80 | } 81 | 82 | inline fun Result.onSuccess(block: (T) -> Unit): Result { 83 | if (isSuccess) block(value) 84 | return this 85 | } 86 | -------------------------------------------------------------------------------- /gl/src/test/kotlin/tests/Clipboard.kt: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import gln.glClearColor 4 | import io.kotest.core.spec.style.StringSpec 5 | import io.kotest.matchers.shouldBe 6 | import org.lwjgl.glfw.GLFW 7 | import org.lwjgl.opengl.GL 8 | import org.lwjgl.opengl.GL11 9 | import org.lwjgl.system.Platform 10 | import uno.gl.GlWindow 11 | import uno.glfw.* 12 | 13 | class Clipboard: StringSpec() { 14 | 15 | val MODIFIER = when (Platform.get()) { 16 | Platform.MACOSX -> GLFW.GLFW_MOD_SUPER 17 | else -> GLFW.GLFW_MOD_CONTROL 18 | } 19 | 20 | fun errorCB(error: glfw.Error, description: String) = System.err.println("Error: $description") 21 | 22 | fun key_callback(window: GlfwWindow, key: Key, scancode: Int, action: InputAction, mods: Int) { 23 | 24 | if (action != InputAction.Press) 25 | return 26 | 27 | when (key) { 28 | Key.ESCAPE -> window.shouldClose = true 29 | Key.V -> 30 | if (mods == MODIFIER) { 31 | val string = glfw.clipboardString 32 | if (string != null) 33 | println("Clipboard contains \"$string\"") 34 | else 35 | println("Clipboard does not contain a string") 36 | } 37 | Key.C -> 38 | if (mods == MODIFIER) { 39 | val string = "Hello GLFW World!" 40 | glfw.clipboardString = string 41 | println("Setting clipboard to \"$string\"") 42 | } 43 | else -> {} 44 | } 45 | } 46 | 47 | init { 48 | "clipboard" { 49 | 50 | glfw.errorCB = ::errorCB 51 | 52 | glfw.init() 53 | 54 | val glfwWindow = GlfwWindow(200, "Clipboard Test") 55 | val window = GlWindow(glfwWindow) 56 | 57 | window.makeCurrent() 58 | // gladLoadGL(glfwGetProcAddress) 59 | GL.createCapabilities() 60 | glfw.swapInterval = VSync.ON 61 | 62 | window.keyCB = ::key_callback 63 | 64 | GL11.glClearColor(0.5f, 0.5f, 0.5f, 0f) 65 | 66 | var i = 0 67 | val string = "Hello GLFW World!" 68 | 69 | while (!window.shouldClose) { 70 | 71 | glClearColor() 72 | 73 | when(i++) { 74 | 0 -> glfw.clipboardString = string 75 | 1 -> glfw.clipboardString shouldBe string 76 | 2 -> window.shouldClose = true 77 | } 78 | 79 | window.swapBuffers() 80 | glfw.waitEvents() 81 | } 82 | 83 | glfw.terminate() 84 | } 85 | } 86 | } 87 | 88 | 89 | -------------------------------------------------------------------------------- /gl/src/main/kotlin/uno/gl/GlWindow.kt: -------------------------------------------------------------------------------- 1 | package uno.gl 2 | 3 | import gln.cap.Caps 4 | import gln.misc.GlDebugSeverity 5 | import gln.misc.GlDebugSource 6 | import gln.misc.GlDebugType 7 | import gln.misc.glDebugCallback 8 | import org.lwjgl.glfw.GLFW 9 | import org.lwjgl.opengl.GL 10 | import org.lwjgl.opengl.GL43C 11 | import org.lwjgl.opengl.GLUtil 12 | import org.lwjgl.system.Callback 13 | import org.lwjgl.system.MemoryUtil 14 | import uno.glfw.GlfwWindow 15 | import uno.glfw.glfw 16 | 17 | open class GlWindow(glfwWindow: GlfwWindow, 18 | profile: Caps.Profile = Caps.Profile.COMPATIBILITY, 19 | forwardCompatible: Boolean = true): GlfwWindow(glfwWindow.handle) { 20 | 21 | /** 22 | * Spasi: "the ideal option for modern applications is: compatibility context + forwardCompatible. A compatibility context 23 | * does not do extra validations that may cost performance and with `forwardCompatible == true` you don't risk using 24 | * legacy functionality by mistake. 25 | * LWJGL will not try to load deprecated functions, so calling them will crash but the context will actually expose them" 26 | */ 27 | init { 28 | makeCurrent() 29 | // This line is critical for LWJGL's interoperation with GLFW's OpenGL context, 30 | // or any context that is managed externally. 31 | // LWJGL detects the context that is current in the current thread, creates the GLCapabilities instance and 32 | // makes the OpenGL bindings available for use. 33 | // GL.createCapabilities() // useless, it's in Caps instantiation 34 | } 35 | val caps = Caps(profile, forwardCompatible) 36 | 37 | var debugProc: Callback? = null 38 | 39 | fun init(show: Boolean = true) { 40 | show(show) 41 | if (glfw.hints.context.debug) { 42 | debugProc = GLUtil.setupDebugMessageCallback() 43 | // turn off notifications only 44 | GL43C.nglDebugMessageControl(GlDebugSource.DONT_CARE.i, GlDebugType.DONT_CARE.i, GlDebugSeverity.NOTIFICATION.i, 0, MemoryUtil.NULL, false) 45 | } 46 | } 47 | 48 | // --- [ glfwMakeContextCurrent ] --- 49 | fun makeCurrent(current: Boolean = true) = GLFW.glfwMakeContextCurrent(if (current) handle else MemoryUtil.NULL) 50 | 51 | /** for Java */ 52 | override fun destroy() { 53 | super.destroy() 54 | debugProc?.free() 55 | glDebugCallback?.free() 56 | GL.destroy() 57 | } 58 | 59 | inline fun withinContext(block: () -> Unit) { 60 | makeCurrent() 61 | GL.setCapabilities(caps.gl) 62 | block() 63 | makeCurrent(false) 64 | } 65 | 66 | /** for Java */ 67 | fun withinContext(runnable: Runnable) = withinContext { runnable.run() } 68 | } -------------------------------------------------------------------------------- /core/src/test/kotlin/uno/testGli.kt: -------------------------------------------------------------------------------- 1 | //package uno 2 | // 3 | //import gli_.gl 4 | //import gli_.gli 5 | //import glm_.buffer.intBufferBig 6 | //import gln.texture.initTexture2d 7 | //import io.kotlintest.specs.StringSpec 8 | //import org.lwjgl.opengl.GL11.* 9 | //import org.lwjgl.opengl.GL12.GL_TEXTURE_BASE_LEVEL 10 | //import org.lwjgl.opengl.GL12.GL_TEXTURE_MAX_LEVEL 11 | //import org.lwjgl.opengl.GL13.glCompressedTexSubImage2D 12 | //import org.lwjgl.opengl.GL33.GL_TEXTURE_SWIZZLE_RGBA 13 | //import org.lwjgl.opengl.GL42.glTexStorage2D 14 | //import uno.buffer.intBufferBig 15 | // 16 | //class testGli : StringSpec() { 17 | // 18 | // init { 19 | // 20 | // fun createTexture(filename: String): Int { 21 | // 22 | // val texture = gli.load(filename) 23 | // if (texture.empty()) 24 | // return 0 25 | // 26 | // gli.gl.profile = gl.Profile.GL33 27 | // val format = gli.gl.translate(texture.format, texture.swizzles) 28 | // val target = gli.gl.translate(texture.target) 29 | // assert(texture.format.isCompressed && target == gl.Target._2D) 30 | // 31 | // val textureName = intBufferBig(1) 32 | // glGenTextures(textureName) 33 | // glBindTexture(target.i, textureName[0]) 34 | // glTexParameteri(target.i, GL_TEXTURE_BASE_LEVEL, 0) 35 | // glTexParameteri(target.i, GL_TEXTURE_MAX_LEVEL, texture.levels() - 1) 36 | // val swizzles = intBufferBig(4) 37 | // format.swizzles to swizzles 38 | // glTexParameteriv(target.i, GL_TEXTURE_SWIZZLE_RGBA, swizzles) 39 | // var extent = texture.extent() 40 | // glTexStorage2D(target.i, texture.levels(), format.internal.i, extent.x, extent.y) 41 | // for (level in 0 until texture.levels()) { 42 | // extent = texture.extent(level) 43 | // glCompressedTexSubImage2D( 44 | // target.i, level, 0, 0, extent.x, extent.y, 45 | // format.internal.i, texture.data(0, 0, level)) 46 | // } 47 | // return textureName[0] 48 | // } 49 | // } 50 | // 51 | // fun createTexture(filename: String): Int { 52 | // 53 | // val texture = gli.load(filename) 54 | // if (texture.empty()) 55 | // return 0 56 | // 57 | // gli.gl.profile = gl.Profile.GL33 58 | // val (target, format) = gli.gl.translate(texture) 59 | // assert(texture.format.isCompressed && target == gl.Target._2D) 60 | // 61 | // return initTexture2d { 62 | // levels = 0 until texture.levels() 63 | // swizzles = format.swizzles 64 | // storage(texture.levels(), format.internal, texture.extent()) 65 | // levels.forEach { 66 | // compressedSubImage(it, texture.extent(it), format.internal, texture.data(0, 0, it)) 67 | // } 68 | // } 69 | // } 70 | //} -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /awt/src/main/kotlin/uno/awt/helpers.kt: -------------------------------------------------------------------------------- 1 | package uno.awt 2 | 3 | import gln.L 4 | import kool.adr 5 | import org.lwjgl.system.MemoryUtil.NULL 6 | import org.lwjgl.system.MemoryUtil.memGetAddress 7 | import org.lwjgl.system.jawt.JAWT 8 | import org.lwjgl.system.jawt.JAWTDrawingSurface 9 | import org.lwjgl.system.jawt.JAWTDrawingSurfaceInfo 10 | import org.lwjgl.system.jawt.JAWTFunctions.* 11 | import org.lwjgl.system.jawt.JAWTWin32DrawingSurfaceInfo 12 | import uno.kotlin.HWND 13 | import java.awt.Canvas 14 | 15 | 16 | @JvmInline 17 | value class JawtVersion(val int: Int) { 18 | companion object { 19 | val _1_3 = JawtVersion(0x10003) 20 | val _1_4 = JawtVersion(0x10004) 21 | val _1_7 = JawtVersion(0x10007) 22 | val _9 = JawtVersion(0x90000) 23 | } 24 | } 25 | 26 | fun JAWT(version: JawtVersion = JawtVersion._1_4) = JAWT.calloc().version(version.int) 27 | 28 | fun JAWT.get(): Boolean = nJAWT_GetAWT(adr.L) 29 | 30 | fun JAWT.getDrawingSurface(canvas: Canvas): JAWTDrawingSurface? { 31 | val adr = memGetAddress(adr.L + JAWT.GETDRAWINGSURFACE) 32 | val result = nJAWT_GetDrawingSurface(canvas, adr) 33 | return JAWTDrawingSurface.createSafe(result) 34 | } 35 | 36 | 37 | 38 | @JvmInline 39 | value class JawtLock(val int: Int) { 40 | companion object { 41 | val ERROR = JawtLock(0x1) 42 | val CLIP_CHANGED = JawtLock(0x2) 43 | val BOUNDS_CHANGED = JawtLock(0x4) 44 | val SURFACE_CHANGED = JawtLock(0x8) 45 | } 46 | } 47 | 48 | fun JAWTDrawingSurface.lock(): JawtLock { 49 | val funAdr = memGetAddress(adr.L + JAWTDrawingSurface.LOCK) 50 | return JawtLock(nJAWT_DrawingSurface_Lock(adr.L, funAdr)) 51 | } 52 | fun JAWTDrawingSurface.unlock() { 53 | val funAdr = memGetAddress(adr.L + JAWTDrawingSurface.UNLOCK) 54 | nJAWT_DrawingSurface_Unlock(adr.L, funAdr) 55 | } 56 | 57 | val JAWTDrawingSurface.info: JAWTDrawingSurfaceInfo? 58 | get() { 59 | val funAdr = memGetAddress(adr.L + JAWTDrawingSurface.GETDRAWINGSURFACEINFO) 60 | val result = nJAWT_DrawingSurface_GetDrawingSurfaceInfo(adr.L, funAdr) 61 | return JAWTDrawingSurfaceInfo.createSafe(result) 62 | } 63 | 64 | fun JAWTWin32DrawingSurfaceInfo(info: JAWTDrawingSurfaceInfo): JAWTWin32DrawingSurfaceInfo { 65 | val adr = memGetAddress(info.adr.L + JAWTDrawingSurfaceInfo.PLATFORMINFO) 66 | return org.lwjgl.system.jawt.JAWTWin32DrawingSurfaceInfo.create(adr) 67 | } 68 | 69 | @JvmInline 70 | value class HDC(val L: Long) { 71 | val isValid get() = L != NULL 72 | val isInvalid get() = L == NULL 73 | } 74 | 75 | val JAWTWin32DrawingSurfaceInfo.hdc: HDC 76 | get() = HDC(memGetAddress(adr.L + JAWTWin32DrawingSurfaceInfo.HDC)) 77 | val JAWTWin32DrawingSurfaceInfo.hwnd: HWND 78 | get() = HWND(memGetAddress(adr.L + JAWTWin32DrawingSurfaceInfo.HWND)) 79 | 80 | infix fun JAWTDrawingSurface.free(dsi: JAWTDrawingSurfaceInfo) { 81 | val adr = memGetAddress(adr.L + JAWTDrawingSurface.FREEDRAWINGSURFACEINFO) 82 | nJAWT_DrawingSurface_FreeDrawingSurfaceInfo(dsi.adr.L, adr) 83 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # unofficial-opengl-SDK 2 | 3 | [![Build Status](https://github.com/kotlin-graphics/uno-sdk/workflows/build/badge.svg)](https://github.com/kotlin-graphics/uno-sdk/actions?workflow=build) 4 | [![license](https://img.shields.io/badge/License-MIT-orange.svg)](https://github.com/kotlin-graphics/uno-sdk/blob/master/LICENSE) 5 | [![Release](https://jitpack.io/v/kotlin-graphics/uno-sdk.svg)](https://jitpack.io/#kotlin-graphics/uno-sdk) 6 | ![Size](https://github-size-badge.herokuapp.com/kotlin-graphics/uno-sdk.svg) 7 | [![Github All Releases](https://img.shields.io/github/downloads/kotlin-graphics/uno-sdk/total.svg)]() 8 | [![Awesome Kotlin Badge](https://kotlin.link/awesome-kotlin.svg)](https://github.com/KotlinBy/awesome-kotlin) 9 | 10 | ### How to retrieve it: 11 | 12 | You can find all the instructions by [mary](https://github.com/kotlin-graphics/mary) 13 | 14 | ### uno 15 | 16 | This is kind of a small suite, born and shaped around GL, it includes the gln dependencies, such as [unsigned support](https://github.com/elect86/kotlin-unsigned), [glm](https://github.com/kotlin-graphics/glm) and [gli](https://github.com/kotlin-graphics/gli). Its main usage is basically as wrapper for the lwjgl glfw binding. 17 | 18 | A kind of a [gln](https://github.com/kotlin-graphics/glm) for glfw. So, code more compact, type-safe, clear and intuitive. You can have up and running a whole gl clear example in just a couple of lines: 19 | 20 | glfw.init("3.3") 21 | 22 | val glfwWindow = GlfwWindow(1280, 720, "OpenGL example") 23 | val window = GlWindow(glfwWindow) 24 | 25 | glClearColor(1f, 0f, 0f, 0f) 26 | 27 | window.loop { 28 | glClear(GL_COLOR_BUFFER_BIT) 29 | } 30 | 31 | 32 | Plus some other small utils like: 33 | - methods for allocating very easily many different type of buffers from a lot of different data type, such as different arrays and so on 34 | - an cap class for a deep and complete resume about a machine opengl capabilities 35 | - matrixStack for [glm](https://github.com/kotlin-graphics/glm) 36 | - glsl utils (to refresh) 37 | - an experimental kotlin stlib on intBuffers. So that you can for example `textureName.forEach(::glDestroyTexture)` 38 | - mousePole, an util for camera management 39 | - and attempt to port stb completely on jvm (unfinished) 40 | - timer util 41 | 42 | And lately it also includes a counterpart of gln for vulkan, [vkk](https://github.com/kotlin-graphics/vkk), plus an util for making short-live allocations easy and free, [kool](https://github.com/kotlin-graphics/kool). 43 | 44 | Don't hesitate to contribute to the project by submitting [issues](https://github.com/kotlin-graphics/uno-sdk/issues) or [pull requests](https://github.com/kotlin-graphics/uno-sdk/pulls) for bugs and features. Any feedback is welcome at [elect86@gmail.com](mailto://elect86@gmail.com). 45 | 46 | ### How to retrieve it: 47 | 48 | ```kotlin 49 | repositories { 50 | maven("https://raw.githubusercontent.com/kotlin-graphics/mary/master") 51 | // or with magik plugin 52 | //github("kotlin-graphics/mary") 53 | } 54 | dependencies { 55 | implementation("kotlin.graphics:uno:0.7.21") 56 | } 57 | ``` 58 | 59 | ## Credits: 60 | 61 | - [Farid Zakaria](https://github.com/fzakaria) for [ascii85](https://github.com/fzakaria/ascii85) 62 | - [Jogamp](http://jogamp.org/) for BufferedImage flipping 63 | -------------------------------------------------------------------------------- /awt/src/test/kotlin/uno/AwtTest.kt: -------------------------------------------------------------------------------- 1 | //package uno 2 | // 3 | //import glm_.f 4 | //import org.lwjgl.opengl.GL 5 | //import org.lwjgl.opengl.GL11.* 6 | //import uno.awtOld.AWTGLCanvas 7 | //import uno.jawt.AbstractGears 8 | //import uno.awtOld.GLData 9 | //import java.awt.BorderLayout 10 | //import java.awt.Dimension 11 | //import java.awt.event.WindowAdapter 12 | //import java.awt.event.WindowEvent 13 | //import javax.swing.JFrame 14 | // 15 | // 16 | //fun main() { 17 | // 18 | // val frame = JFrame("AWT test").apply { 19 | // defaultCloseOperation = JFrame.DISPOSE_ON_CLOSE 20 | // layout = BorderLayout() 21 | // preferredSize = Dimension(600, 600) 22 | // addWindowListener(object : WindowAdapter() { 23 | // override fun windowClosing(e: WindowEvent?) { 24 | // e!!.window.dispose() 25 | // System.exit(0) 26 | // } 27 | // }) 28 | // } 29 | // val data = GLData().apply { 30 | // samples = 4 31 | // swapInterval = 0 32 | // } 33 | // 34 | // val canvas = object : AWTGLCanvas(data) { 35 | // 36 | // var last = 0L 37 | // var time = 0L 38 | // var frames = 0 39 | // 40 | // val gears = AbstractGears() 41 | // 42 | // override fun initGL() { 43 | // System.out.println("OpenGL version: " + effective.majorVersion + "." + effective.minorVersion + " (Profile: " + effective.profile + ")") 44 | // GL.createCapabilities() 45 | // glClearColor(0.3f, 0.4f, 0.5f, 1f) 46 | // gears.init() 47 | // last = System.currentTimeMillis() 48 | // } 49 | // 50 | // override fun paintGL() { 51 | // // System.out.println("paintGL"); 52 | // val aspect = width.f / height 53 | // glClear(GL_COLOR_BUFFER_BIT) 54 | // glViewport(0, 0, width, height) 55 | //// glBegin(GL_QUADS) 56 | //// glColor3f(0.4f, 0.6f, 0.8f) 57 | //// glVertex2f(-0.75f / aspect, 0.0f) 58 | //// glVertex2f(0f, -0.75f) 59 | //// glVertex2f(+0.75f / aspect, 0f) 60 | //// glVertex2f(0f, +0.75f) 61 | //// glEnd() 62 | // gears.render() 63 | // swapBuffers() 64 | // 65 | // val now = System.currentTimeMillis() 66 | // time += now - last 67 | // last = now 68 | // frames++ 69 | // if (time > 1000) { 70 | // time %= 1000 71 | // println("fps = $frames") 72 | // frames = 0 73 | // } 74 | // } 75 | // } 76 | // 77 | // frame.apply { 78 | // add(canvas, BorderLayout.CENTER) 79 | // pack() 80 | // isVisible = true 81 | // transferFocus() 82 | // } 83 | // 84 | //// val render = object : Runnable { 85 | //// override fun run() { 86 | //// if (!canvas.isValid) 87 | //// return 88 | //// canvas.render() 89 | //// SwingUtilities.invokeLater(this) 90 | //// } 91 | //// } 92 | //// SwingUtilities.invokeLater(render) 93 | // 94 | // var run = true 95 | // 96 | // Thread { 97 | // canvas.init() 98 | // while (run) { 99 | // if (canvas.isValid) 100 | // canvas.render() 101 | // } 102 | // canvas.end() 103 | // }.start() 104 | //} -------------------------------------------------------------------------------- /core/src/main/kotlin/uno/glfw/monitor.kt: -------------------------------------------------------------------------------- 1 | package uno.glfw 2 | 3 | import glm_.vec2.Vec2 4 | import glm_.vec2.Vec2i 5 | import glm_.vec4.Vec4i 6 | import kool.Ptr 7 | import kool.get 8 | import kool.stack 9 | import kool.toPtr 10 | import org.lwjgl.glfw.GLFW.* 11 | import org.lwjgl.system.MemoryUtil 12 | import uno.kotlin.ptrInt 13 | import uno.kotlin.readVec2 14 | import uno.kotlin.readVec2i 15 | import uno.kotlin.readVec4i 16 | 17 | @JvmInline 18 | value class GlfwMonitors(val handles: LongArray) : Iterable { 19 | 20 | val size: Int 21 | get() = handles.size 22 | 23 | fun isEmpty(): Boolean = handles.isEmpty() 24 | fun isNotEmpty(): Boolean = handles.isNotEmpty() 25 | 26 | operator fun get(index: Int): GlfwMonitor = GlfwMonitor(handles[index]) 27 | operator fun set(index: Int, monitor: GlfwMonitor) = handles.set(index, monitor.handle) 28 | override fun iterator(): Iterator = GlfwMonitorsIterator(handles) 29 | 30 | class GlfwMonitorsIterator(private val handles: LongArray) : Iterator { 31 | 32 | private var index = 0 33 | 34 | override fun hasNext(): Boolean = index in handles.indices 35 | 36 | override fun next(): GlfwMonitor = GlfwMonitor(handles[index++]) 37 | } 38 | } 39 | 40 | @JvmInline 41 | value class GlfwMonitor(val handle: Long) { 42 | 43 | val isValid: Boolean 44 | get() = handle != MemoryUtil.NULL 45 | val isNotValid: Boolean 46 | get() = !isValid 47 | 48 | // --- [ glfwGetMonitorPos ] --- 49 | val pos: Vec2i 50 | get() = readVec2i { x, y -> nglfwGetMonitorPos(handle, x, y) } 51 | 52 | // --- [ glfwGetMonitorWorkarea ] --- 53 | val workArea: Vec4i 54 | get() = readVec4i { x, y, z, w -> nglfwGetMonitorWorkarea(handle, x, y, z, w) } 55 | 56 | // --- [ glfwGetMonitorPhysicalSize ] --- 57 | val physicalSize: Vec2i 58 | get() = readVec2i { x, y -> nglfwGetMonitorPhysicalSize(handle, x, y) } 59 | 60 | // --- [ glfwGetMonitorContentScale ] --- 61 | val contentScale: Vec2 62 | get() = readVec2 { x, y -> nglfwGetMonitorPhysicalSize(handle, x, y) } 63 | 64 | // --- [ glfwGetMonitorName ] --- 65 | val name: String? 66 | get() = glfwGetMonitorName(handle) 67 | 68 | // --- [ glfwSetMonitorUserPointer ] --- 69 | var userPointer: Ptr<*> 70 | get() = glfwGetMonitorUserPointer(handle).toPtr() 71 | set(value) = glfwSetMonitorUserPointer(handle, value.adr.toLong()) 72 | 73 | 74 | // --- [ glfwGetVideoModes ] --- 75 | val videoModes: Array 76 | get() = stack { s -> 77 | val pCount = s.ptrInt() 78 | val pModes = nglfwGetVideoModes(handle, pCount.adr.toLong()).toPtr() 79 | val count = pCount[0] 80 | Array(count) { pModes[it] } 81 | } 82 | 83 | // --- [ glfwGetVideoMode ] --- 84 | val videoMode: GlfwVidMode 85 | get() = GlfwVidMode(nglfwGetVideoMode(handle).toPtr()) 86 | 87 | // --- [ glfwSetGamma ] --- 88 | var gamma: Float 89 | @Deprecated(message = "Write only property", level = DeprecationLevel.HIDDEN) get() = error("") 90 | set(value) = glfwSetGamma(handle, value) 91 | 92 | // --- [ glfwGetGammaRamp ] --- 93 | var gammaRamp: GlfwGammaRamp 94 | get() = GlfwGammaRamp(nglfwGetGammaRamp(handle).toPtr()) 95 | set(value) = stack { nglfwSetGammaRamp(handle, value.toStack(it).adr.toLong()) } 96 | 97 | companion object { 98 | val NULL = GlfwMonitor(MemoryUtil.NULL) 99 | } 100 | } 101 | 102 | typealias GlfwMonitorFun = (monitor: GlfwMonitor, connected: Boolean) -> Unit -------------------------------------------------------------------------------- /awt/src/test/kotlin/uno/jawt/JawtDemo.kt: -------------------------------------------------------------------------------- 1 | package uno.jawt 2 | 3 | import glm_.vec2.Vec2i 4 | import org.lwjgl.opengl.GL11C 5 | import org.lwjgl.system.Platform 6 | import uno.awt.LwjglCanvas 7 | import java.awt.BorderLayout 8 | import java.awt.event.* 9 | import javax.swing.JFrame 10 | import javax.swing.WindowConstants 11 | import org.lwjgl.system.jawt.JAWT as Jawt 12 | 13 | 14 | /** AWT integration demo using jawt. */ 15 | fun main() { 16 | 17 | if (Platform.get() != Platform.WINDOWS) 18 | throw UnsupportedOperationException("This demo can only run on Windows.") 19 | 20 | val viewer = Viewer()//.apply { setSize(640, 480) } 21 | 22 | val frame = JFrame("JAWT Demo").apply { 23 | // set it, because default is HIDE_ON_CLOSE 24 | defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE 25 | addWindowListener(object : WindowAdapter() { 26 | /* 27 | windowClosed will be called when it's too late, the awtOld hwnd will be invalid at that point and 28 | so will be also the glfw Window handle 29 | */ 30 | override fun windowClosing(e: WindowEvent?) { 31 | println("windowClosing") 32 | viewer.destroyInternal() 33 | } 34 | }) 35 | } 36 | 37 | val keyListener = object : KeyListener { 38 | override fun keyTyped(e: KeyEvent) { 39 | println("keyTyped " + Thread.currentThread().name) 40 | } 41 | 42 | override fun keyPressed(e: KeyEvent) { 43 | println("keyPressed") 44 | if (e.keyCode == KeyEvent.VK_ESCAPE) 45 | frame.dispose() 46 | else if (e.keyCode == KeyEvent.VK_A) 47 | viewer.toggleAnimation() 48 | } 49 | 50 | override fun keyReleased(e: KeyEvent) { 51 | println("keyReleased") 52 | } 53 | } 54 | 55 | val mouseListener = object : MouseListener, MouseMotionListener, MouseWheelListener { 56 | override fun mouseClicked(e: MouseEvent) { 57 | println("clicked") 58 | } 59 | 60 | override fun mouseEntered(e: MouseEvent?) { 61 | println("entered") 62 | } 63 | 64 | override fun mouseExited(e: MouseEvent?) { 65 | println("exited") 66 | } 67 | 68 | override fun mousePressed(e: MouseEvent?) { 69 | println("pressed") 70 | } 71 | 72 | override fun mouseReleased(e: MouseEvent?) { 73 | println("released") 74 | } 75 | 76 | override fun mouseDragged(e: MouseEvent?) { 77 | println("dragged") 78 | } 79 | 80 | override fun mouseMoved(e: MouseEvent) { 81 | println("moved (" + e.x + ", " + e.y + ") " + Thread.currentThread().name) 82 | } 83 | 84 | override fun mouseWheelMoved(e: MouseWheelEvent?) { 85 | println("wheel") 86 | } 87 | } 88 | 89 | frame.apply { 90 | layout = BorderLayout() 91 | add(viewer, BorderLayout.CENTER) 92 | 93 | pack() 94 | setSize(640, 480) 95 | viewer.addKeyListener(keyListener) 96 | // canvas.addMouseListener(mouseListener) 97 | // canvas.addMouseMotionListener(mouseListener) 98 | // canvas.addMouseWheelListener(mouseListener) 99 | isVisible = true 100 | } 101 | } 102 | 103 | 104 | class Viewer : LwjglCanvas(true) { 105 | 106 | val gears = AbstractGears() 107 | 108 | override fun init() = gears.init() 109 | override fun render() = gears.render() 110 | override fun reshape(size: Vec2i) = gears.reshape(size) 111 | override fun destroy() = gears.destroy() 112 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/uno/time/timer.kt: -------------------------------------------------------------------------------- 1 | package uno.time 2 | 3 | import glm_.glm 4 | 5 | /** 6 | * Created by GBarbieri on 13.03.2017. 7 | */ 8 | 9 | /** Creates a timer with the given type. 10 | * LOOP and SINGLE timers need an explicit duration. This represents the time in seconds through a loop, or the time in 11 | * seconds until the timer expires. 12 | * INFINITE timers ignore the duration. 13 | * It is legal to create these statically. **/ 14 | class Timer(var type: Type, val secDuration: Float) { 15 | 16 | constructor(secDuration: Float) : this(Type.Infinite, 1f) 17 | 18 | private var hasUpdated = false 19 | var isPaused = false 20 | 21 | private var absPrevTime = 0f 22 | private var secAccumTime = 0f 23 | private val start = System.currentTimeMillis() 24 | 25 | init { 26 | if (type != Type.Infinite) 27 | assert(secDuration > 0f) 28 | } 29 | 30 | /** Resets the timer, as though the user just created the object with the original parameters. */ 31 | fun reset() { 32 | hasUpdated = false 33 | secAccumTime = 0f 34 | } 35 | 36 | /** Pauses/unpauses. Returns true if the timer is paused after the toggling. */ 37 | fun togglePause(): Boolean { 38 | isPaused = !isPaused 39 | return isPaused 40 | } 41 | 42 | /** Updates the time for the timer. Returns true if the timer has reached the end. 43 | * Will only return true for SINGLE timers that have reached their duration. */ 44 | fun update(): Boolean { 45 | 46 | val absCurrTime = (System.currentTimeMillis() - start) / 1_000f 47 | if (!hasUpdated) { 48 | absPrevTime = absCurrTime 49 | hasUpdated = true 50 | } 51 | 52 | if (isPaused) { 53 | absPrevTime = absCurrTime 54 | return false 55 | } 56 | 57 | val deltaTime = absCurrTime - absPrevTime 58 | secAccumTime += deltaTime 59 | 60 | absPrevTime = absCurrTime 61 | if (type == Type.Single) 62 | return secAccumTime > secDuration 63 | 64 | return false 65 | } 66 | 67 | /** Subtracts secRewind from the current time and continues from there. */ 68 | fun rewind(secRewind: Float) { 69 | secAccumTime -= secRewind 70 | if (secAccumTime < 0f) 71 | secAccumTime = 0f 72 | } 73 | 74 | /** Adds secRewind to the current time and continues from there. */ 75 | fun fastForward(secFF: Float) { 76 | secAccumTime += secFF 77 | } 78 | 79 | /** Returns a number [0, 1], representing progress through the duration. Only used for SINGLE and LOOP timers. */ 80 | val alpha: Float 81 | get() = when (type) { 82 | 83 | Type.Loop -> glm.mod(secAccumTime, secDuration) / secDuration 84 | 85 | Type.Single -> glm.clamp(secAccumTime / secDuration, 0f, 1f) 86 | 87 | else -> -1f //Garbage. 88 | } 89 | 90 | /** Returns a number [0, duration], representing the progress through the timer in seconds. Only for SINGLE and LOOP timers. */ 91 | val progression: Float 92 | get() = when (type) { 93 | 94 | Type.Loop -> glm.mod(secAccumTime, secDuration) 95 | 96 | Type.Single -> glm.clamp(secAccumTime, 0f, secDuration) 97 | 98 | else -> -1f //Garbage. 99 | } 100 | 101 | /** Returns the time in seconds since the timer was started, excluding time for pausing. */ 102 | val timeSinceStart: Float 103 | get() = secAccumTime 104 | 105 | /** Returns the timer's duration that was passed in. */ 106 | val duration: Float 107 | get() = secDuration 108 | 109 | enum class Type { Loop, Single, Infinite, MAX } 110 | } -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [ push ] 4 | 5 | jobs: 6 | 7 | linux: 8 | name: 'Linux' 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v3 13 | - uses: gradle/wrapper-validation-action@v1 14 | - name: Set up JDK 1.8 15 | uses: actions/setup-java@v3 16 | with: 17 | distribution: temurin 18 | java-version: 8 19 | - name: Grant execute permission for gradlew 20 | run: chmod +x gradlew 21 | # - name: Build with Gradle 22 | # run: ./gradlew build -x dokkaHtml -x dokkaHtmlJar 23 | - uses: burrunan/gradle-cache-action@v1 24 | name: Build 25 | with: 26 | arguments: assemble #-x dokkaHtml -x dokkaHtmlJar -x javadoc -x dokkaJavadocJar 27 | # - name: Cleanup Gradle Cache 28 | # # Remove some files from the Gradle cache, so they aren't cached by GitHub Actions. 29 | # # Restoring these files from a GitHub Actions cache might cause problems for future builds. 30 | # run: | 31 | # rm -f ~/.gradle/caches/modules-2/modules-2.lock 32 | # rm -f ~/.gradle/caches/modules-2/gc.properties 33 | # - name: Show Working directory content 34 | # run: ls 35 | # - uses: actions/checkout@master 36 | # with: 37 | # repository: kotlin-graphics/mary 38 | # path: ./mary 39 | - name: Show ../.. directory content 40 | run: ls ../.. 41 | # - name: move mary up 42 | # run: mv ./mary ../.. 43 | # - name: Show Working directory content 44 | # run: ls 45 | # - name: Show ../.. directory content 46 | # run: ls ../.. 47 | 48 | windows: 49 | name: 'Windows' 50 | runs-on: windows-latest 51 | 52 | steps: 53 | - uses: actions/checkout@v3 54 | - uses: gradle/wrapper-validation-action@v1 55 | - name: Set up JDK 1.8 56 | uses: actions/setup-java@v3 57 | with: 58 | distribution: temurin 59 | java-version: 8 60 | # - name: Build with Gradle 61 | # run: .\gradlew.bat build -x dokkaHtml -x dokkaHtmlJar 62 | - uses: burrunan/gradle-cache-action@v1 63 | name: Build 64 | with: 65 | arguments: assemble #-x dokkaHtml -x dokkaHtmlJar -x javadoc -x dokkaJavadocJar 66 | # - name: Cleanup Gradle Cache 67 | # # Remove some files from the Gradle cache, so they aren't cached by GitHub Actions. 68 | # # Restoring these files from a GitHub Actions cache might cause problems for future builds. 69 | # run: | 70 | # rm -f ~/.gradle/caches/modules-2/modules-2.lock 71 | # rm -f ~/.gradle/caches/modules-2/gc.properties 72 | 73 | mac: 74 | name: 'Mac OS' 75 | runs-on: macos-latest 76 | 77 | steps: 78 | - uses: actions/checkout@v3 79 | - uses: gradle/wrapper-validation-action@v1 80 | - name: Set up JDK 1.8 81 | uses: actions/setup-java@v3 82 | with: 83 | distribution: temurin 84 | java-version: 8 85 | - name: Grant execute permission for gradlew 86 | run: chmod +x gradlew 87 | # - name: Build with Gradle 88 | # run: ./gradlew build -x dokkaHtml -x dokkaHtmlJar 89 | - uses: burrunan/gradle-cache-action@v1 90 | name: Build 91 | with: 92 | arguments: assemble #-x dokkaHtml -x dokkaHtmlJar -x javadoc -x dokkaJavadocJar 93 | # - name: Cleanup Gradle Cache 94 | # # Remove some files from the Gradle cache, so they aren't cached by GitHub Actions. 95 | # # Restoring these files from a GitHub Actions cache might cause problems for future builds. 96 | # run: | 97 | # rm -f ~/.gradle/caches/modules-2/modules-2.lock 98 | # rm -f ~/.gradle/caches/modules-2/gc.properties 99 | -------------------------------------------------------------------------------- /core/src/main/kotlin/uno/kotlin/util.kt: -------------------------------------------------------------------------------- 1 | package uno.kotlin 2 | 3 | import glm_.i 4 | import glm_.vec2.Vec2 5 | import glm_.vec2.Vec2d 6 | import glm_.vec2.Vec2i 7 | import glm_.vec3.Vec3i 8 | import glm_.vec4.Vec4i 9 | import kool.* 10 | import java.awt.event.KeyEvent 11 | import java.io.File 12 | import java.nio.IntBuffer 13 | import java.util.* 14 | import kotlin.reflect.KMutableProperty0 15 | import kotlin.reflect.KProperty 16 | 17 | /** 18 | * Created by GBarbieri on 30.03.2017. 19 | */ 20 | 21 | infix fun (() -> Any).shallThrow(exClass: Class) { 22 | try { 23 | this() 24 | } catch (e: Throwable) { 25 | if (exClass.isInstance(e)) return 26 | else throw Error("Exception type different") 27 | } 28 | throw Error("No exception") 29 | } 30 | 31 | val String.uri get() = url.toURI()!! 32 | val String.url get() = ClassLoader.getSystemResource(this)!! 33 | val String.stream get() = ClassLoader.getSystemResourceAsStream(this)!! 34 | 35 | val String.file get() = File(uri) 36 | 37 | val Char.isPrintable: Boolean 38 | get() = with(Character.UnicodeBlock.of(this)) { 39 | (!Character.isISOControl(this@isPrintable)) && 40 | this@isPrintable != KeyEvent.CHAR_UNDEFINED && 41 | this != null && 42 | this != Character.UnicodeBlock.SPECIALS 43 | } 44 | 45 | fun Char.parseInt() = java.lang.Character.getNumericValue(this) 46 | 47 | operator fun HashSet.plusAssign(element: K) { 48 | add(element) 49 | } 50 | 51 | operator fun HashSet.minusAssign(element: K) { 52 | remove(element) 53 | } 54 | 55 | infix operator fun Appendable.plusAssign(char: Char) { 56 | append(char) 57 | } 58 | 59 | infix operator fun Appendable.plusAssign(string: String) { 60 | append(string) 61 | } 62 | 63 | infix operator fun StringBuilder.plusAssign(element: T) { 64 | append(element) 65 | } 66 | 67 | fun treeSetOf() = TreeSet() 68 | 69 | fun SortedMap.getOrfirst(key: K): V? = get(key) ?: first 70 | val SortedMap.first: V? get() = get(firstKey()) 71 | 72 | operator fun KMutableProperty0.setValue(host: Any?, property: KProperty<*>, value: R) = set(value) 73 | operator fun KMutableProperty0.getValue(host: Any?, property: KProperty<*>): R = get() 74 | operator fun KMutableProperty0.invoke(t: T): KMutableProperty0 { 75 | set(t) 76 | return this 77 | } 78 | 79 | operator fun IntBuffer.plusAssign(bool: Boolean) = plusAssign(bool.i) 80 | operator fun IntBuffer.plusAssign(i: Int) { 81 | put(i) 82 | } 83 | 84 | 85 | //fun MemoryStack.Ptr() = 0 86 | 87 | const val NUL = '\u0000' 88 | 89 | val isNotCI: Boolean 90 | get() = System.getenv("GITHUB_ACTIONS") != "true" && System.getenv("TRAVIS") != "true" 91 | 92 | inline fun readVec2(block: (Long, Long) -> Unit): Vec2 = stack { 93 | val ptr = it.PtrFloat(Vec2.length) 94 | block(ptr.adr.toLong(), (ptr + 1).adr.toLong()) 95 | Vec2(ptr) 96 | } 97 | inline fun readVec2d(block: (Long, Long) -> Unit): Vec2d = stack { 98 | val ptr = it.PtrDouble(Vec2d.length) 99 | block(ptr.adr.toLong(), (ptr + 1).adr.toLong()) 100 | Vec2d(ptr) 101 | } 102 | 103 | inline fun readVec2i(block: (Long, Long) -> Unit): Vec2i = stack { 104 | val ptr = it.PtrInt(Vec2i.length) 105 | block(ptr.adr.toLong(), (ptr + 1).adr.toLong()) 106 | Vec2i(ptr) 107 | } 108 | 109 | inline fun readVec3i(block: (Long, Long, Long) -> Unit): Vec3i = stack { 110 | val ptr = it.PtrInt(Vec3i.length) 111 | block(ptr.adr.toLong(), (ptr + 1).adr.toLong(), (ptr + 2).adr.toLong()) 112 | Vec3i(ptr) 113 | } 114 | 115 | inline fun readVec4i(block: (Long, Long, Long, Long) -> Unit): Vec4i = stack { 116 | val ptr = it.PtrInt(Vec4i.length) 117 | block(ptr.adr.toLong(), (ptr + 1).adr.toLong(), (ptr + 2).adr.toLong(), (ptr + 3).adr.toLong()) 118 | Vec4i(ptr) 119 | } 120 | 121 | @JvmInline 122 | value class HWND(val L: Long) 123 | 124 | typealias Seconds = Double 125 | typealias Hz = ULong -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/java,gradle,intellij,kotlin 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=java,gradle,intellij,kotlin 3 | 4 | ### Intellij ### 5 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 6 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 7 | 8 | # User-specific stuff 9 | .idea/**/workspace.xml 10 | .idea/**/tasks.xml 11 | .idea/**/usage.statistics.xml 12 | .idea/**/dictionaries 13 | .idea/**/shelf 14 | 15 | # Generated files 16 | .idea/**/contentModel.xml 17 | 18 | # Sensitive or high-churn files 19 | .idea/**/dataSources/ 20 | .idea/**/dataSources.ids 21 | .idea/**/dataSources.local.xml 22 | .idea/**/sqlDataSources.xml 23 | .idea/**/dynamic.xml 24 | .idea/**/uiDesigner.xml 25 | .idea/**/dbnavigator.xml 26 | 27 | # Gradle 28 | .idea/**/gradle.xml 29 | .idea/**/libraries 30 | 31 | # Gradle and Maven with auto-import 32 | # When using Gradle or Maven with auto-import, you should exclude module files, 33 | # since they will be recreated, and may cause churn. Uncomment if using 34 | # auto-import. 35 | .idea/artifacts 36 | .idea/compiler.xml 37 | .idea/jarRepositories.xml 38 | .idea/modules.xml 39 | .idea/*.iml 40 | .idea/modules 41 | *.iml 42 | *.ipr 43 | 44 | # CMake 45 | cmake-build-*/ 46 | 47 | # Mongo Explorer plugin 48 | .idea/**/mongoSettings.xml 49 | 50 | # File-based project format 51 | *.iws 52 | 53 | # IntelliJ 54 | out/ 55 | 56 | # mpeltonen/sbt-idea plugin 57 | .idea_modules/ 58 | 59 | # JIRA plugin 60 | atlassian-ide-plugin.xml 61 | 62 | # Cursive Clojure plugin 63 | .idea/replstate.xml 64 | 65 | # Crashlytics plugin (for Android Studio and IntelliJ) 66 | com_crashlytics_export_strings.xml 67 | crashlytics.properties 68 | crashlytics-build.properties 69 | fabric.properties 70 | 71 | # Editor-based Rest Client 72 | .idea/httpRequests 73 | 74 | # Android studio 3.1+ serialized cache file 75 | .idea/caches/build_file_checksums.ser 76 | 77 | ### Intellij Patch ### 78 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 79 | 80 | # *.iml 81 | # modules.xml 82 | # .idea/misc.xml 83 | # *.ipr 84 | 85 | # Sonarlint plugin 86 | # https://plugins.jetbrains.com/plugin/7973-sonarlint 87 | .idea/**/sonarlint/ 88 | 89 | # SonarQube Plugin 90 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin 91 | .idea/**/sonarIssues.xml 92 | 93 | # Markdown Navigator plugin 94 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced 95 | .idea/**/markdown-navigator.xml 96 | .idea/**/markdown-navigator-enh.xml 97 | .idea/**/markdown-navigator/ 98 | 99 | # Cache file creation bug 100 | # See https://youtrack.jetbrains.com/issue/JBR-2257 101 | .idea/$CACHE_FILE$ 102 | 103 | # CodeStream plugin 104 | # https://plugins.jetbrains.com/plugin/12206-codestream 105 | .idea/codestream.xml 106 | 107 | ### Java ### 108 | # Compiled class file 109 | *.class 110 | 111 | # Log file 112 | *.log 113 | 114 | # BlueJ files 115 | *.ctxt 116 | 117 | # Mobile Tools for Java (J2ME) 118 | .mtj.tmp/ 119 | 120 | # Package Files # 121 | *.jar 122 | *.war 123 | *.nar 124 | *.ear 125 | *.zip 126 | *.tar.gz 127 | *.rar 128 | 129 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 130 | hs_err_pid* 131 | 132 | ### Kotlin ### 133 | # Compiled class file 134 | 135 | # Log file 136 | 137 | # BlueJ files 138 | 139 | # Mobile Tools for Java (J2ME) 140 | 141 | # Package Files # 142 | 143 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 144 | 145 | ### Gradle ### 146 | .gradle 147 | build/ 148 | 149 | # Ignore Gradle GUI config 150 | gradle-app.setting 151 | 152 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 153 | !gradle-wrapper.jar 154 | 155 | # Cache of project 156 | .gradletasknamecache 157 | 158 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 159 | # gradle/wrapper/gradle-wrapper.properties 160 | 161 | ### Gradle Patch ### 162 | **/build/ 163 | 164 | # End of https://www.toptal.com/developers/gitignore/api/java,gradle,intellij,kotlin -------------------------------------------------------------------------------- /core/src/main/kotlin/uno/glfw/Joystick.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(ExperimentalUnsignedTypes::class) 2 | 3 | package uno.glfw 4 | 5 | import glm_.bool 6 | import kool.Ptr 7 | import kool.get 8 | import kool.stack 9 | import kool.toPtr 10 | import org.lwjgl.glfw.GLFW 11 | import org.lwjgl.glfw.GLFWGamepadState 12 | import org.lwjgl.glfw.GLFWJoystickCallbackI 13 | import uno.kotlin.ptrFloat 14 | import uno.kotlin.ptrInt 15 | import uno.kotlin.ptrUByte 16 | 17 | @JvmInline 18 | value class Joystick(val id: Int) { 19 | 20 | // --- [ glfwJoystickPresent ] --- 21 | val isPresent: Boolean 22 | get() = GLFW.glfwJoystickPresent(id) 23 | 24 | // --- [ glfwGetJoystickAxes ] --- 25 | 26 | val axes: FloatArray? 27 | get() = stack { s -> 28 | val pCount = s.ptrInt() 29 | val pAxes = GLFW.nglfwGetJoystickAxes(id, pCount.adr.toLong()).toPtr() 30 | if (pAxes.isValid) 31 | FloatArray(pCount[0]) { pAxes[it] } 32 | else null 33 | } 34 | 35 | // --- [ glfwGetJoystickButtons ] --- 36 | 37 | val buttons: BooleanArray? 38 | get() = stack { s -> 39 | val pCount = s.ptrInt() 40 | val pButtons = GLFW.nglfwGetJoystickButtons(id, pCount.adr.toLong()).toPtr() 41 | if (pButtons.isValid) 42 | BooleanArray(pCount[0]) { pButtons[0] == GLFW.GLFW_PRESS } 43 | else null 44 | } 45 | 46 | // --- [ glfwGetJoystickHats ] --- 47 | 48 | enum class Hat(val i: Int) { 49 | Centered(0), 50 | Up(1), 51 | Right(2), 52 | Down(4), 53 | Left(8), 54 | RightUp(Right.i or Up.i), 55 | RightDown(Right.i or Down.i), 56 | LeftUp(Left.i or Up.i), 57 | LeftDown(Left.i or Down.i); 58 | 59 | companion object { 60 | infix fun of(i: Int) = values().first { it.i == i } 61 | } 62 | } 63 | 64 | val hats: Array? 65 | get() = stack { s -> 66 | val pCount = s.ptrInt() 67 | val pHats = GLFW.nglfwGetJoystickHats(id, pCount.adr.toLong()).toPtr() 68 | if (pHats.isValid) 69 | Array(pCount[0]) { Hat of pHats[it].toInt() } 70 | else null 71 | } 72 | 73 | // --- [ glfwGetJoystickName ] --- 74 | val name: String? 75 | get() = GLFW.glfwGetJoystickName(id) 76 | 77 | // --- [ glfwGetJoystickGUID ] --- 78 | val guid: String? 79 | get() = GLFW.glfwGetJoystickGUID(id) 80 | 81 | // --- [ glfwSetJoystickUserPointer ] --- 82 | // --- [ glfwGetJoystickUserPointer ] --- 83 | var userPointer: Ptr<*> 84 | get() = GLFW.glfwGetJoystickUserPointer(id).toPtr() 85 | set(value) = GLFW.glfwSetJoystickUserPointer(id, value.adr.toLong()) 86 | 87 | // --- [ glfwJoystickIsGamepad ] --- 88 | val isGamepad: Boolean 89 | get() = GLFW.glfwJoystickIsGamepad(id) 90 | 91 | // --- [ glfwSetJoystickCallback ] --- 92 | var connectionCB: JoystickCB? 93 | @Deprecated(message = "Write only property", level = DeprecationLevel.HIDDEN) get() = error("") 94 | set(value) { 95 | val cb = value?.let { GLFWJoystickCallbackI { jid, event -> it(Joystick(jid), event == GLFW.GLFW_CONNECTED) } } 96 | GLFW.glfwSetJoystickCallback(cb)?.free() 97 | } 98 | 99 | // --- [ glfwUpdateGamepadMappings ] --- 100 | // -> glfw 101 | 102 | // --- [ glfwGetGamepadName ] --- 103 | val gamepadName: String? 104 | get() = GLFW.glfwGetGamepadName(id) 105 | 106 | // --- [ glfwGetGamepadState ] --- 107 | val gamepadState: GamepadState? 108 | get() = stack { s -> 109 | val pState = s.nmalloc(GLFWGamepadState.ALIGNOF, GLFWGamepadState.SIZEOF) 110 | if (GLFW.nglfwGetGamepadState(id, pState).bool) { 111 | val pButtons = s.ptrUByte(15) 112 | val pAxes = s.ptrFloat(6) 113 | GamepadState(UByteArray(15) { pButtons[it] }, FloatArray(6) { pAxes[it] }) 114 | } else null 115 | } 116 | 117 | companion object { 118 | val joysticks: Array = Array(16) { Joystick(GLFW.GLFW_JOYSTICK_1 + it) } 119 | } 120 | } 121 | 122 | 123 | class GamepadState(val buttons: UByteArray, val axes: FloatArray) -------------------------------------------------------------------------------- /gl/src/test/java/HelloWorld.java: -------------------------------------------------------------------------------- 1 | import org.lwjgl.*; 2 | import org.lwjgl.glfw.*; 3 | import org.lwjgl.opengl.*; 4 | import org.lwjgl.system.*; 5 | 6 | import java.nio.*; 7 | 8 | import static org.lwjgl.glfw.Callbacks.*; 9 | import static org.lwjgl.glfw.GLFW.*; 10 | import static org.lwjgl.opengl.GL11.*; 11 | import static org.lwjgl.system.MemoryStack.*; 12 | import static org.lwjgl.system.MemoryUtil.*; 13 | 14 | public class HelloWorld { 15 | 16 | // The window handle 17 | private long window; 18 | 19 | public void run() { 20 | System.out.println("Hello LWJGL " + Version.getVersion() + "!"); 21 | 22 | init(); 23 | loop(); 24 | 25 | // Free the window callbacks and destroy the window 26 | glfwFreeCallbacks(window); 27 | glfwDestroyWindow(window); 28 | 29 | // Terminate GLFW and free the error callback 30 | glfwTerminate(); 31 | glfwSetErrorCallback(null).free(); 32 | } 33 | 34 | private void init() { 35 | // Setup an error callback. The default implementation 36 | // will print the error message in System.err. 37 | GLFWErrorCallback.createPrint(System.err).set(); 38 | 39 | // Initialize GLFW. Most GLFW functions will not work before doing this. 40 | if (!glfwInit()) 41 | throw new IllegalStateException("Unable to initialize GLFW"); 42 | 43 | // Configure GLFW 44 | glfwDefaultWindowHints(); // optional, the current window hints are already the default 45 | glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // the window will stay hidden after creation 46 | glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable 47 | 48 | // Create the window 49 | window = glfwCreateWindow(300, 300, "Hello World!", NULL, NULL); 50 | if (window == NULL) 51 | throw new RuntimeException("Failed to create the GLFW window"); 52 | 53 | // Setup a key callback. It will be called every time a key is pressed, repeated or released. 54 | glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> { 55 | if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE) 56 | glfwSetWindowShouldClose(window, true); // We will detect this in the rendering loop 57 | }); 58 | 59 | // Get the thread stack and push a new frame 60 | try (MemoryStack stack = stackPush()) { 61 | IntBuffer pWidth = stack.mallocInt(1); // int* 62 | IntBuffer pHeight = stack.mallocInt(1); // int* 63 | 64 | // Get the window size passed to glfwCreateWindow 65 | glfwGetWindowSize(window, pWidth, pHeight); 66 | 67 | // Get the resolution of the primary monitor 68 | GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); 69 | 70 | // Center the window 71 | glfwSetWindowPos( 72 | window, 73 | (vidmode.width() - pWidth.get(0)) / 2, 74 | (vidmode.height() - pHeight.get(0)) / 2 75 | ); 76 | } // the stack frame is popped automatically 77 | 78 | // Make the OpenGL context current 79 | glfwMakeContextCurrent(window); 80 | // Enable v-sync 81 | glfwSwapInterval(1); 82 | 83 | // Make the window visible 84 | glfwShowWindow(window); 85 | } 86 | 87 | private void loop() { 88 | // This line is critical for LWJGL's interoperation with GLFW's 89 | // OpenGL context, or any context that is managed externally. 90 | // LWJGL detects the context that is current in the current thread, 91 | // creates the GLCapabilities instance and makes the OpenGL 92 | // bindings available for use. 93 | GL.createCapabilities(); 94 | 95 | // Set the clear color 96 | glClearColor(1.0f, 0.0f, 0.0f, 0.0f); 97 | 98 | // Run the rendering loop until the user has attempted to close 99 | // the window or has pressed the ESCAPE key. 100 | while (!glfwWindowShouldClose(window)) { 101 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer 102 | 103 | glfwSwapBuffers(window); // swap the color buffers 104 | 105 | // Poll for window events. The key callback above will only be 106 | // invoked during this call. 107 | glfwPollEvents(); 108 | } 109 | } 110 | 111 | public static void main(String[] args) { 112 | new HelloWorld().run(); 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /core/src/test/kotlin/uno/test.kt: -------------------------------------------------------------------------------- 1 | //package uno.awtOld 2 | // 3 | //import org.lwjgl.opengl.GL30C 4 | //import java.awtOld.* 5 | //import java.awtOld.event.KeyAdapter 6 | //import java.awtOld.event.KeyEvent 7 | //import javax.swing.* 8 | //import java.awtOld.event.WindowAdapter 9 | //import java.awtOld.event.WindowEvent 10 | //import javax.swing.JPanel 11 | //import kotlin.system.exitProcess 12 | //import javax.swing.RepaintManager 13 | // 14 | // 15 | // 16 | // 17 | //lateinit var test: Test 18 | // 19 | //fun main() { 20 | // test = Test() 21 | //} 22 | // 23 | //class Test : JFrame() { 24 | // 25 | // lateinit var borderPanel: BorderPanel 26 | // lateinit var centerPanel: CenterPanel 27 | // var viewer: Viewer 28 | // 29 | // init { 30 | // JFrame.setDefaultLookAndFeelDecorated(false) 31 | // isUndecorated = false 32 | // 33 | // val rm = RepaintManager.currentManager(this) 34 | // val b = rm.isDoubleBufferingEnabled() 35 | // rm.setDoubleBufferingEnabled(false) 36 | // 37 | // // Set Look & Feel 38 | // UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()) 39 | // 40 | // initComponents() 41 | // 42 | // addWindowListener(object : WindowAdapter() { 43 | // override fun windowClosing(event: WindowEvent) = println("windowClosing") 44 | // }) 45 | // 46 | // JPopupMenu.setDefaultLightWeightPopupEnabled(false) 47 | // 48 | // viewer = Viewer().apply { 49 | // isVisible = true 50 | // size = Dimension(500,500) 51 | // } 52 | // centerPanel.add(viewer, BorderLayout.CENTER) 53 | // 54 | // size = Dimension(500, 500) 55 | // location = Point(100, 50) 56 | // isVisible = true 57 | // 58 | // 59 | // } 60 | // 61 | // fun makeScreenShot(){ 62 | // viewer.renderImmediatly() 63 | // System.err.println("1") 64 | // viewer.renderImmediatly() 65 | // System.err.println("2") 66 | // viewer.renderImmediatly() 67 | // System.err.println("3") 68 | // 69 | // } 70 | // 71 | // fun initComponents() { 72 | // 73 | // borderPanel = BorderPanel().apply { size = Dimension(50, 50) } 74 | // centerPanel = CenterPanel().apply { 75 | // size = Dimension(50, 50) 76 | // } 77 | // 78 | // defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE 79 | // 80 | // centerPanel.layout = BorderLayout() 81 | // borderPanel.add(centerPanel, BorderLayout.CENTER) 82 | // 83 | // contentPane.add(borderPanel, BorderLayout.CENTER) 84 | // 85 | // pack() 86 | // 87 | // borderPanel.isVisible = true 88 | // centerPanel.isVisible = true 89 | // } 90 | // 91 | // override fun paint(g: Graphics) { 92 | // super.paint(g) 93 | // println("[Test] paint") 94 | // } 95 | //} 96 | // 97 | //class CenterPanel : JPanel() { 98 | // override fun paint(g: Graphics) { 99 | // super.paint(g) 100 | // test.viewer.paint(g) 101 | // println("[CenterPanel] paint") 102 | // } 103 | //} 104 | // 105 | //class BorderPanel : JPanel() { 106 | // override fun paint(g: Graphics) { 107 | // super.paint(g) 108 | // println("[BorderPanel] paint") 109 | // } 110 | //} 111 | // 112 | //class Viewer : LwjglCanvas() { 113 | // var count = 0; 114 | // var halloCount = 0; 115 | // init { 116 | // animated = false 117 | // focusTraversalKeysEnabled = false 118 | // val rm = RepaintManager.currentManager(this) 119 | // val b = rm.isDoubleBufferingEnabled() 120 | // rm.setDoubleBufferingEnabled(false) 121 | // 122 | // addKeyListener(object : KeyAdapter() { 123 | // override fun keyPressed(e: KeyEvent) { 124 | // println("keyPressed") 125 | // if (e.keyCode == KeyEvent.VK_F10) { 126 | // (test.centerPanel as JComponent).paintImmediately(0, 0, 500, 500) 127 | // } else if (e.keyCode == KeyEvent.VK_F11){ 128 | // 129 | // test.makeScreenShot() 130 | // 131 | // } else if (e.keyCode == KeyEvent.VK_ESCAPE) 132 | // exitProcess(0) 133 | // } 134 | // }) 135 | // } 136 | // 137 | // fun renderImmediatly(){ 138 | // val g = test.viewer.graphics; 139 | // if(g != null) { 140 | // halloCount++ 141 | // test.viewer.paint(g) 142 | // 143 | // //g.clearRect(0,0,500,500) 144 | // //g.drawString(" HALLO!"+ halloCount, 350, 250); 145 | // g.dispose() 146 | // } 147 | // } 148 | // 149 | // override fun render() { 150 | // val time = System.currentTimeMillis() 151 | // GL30C.glClearColor((time % 1000) / 1000f, 0.5f, 0f, 1f) 152 | // GL30C.glClear(GL_COLOR_BUFFER_BIT.i) 153 | // } 154 | //} -------------------------------------------------------------------------------- /core/src/main/kotlin/uno/glfw/inputEnums.kt: -------------------------------------------------------------------------------- 1 | package uno.glfw 2 | 3 | import org.lwjgl.glfw.GLFW.* 4 | 5 | enum class Key(val i: Int) { 6 | // Printable keys. 7 | SPACE(GLFW_KEY_SPACE), 8 | APOSTROPHE(GLFW_KEY_APOSTROPHE), 9 | COMMA(GLFW_KEY_COMMA), 10 | MINUS(GLFW_KEY_MINUS), 11 | PERIOD(GLFW_KEY_PERIOD), 12 | SLASH(GLFW_KEY_SLASH), 13 | `0`(GLFW_KEY_0), 14 | `1`(GLFW_KEY_1), 15 | `2`(GLFW_KEY_2), 16 | `3`(GLFW_KEY_3), 17 | `4`(GLFW_KEY_4), 18 | `5`(GLFW_KEY_5), 19 | `6`(GLFW_KEY_6), 20 | `7`(GLFW_KEY_7), 21 | `8`(GLFW_KEY_8), 22 | `9`(GLFW_KEY_9), 23 | SEMICOLON(GLFW_KEY_SEMICOLON), 24 | EQUAL(GLFW_KEY_EQUAL), 25 | A(GLFW_KEY_A), 26 | B(GLFW_KEY_B), 27 | C(GLFW_KEY_C), 28 | D(GLFW_KEY_D), 29 | E(GLFW_KEY_E), 30 | F(GLFW_KEY_F), 31 | G(GLFW_KEY_G), 32 | H(GLFW_KEY_H), 33 | I(GLFW_KEY_I), 34 | J(GLFW_KEY_J), 35 | K(GLFW_KEY_K), 36 | L(GLFW_KEY_L), 37 | M(GLFW_KEY_M), 38 | N(GLFW_KEY_N), 39 | O(GLFW_KEY_O), 40 | P(GLFW_KEY_P), 41 | Q(GLFW_KEY_Q), 42 | R(GLFW_KEY_R), 43 | S(GLFW_KEY_S), 44 | T(GLFW_KEY_T), 45 | U(GLFW_KEY_U), 46 | V(GLFW_KEY_V), 47 | W(GLFW_KEY_W), 48 | X(GLFW_KEY_X), 49 | Y(GLFW_KEY_Y), 50 | Z(GLFW_KEY_Z), 51 | LEFT_BRACKET(GLFW_KEY_LEFT_BRACKET), 52 | BACKSLASH(GLFW_KEY_BACKSLASH), 53 | RIGHT_BRACKET(GLFW_KEY_RIGHT_BRACKET), 54 | GRAVE_ACCENT(GLFW_KEY_GRAVE_ACCENT), 55 | WORLD_1(GLFW_KEY_WORLD_1), 56 | WORLD_2(GLFW_KEY_WORLD_2), 57 | 58 | // Function keys. 59 | 60 | ESCAPE(GLFW_KEY_ESCAPE), 61 | ENTER(GLFW_KEY_ENTER), 62 | TAB(GLFW_KEY_TAB), 63 | BACKSPACE(GLFW_KEY_BACKSPACE), 64 | INSERT(GLFW_KEY_INSERT), 65 | DELETE(GLFW_KEY_DELETE), 66 | RIGHT(GLFW_KEY_RIGHT), 67 | LEFT(GLFW_KEY_LEFT), 68 | DOWN(GLFW_KEY_DOWN), 69 | UP(GLFW_KEY_UP), 70 | PAGE_UP(GLFW_KEY_PAGE_UP), 71 | PAGE_DOWN(GLFW_KEY_PAGE_DOWN), 72 | HOME(GLFW_KEY_HOME), 73 | END(GLFW_KEY_END), 74 | CAPS_LOCK(GLFW_KEY_CAPS_LOCK), 75 | SCROLL_LOCK(GLFW_KEY_SCROLL_LOCK), 76 | NUM_LOCK(GLFW_KEY_NUM_LOCK), 77 | PRINT_SCREEN(GLFW_KEY_PRINT_SCREEN), 78 | PAUSE(GLFW_KEY_PAUSE), 79 | F1(GLFW_KEY_F1), 80 | F2(GLFW_KEY_F2), 81 | F3(GLFW_KEY_F3), 82 | F4(GLFW_KEY_F4), 83 | F5(GLFW_KEY_F5), 84 | F6(GLFW_KEY_F6), 85 | F7(GLFW_KEY_F7), 86 | F8(GLFW_KEY_F8), 87 | F9(GLFW_KEY_F9), 88 | F10(GLFW_KEY_F10), 89 | F11(GLFW_KEY_F11), 90 | F12(GLFW_KEY_F12), 91 | F13(GLFW_KEY_F13), 92 | F14(GLFW_KEY_F14), 93 | F15(GLFW_KEY_F15), 94 | F16(GLFW_KEY_F16), 95 | F17(GLFW_KEY_F17), 96 | F18(GLFW_KEY_F18), 97 | F19(GLFW_KEY_F19), 98 | F20(GLFW_KEY_F20), 99 | F21(GLFW_KEY_F21), 100 | F22(GLFW_KEY_F22), 101 | F23(GLFW_KEY_F23), 102 | F24(GLFW_KEY_F24), 103 | F25(GLFW_KEY_F25), 104 | KP_0(GLFW_KEY_KP_0), 105 | KP_1(GLFW_KEY_KP_1), 106 | KP_2(GLFW_KEY_KP_2), 107 | KP_3(GLFW_KEY_KP_3), 108 | KP_4(GLFW_KEY_KP_4), 109 | KP_5(GLFW_KEY_KP_5), 110 | KP_6(GLFW_KEY_KP_6), 111 | KP_7(GLFW_KEY_KP_7), 112 | KP_8(GLFW_KEY_KP_8), 113 | KP_9(GLFW_KEY_KP_9), 114 | KP_DECIMAL(GLFW_KEY_KP_DECIMAL), 115 | KP_DIVIDE(GLFW_KEY_KP_DIVIDE), 116 | KP_MULTIPLY(GLFW_KEY_KP_MULTIPLY), 117 | KP_SUBTRACT(GLFW_KEY_KP_SUBTRACT), 118 | KP_ADD(GLFW_KEY_KP_ADD), 119 | KP_ENTER(GLFW_KEY_KP_ENTER), 120 | KP_EQUAL(GLFW_KEY_KP_EQUAL), 121 | LEFT_SHIFT(GLFW_KEY_LEFT_SHIFT), 122 | LEFT_CONTROL(GLFW_KEY_LEFT_CONTROL), 123 | LEFT_ALT(GLFW_KEY_LEFT_ALT), 124 | LEFT_SUPER(GLFW_KEY_LEFT_SUPER), 125 | RIGHT_SHIFT(GLFW_KEY_RIGHT_SHIFT), 126 | RIGHT_CONTROL(GLFW_KEY_RIGHT_CONTROL), 127 | RIGHT_ALT(GLFW_KEY_RIGHT_ALT), 128 | RIGHT_SUPER(GLFW_KEY_RIGHT_SUPER), 129 | MENU(GLFW_KEY_MENU), 130 | LAST(GLFW_KEY_MENU); 131 | 132 | // --- [ glfwGetKeyName ] --- 133 | val keyName: String? 134 | get() = glfwGetKeyName(i, scancode) 135 | 136 | // --- [ glfwGetKeyScancode ] --- 137 | val scancode: Int 138 | get() = glfwGetKeyScancode(i) 139 | 140 | companion object { 141 | infix fun of(i: Int) = values().first { it.i == i } 142 | } 143 | } 144 | 145 | enum class InputAction(val i: Int) { 146 | Press(GLFW_PRESS), Release(GLFW_RELEASE), Repeat(GLFW_REPEAT); 147 | companion object { 148 | infix fun of(i: Int) = values().first { it.i == i } 149 | } 150 | } 151 | 152 | enum class MouseButton(@JvmField val i: Int) { 153 | 154 | `1`(0), 155 | `2`(1), 156 | `3`(2), 157 | `4`(3), 158 | `5`(4), 159 | `6`(5), 160 | `7`(6), 161 | `8`(7), 162 | LAST(`8`.i), 163 | LEFT(`1`.i), 164 | RIGHT(`2`.i), 165 | MIDDLE(`3`.i); 166 | 167 | companion object { 168 | infix fun of(i: Int) = values().first { it.i == i } 169 | } 170 | } -------------------------------------------------------------------------------- /core/src/test/kotlin/uno/ascii85.kt: -------------------------------------------------------------------------------- 1 | package uno 2 | 3 | import io.kotest.assertions.throwables.shouldThrow 4 | import io.kotest.core.spec.style.StringSpec 5 | import io.kotest.matchers.shouldBe 6 | import uno.convert.Ascii85 7 | import java.util.* 8 | 9 | 10 | class ascii85 : StringSpec() { 11 | 12 | init { 13 | 14 | "illegal argument" { shouldThrow { Ascii85.decode("") } } 15 | 16 | "wiki decode" { 17 | 18 | val encodedString = "9jqo^BlbD-BleB1DJ+*+F(f,q/0JhKFCj@.4Gp\$d7F!,L7@<6@)/0JDEF@3BB/F*&OCAfu2/AKYi(DIb:@FD,*)+C]U=@3BN#EcY" + 20 | "f8ATD3s@q?d\$AftVqCh[NqF-FD5W8ARlolDIal(DIduD.RTpAKYo'+CT/5+Cei#DII?(E,9)oF*2M7/c" 22 | 23 | val solution = "Man is distinguished, not only by his reason, but by this singular passion from other " + 24 | "animals, which is a lust of the mind, that by a perseverance of delight in the continued and " + 25 | "indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure." 26 | 27 | solution shouldBe String(Ascii85.decode(encodedString)) 28 | } 29 | 30 | "wiki decode new lines" { 31 | 32 | val encodedString = "9jqo^BlbD-BleB1DJ+*+F(f,q/0JhKFCj@.4Gp\$d7F!,L7@<6@)/0JDEF@3BB/F*&OCAfu2/AKY\n" + 34 | "i(DIb:@FD,*)+C]U=@3BN#EcYf8ATD3s@q?d\$AftVqCh[NqF-FD5W8ARlolDIa\n" + 35 | "l(DIduD.RTpAKYo'+CT/5+Cei#DII?(E,9)oF*2M7/c" 37 | 38 | val solution = "Man is distinguished, not only by his reason, but by this singular passion from other animals, " + 39 | "which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation " + 40 | "of knowledge, exceeds the short vehemence of any carnal pleasure." 41 | 42 | solution shouldBe String(Ascii85.decode(encodedString)) 43 | } 44 | 45 | "wiki decode ignore spaces" { 46 | 47 | val encodedString = "9jqo^BlbD-BleB1DJ+*+F (f,q/0JhKFCj@.4Gp\$d7F!,L7@<6@)/0JDEF@3BB/F*&OCAfu2/AKY\n" + 49 | "i(DIb:@FD,*)+C]U=@3BN#EcYf8ATD3s@q?d\$AftVqCh[NqF-FD5W8ARlolDIa\n" + 50 | "l(DIduD.RTpAKYo'+CT/5+Cei#DII?(E,9)oF*2M7/c" 52 | 53 | val solution = "Man is distinguished, not only by his reason, but by this singular passion from other animals, " + 54 | "which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation " + 55 | "of knowledge, exceeds the short vehemence of any carnal pleasure." 56 | 57 | solution shouldBe String(Ascii85.decode(encodedString)) 58 | } 59 | 60 | "encode" { 61 | 62 | val solution = "9jqo^BlbD-BleB1DJ+*+F(f,q/0JhKFCj@.4Gp\$d7F!,L7@<6@)/0JDEF@3BB/F*&OCAfu2/AKY" + 64 | "i(DIb:@FD,*)+C]U=@3BN#EcYf8ATD3s@q?d\$AftVqCh[NqF-FD5W8ARlolDIa" + 65 | "l(DIduD.RTpAKYo'+CT/5+Cei#DII?(E,9)oF*2M7/c" 67 | 68 | val decodedString = "Man is distinguished, not only by his reason, but by this singular passion from other animals, " + 69 | "which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation " + 70 | "of knowledge, exceeds the short vehemence of any carnal pleasure." 71 | 72 | solution shouldBe Ascii85.encode(decodedString.toByteArray()) 73 | } 74 | 75 | "negative bytes" { 76 | 77 | val randoms = byteArrayOf(-127, -127, -127, -127) 78 | val encoded = Ascii85.encode(randoms) 79 | 80 | Arrays.equals(randoms, Ascii85.decode(encoded)) shouldBe true 81 | } 82 | // 83 | // "overflow" { 84 | // 85 | //// val N = 0x23C34567 86 | // val N = 0x03C34567 87 | // val sb = StringBuilder(N) 88 | // 89 | // for (i in 0 until N) 90 | // sb.append('-') 91 | // 92 | // val encodedString = sb.toString() 93 | // 94 | // Ascii85.decode(encodedString) shouldNotBe null 95 | // } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /core/src/main/kotlin/uno/glfw/helpers.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(ExperimentalUnsignedTypes::class) 2 | 3 | package uno.glfw 4 | 5 | import glm_.vec2.Vec2 6 | import glm_.vec2.Vec2d 7 | import glm_.vec2.Vec2i 8 | import kool.* 9 | import org.lwjgl.glfw.GLFW 10 | import org.lwjgl.glfw.GLFW.glfwDestroyCursor 11 | import org.lwjgl.glfw.GLFWGammaRamp 12 | import org.lwjgl.glfw.GLFWImage 13 | import org.lwjgl.system.MemoryStack 14 | import org.lwjgl.system.MemoryUtil 15 | import org.lwjgl.system.MemoryUtil.* 16 | import uno.kotlin.ptrUByte 17 | import uno.kotlin.ptrUShort 18 | 19 | 20 | typealias GLFWErrorCallbackT = (error: glfw.Error, desc: String) -> Unit 21 | 22 | class GlfwVidMode(val size: Vec2i, val redBits: Int, val greenBits: Int, val blueBits: Int, val refreshRate: Int) { 23 | constructor(ptr: Ptr) : this(Vec2i(ptr), ptr[2], ptr[3], ptr[4], ptr[5]) 24 | 25 | companion object { 26 | val Size = Vec2i.size + Int.BYTES * 4 27 | } 28 | } 29 | 30 | operator fun Ptr.get(index: Int) = GlfwVidMode((adr.toLong() + index * GlfwVidMode.Size).toPtr()) 31 | 32 | class GlfwGammaRamp(val red: IntArray, val green: IntArray, val blue: IntArray) { 33 | 34 | infix fun toStack(stack: MemoryStack): Ptr { 35 | val r = stack.ptrUShort(red.size) 36 | val g = stack.ptrUShort(red.size) 37 | val b = stack.ptrUShort(red.size) 38 | for (i in red.indices) { 39 | r[i] = red[i].toUShort() 40 | g[i] = green[i].toUShort() 41 | b[i] = blue[i].toUShort() 42 | } 43 | val ptr = stack.nmalloc(GLFWGammaRamp.ALIGNOF, GLFWGammaRamp.SIZEOF) 44 | memPutAddress(ptr + GLFWGammaRamp.RED, r.adr.toLong()) 45 | memPutAddress(ptr + GLFWGammaRamp.GREEN, g.adr.toLong()) 46 | memPutAddress(ptr + GLFWGammaRamp.BLUE, b.adr.toLong()) 47 | memPutInt(ptr + GLFWGammaRamp.SIZE, red.size) 48 | return ptr.toPtr() 49 | } 50 | } 51 | 52 | fun GlfwGammaRamp(ptr: Ptr): GlfwGammaRamp { 53 | val red = ptr.toPtr() 54 | val green = red + 1 55 | val blue = red + 2 56 | val size = (red + 3).toPtr()[0] 57 | return GlfwGammaRamp(IntArray(size) { red[it].toInt() }, IntArray(size) { green[it].toInt() }, IntArray(size) { blue[it].toInt() }) 58 | } 59 | 60 | @JvmInline 61 | value class GlfwCursor(val handle: Long) { 62 | // --- [ glfwCreateCursor ] --- 63 | constructor(image: GlfwImage, hot: Int) : this(image, hot, hot) 64 | constructor(image: GlfwImage, xHot: Int, yHot: Int) : this(stack { GLFW.nglfwCreateCursor(image.toStack(it).adr.toLong(), xHot, yHot) }) 65 | 66 | // --- [ glfwDestroyCursor ] --- 67 | fun destroy() = glfwDestroyCursor(handle) 68 | 69 | val isValid: Boolean 70 | get() = handle != MemoryUtil.NULL 71 | val isNotValid: Boolean 72 | get() = !isValid 73 | 74 | companion object { 75 | val NULL = GlfwCursor(MemoryUtil.NULL) 76 | } 77 | } 78 | 79 | typealias WindowPosCB = (window: GlfwWindow, pos: Vec2i) -> Unit 80 | typealias WindowSizeCB = (window: GlfwWindow, size: Vec2i) -> Unit 81 | typealias WindowCloseCB = (window: GlfwWindow) -> Unit 82 | typealias WindowRefreshCB = (window: GlfwWindow) -> Unit 83 | typealias WindowFocusCB = (window: GlfwWindow, focused: Boolean) -> Unit 84 | typealias WindowIconifyCB = (window: GlfwWindow, iconified: Boolean) -> Unit 85 | typealias WindowMaximizeCB = (window: GlfwWindow, maximized: Boolean) -> Unit 86 | typealias FramebufferSizeCB = (window: GlfwWindow, size: Vec2i) -> Unit 87 | typealias WindowContentScaleCB = (window: GlfwWindow, scale: Vec2) -> Unit 88 | typealias KeyCB = (window: GlfwWindow, key: Key, scanCode: Int, action: InputAction, mods: Int) -> Unit 89 | typealias CharCB = (window: GlfwWindow, codePoint: Int) -> Unit 90 | typealias CharModsCB = (window: GlfwWindow, codePoint: Int, mods: Int) -> Unit 91 | typealias MouseButtonCB = (window: GlfwWindow, button: Int, action: Int, mods: Int) -> Unit 92 | typealias CursorPosCB = (window: GlfwWindow, pos: Vec2d) -> Unit 93 | typealias CursorEnterCB = (window: GlfwWindow, entered: Boolean) -> Unit 94 | typealias ScrollCB = (window: GlfwWindow, scroll: Vec2d) -> Unit 95 | typealias DropCB = (window: GlfwWindow, names: Array) -> Unit 96 | 97 | typealias JoystickCB = (joystick: Joystick, connected: Boolean) -> Unit 98 | 99 | enum class VSync { 100 | AdaptiveHalfRate, Adaptive, OFF, ON; 101 | 102 | val i = ordinal - 2 103 | } 104 | 105 | typealias GLFWglproc = Long 106 | 107 | class GlfwImage(val width: Int, val height: Int, val pixels: UByteArray) { 108 | constructor(size: Int, pixels: UByteArray) : this(size, size, pixels) 109 | 110 | infix fun toStack(stack: MemoryStack): Ptr { 111 | val ptr = stack.nmalloc(GLFWImage.ALIGNOF, GLFWImage.SIZEOF) 112 | memPutInt(ptr + GLFWImage.WIDTH, width) 113 | memPutInt(ptr + GLFWImage.HEIGHT, height) 114 | val pPixels = stack.ptrUByte(pixels.size) 115 | for (i in pixels.indices) 116 | pPixels[i] = pixels[i] 117 | memPutAddress(ptr + GLFWImage.PIXELS, pPixels.adr.toLong()) 118 | return ptr.toPtr() 119 | } 120 | } -------------------------------------------------------------------------------- /core/src/test/kotlin/uno/awe.kt: -------------------------------------------------------------------------------- 1 | package uno 2 | 3 | //import glm_.and 4 | //import org.lwjgl.glfw.Callbacks.glfwFreeCallbacks 5 | //import org.lwjgl.glfw.GLFW.* 6 | //import org.lwjgl.glfw.GLFWErrorCallback 7 | //import org.lwjgl.glfw.GLFWVidMode 8 | //import org.lwjgl.opengl.GL 9 | //import org.lwjgl.opengl.GL11C.GL_COLOR_BUFFER_BIT 10 | //import org.lwjgl.opengl.GL11C.glClear 11 | //import org.lwjgl.system.MemoryStack.stackPush 12 | //import org.lwjgl.system.MemoryUtil.NULL 13 | //import org.lwjgl.util.tinyfd.TinyFileDialogs.* 14 | //import java.util.* 15 | // 16 | //object HelloTinyFD { 17 | // 18 | // @JvmStatic 19 | // fun main(args: Array) { 20 | // GLFWErrorCallback.createPrint().set() 21 | // if (!glfwInit()) { 22 | // throw IllegalStateException("Unable to initialize GLFW") 23 | // } 24 | // 25 | // val window = glfwCreateWindow(300, 300, "Hello tiny file dialogs!", NULL, NULL) 26 | // if (window == NULL) { 27 | // throw RuntimeException("Failed to create the GLFW window") 28 | // } 29 | // 30 | // glfwSetKeyCallback(window) { windowHnd, key, scancode, action, mods -> 31 | // if (action == GLFW_RELEASE) { 32 | // return@glfwSetKeyCallback 33 | // } 34 | // 35 | // when (key) { 36 | // GLFW_KEY_ESCAPE -> glfwSetWindowShouldClose(windowHnd, true) 37 | // GLFW_KEY_B -> tinyfd_beep() 38 | // GLFW_KEY_N -> { 39 | // println("\nOpening notification popup...") 40 | // println(tinyfd_notifyPopup("Please read...", "...this message.", "info")) 41 | // } 42 | // GLFW_KEY_1 -> { 43 | // println("\nOpening message dialog...") 44 | // println(if (tinyfd_messageBox("Please read...", "...this message.", "okcancel", "info", true)) "OK" else "Cancel") 45 | // } 46 | // GLFW_KEY_2 -> { 47 | // println("\nOpening input box dialog...") 48 | // println(tinyfd_inputBox("Input Value", "How old are you?", "30")) 49 | // } 50 | // GLFW_KEY_3 -> { 51 | // println("\nOpening file open dialog...") 52 | // println(tinyfd_openFileDialog("Open File(s)", "", null, null, true)) 53 | // } 54 | // GLFW_KEY_4 -> stackPush().use({ stack -> 55 | // val aFilterPatterns = stack.mallocPointer(2) 56 | // 57 | // aFilterPatterns.put(stack.UTF8("*.jpg")) 58 | // aFilterPatterns.put(stack.UTF8("*.png")) 59 | // 60 | // aFilterPatterns.flip() 61 | // 62 | // println("\nOpening file save dialog...") 63 | // println(tinyfd_saveFileDialog("Save Image", "", aFilterPatterns, "Image files (*.jpg, *.png)")) 64 | // }) 65 | // GLFW_KEY_5 -> { 66 | // println("\nOpening folder select dialog...") 67 | // println(tinyfd_selectFolderDialog("Select Folder", "")) 68 | // } 69 | // GLFW_KEY_6 -> { 70 | // println("\nOpening color chooser dialog...") 71 | // stackPush().use { stack -> 72 | // val color = stack.malloc(3) 73 | // val hex = tinyfd_colorChooser("Choose Color", "#FF00FF", null, color) 74 | // println(hex) 75 | // if (hex != null) { 76 | // println("\tR: " + (color.get(0) and 0xFF)) 77 | // println("\tG: " + (color.get(1) and 0xFF)) 78 | // println("\tB: " + (color.get(2) and 0xFF)) 79 | // } 80 | // } 81 | // } 82 | // } 83 | // } 84 | // 85 | // // Center window 86 | // val vidmode = Objects.requireNonNull(glfwGetVideoMode(glfwGetPrimaryMonitor())) 87 | // glfwSetWindowPos( 88 | // window, 89 | // (vidmode.width() - 300) / 2, 90 | // (vidmode.height() - 300) / 2 91 | // ) 92 | // 93 | // glfwMakeContextCurrent(window) 94 | // GL.createCapabilities() 95 | // 96 | // glfwSwapInterval(1) 97 | // 98 | // tinyfd_messageBox("tinyfd_query", "", "ok", "info", true) 99 | // println("tiny file dialogs " + tinyfd_version + " (" + tinyfd_response() + ")") 100 | // println() 101 | // println(tinyfd_needs) 102 | // println() 103 | // println("Press 1 to launch a message dialog.") 104 | // println("Press 2 to launch an input box fialog.") 105 | // println("Press 3 to launch a file open dialog.") 106 | // println("Press 4 to launch a file save dialog.") 107 | // println("Press 5 to launch a folder select dialog.") 108 | // println("Press 6 to launch a color chooser dialog.") 109 | // while (!glfwWindowShouldClose(window)) { 110 | // glfwPollEvents() 111 | // 112 | // glClear(GL_COLOR_BUFFER_BIT) 113 | // glfwSwapBuffers(window) 114 | // } 115 | // 116 | // GL.setCapabilities(null) 117 | // 118 | // glfwFreeCallbacks(window) 119 | // glfwDestroyWindow(window) 120 | // glfwTerminate() 121 | // 122 | // Objects.requireNonNull(glfwSetErrorCallback(null)).free() 123 | // } 124 | // 125 | //} -------------------------------------------------------------------------------- /core/src/main/kotlin/uno/buffer/multiple constuctors.kt: -------------------------------------------------------------------------------- 1 | package uno.buffer 2 | 3 | 4 | // TODO remove whole /buffer in favor of kool 5 | 6 | import kool.* 7 | import org.lwjgl.PointerBuffer 8 | import org.lwjgl.system.MemoryUtil 9 | import uno.kotlin.Quadruple 10 | import uno.kotlin.Quintuple 11 | import java.nio.* 12 | 13 | /** 14 | * Created by elect on 05/03/17. 15 | */ 16 | 17 | 18 | //fun PointerBuffer(capacity: Int): PointerBuffer = MemoryUtil.memCallocPointer(capacity) 19 | //fun PointerBuffer(capacity: IntBuffer): PointerBuffer = MemoryUtil.memCallocPointer(capacity[0]) 20 | //fun PointerBuffer(capacity: IntArray): PointerBuffer = MemoryUtil.memCallocPointer(capacity[0]) 21 | 22 | // i.e: clear color buffer 23 | fun FloatBuffer.put(vararg floats: Float): FloatBuffer { 24 | for (i in 0 until floats.size) 25 | put(pos + i, floats[i]) 26 | return this 27 | } 28 | 29 | fun ByteBuffers(sizeA: Int, sizeB: Int) = Pair(Buffer(sizeA), Buffer(sizeB)) 30 | fun ByteBuffers(sizeA: Int, sizeB: Int, sizeC: Int) = Triple(Buffer(sizeA), Buffer(sizeB), Buffer(sizeC)) 31 | fun ByteBuffers(sizeA: Int, sizeB: Int, sizeC: Int, sizeD: Int) = Quadruple(Buffer(sizeA), Buffer(sizeB), Buffer(sizeC), Buffer(sizeD)) 32 | fun ByteBuffers(sizeA: Int, sizeB: Int, sizeC: Int, sizeD: Int, sizeE: Int) = Quintuple(Buffer(sizeA), Buffer(sizeB), Buffer(sizeC), Buffer(sizeD), Buffer(sizeE)) 33 | 34 | fun ShortBuffers(sizeA: Int, sizeB: Int) = Pair(ShortBuffer(sizeA), ShortBuffer(sizeB)) 35 | fun ShortBuffers(sizeA: Int, sizeB: Int, sizeC: Int) = Triple(ShortBuffer(sizeA), ShortBuffer(sizeB), ShortBuffer(sizeC)) 36 | fun ShortBuffers(sizeA: Int, sizeB: Int, sizeC: Int, sizeD: Int) = Quadruple(ShortBuffer(sizeA), ShortBuffer(sizeB), ShortBuffer(sizeC), ShortBuffer(sizeD)) 37 | fun ShortBuffers(sizeA: Int, sizeB: Int, sizeC: Int, sizeD: Int, sizeE: Int) = Quintuple(ShortBuffer(sizeA), ShortBuffer(sizeB), ShortBuffer(sizeC), ShortBuffer(sizeD), ShortBuffer(sizeE)) 38 | 39 | fun IntBuffers(sizeA: Int, sizeB: Int) = Pair(IntBuffer(sizeA), IntBuffer(sizeB)) 40 | fun IntBuffers(sizeA: Int, sizeB: Int, sizeC: Int) = Triple(IntBuffer(sizeA), IntBuffer(sizeB), IntBuffer(sizeC)) 41 | fun IntBuffers(sizeA: Int, sizeB: Int, sizeC: Int, sizeD: Int) = Quadruple(IntBuffer(sizeA), IntBuffer(sizeB), IntBuffer(sizeC), IntBuffer(sizeD)) 42 | fun IntBuffers(sizeA: Int, sizeB: Int, sizeC: Int, sizeD: Int, sizeE: Int) = Quintuple(IntBuffer(sizeA), IntBuffer(sizeB), IntBuffer(sizeC), IntBuffer(sizeD), IntBuffer(sizeE)) 43 | 44 | fun LongBuffers(sizeA: Int, sizeB: Int) = Pair(LongBuffer(sizeA), LongBuffer(sizeB)) 45 | fun LongBuffers(sizeA: Int, sizeB: Int, sizeC: Int) = Triple(LongBuffer(sizeA), LongBuffer(sizeB), LongBuffer(sizeC)) 46 | fun LongBuffers(sizeA: Int, sizeB: Int, sizeC: Int, sizeD: Int) = Quadruple(LongBuffer(sizeA), LongBuffer(sizeB), LongBuffer(sizeC), LongBuffer(sizeD)) 47 | fun LongBuffers(sizeA: Int, sizeB: Int, sizeC: Int, sizeD: Int, sizeE: Int) = Quintuple(LongBuffer(sizeA), LongBuffer(sizeB), LongBuffer(sizeC), LongBuffer(sizeD), LongBuffer(sizeE)) 48 | 49 | fun FloatBuffers(sizeA: Int, sizeB: Int) = Pair(FloatBuffer(sizeA), FloatBuffer(sizeB)) 50 | fun FloatBuffers(sizeA: Int, sizeB: Int, sizeC: Int) = Triple(FloatBuffer(sizeA), FloatBuffer(sizeB), FloatBuffer(sizeC)) 51 | fun FloatBuffers(sizeA: Int, sizeB: Int, sizeC: Int, sizeD: Int) = Quadruple(FloatBuffer(sizeA), FloatBuffer(sizeB), FloatBuffer(sizeC), FloatBuffer(sizeD)) 52 | fun FloatBuffers(sizeA: Int, sizeB: Int, sizeC: Int, sizeD: Int, sizeE: Int) = Quintuple(FloatBuffer(sizeA), FloatBuffer(sizeB), FloatBuffer(sizeC), FloatBuffer(sizeD), FloatBuffer(sizeE)) 53 | 54 | fun DoubleBuffers(sizeA: Int, sizeB: Int) = Pair(DoubleBuffer(sizeA), DoubleBuffer(sizeB)) 55 | fun DoubleBuffers(sizeA: Int, sizeB: Int, sizeC: Int) = Triple(DoubleBuffer(sizeA), DoubleBuffer(sizeB), DoubleBuffer(sizeC)) 56 | fun DoubleBuffers(sizeA: Int, sizeB: Int, sizeC: Int, sizeD: Int) = Quadruple(DoubleBuffer(sizeA), DoubleBuffer(sizeB), DoubleBuffer(sizeC), DoubleBuffer(sizeD)) 57 | fun DoubleBuffers(sizeA: Int, sizeB: Int, sizeC: Int, sizeD: Int, sizeE: Int) = Quintuple(DoubleBuffer(sizeA), DoubleBuffer(sizeB), DoubleBuffer(sizeC), DoubleBuffer(sizeD), DoubleBuffer(sizeE)) 58 | 59 | fun memFree(vararg buffers: Buffer) { 60 | for (i in 0 until buffers.size) 61 | MemoryUtil.memFree(buffers[i]) 62 | } 63 | 64 | //@Suppress("UNCHECKED_CAST") 65 | //fun withBuffer(list: List<*>, block: ByteBuffer.() -> Unit) { 66 | // when (list.elementAt(0)) { 67 | // is Byte -> Buffer(list.size).apply { 68 | // val l = list as List 69 | // for (i in l.indices) 70 | // put(i, l[i]) 71 | // } 72 | // is Short -> Buffer(list.size * Short.BYTES).apply { 73 | // val l = list as List 74 | // for (i in l.indices) putShort(i * Short.BYTES, l[i]) 75 | // } 76 | // is Int -> Buffer(list.size * Int.BYTES).apply { 77 | // val l = list as List 78 | // for (i in l.indices) putInt(i * Int.BYTES, l[i]) 79 | // } 80 | // is Long -> Buffer(list.size * Long.BYTES).apply { 81 | // val l = list as List 82 | // for (i in l.indices) putLong(i * Long.BYTES, l[i]) 83 | // } 84 | // is Float -> Buffer(list.size * Float.BYTES).apply { 85 | // val l = list as List 86 | // for (i in l.indices) putFloat(i * Float.BYTES, l[i]) 87 | // } 88 | // is Double -> Buffer(list.size * Double.BYTES).apply { 89 | // val l = list as List 90 | // for (i in l.indices) putDouble(i * Double.BYTES, l[i]) 91 | // } 92 | // else -> throw Error("unsupported type") 93 | // }.run { 94 | // block() 95 | // free() 96 | // } 97 | //} -------------------------------------------------------------------------------- /core/src/main/kotlin/uno/buffer/advanced of.kt: -------------------------------------------------------------------------------- 1 | package uno.buffer 2 | 3 | import glm_.b 4 | import glm_.vec2.Vec2 5 | import glm_.vec2.Vec2i 6 | import glm_.vec3.Vec3 7 | import glm_.vec3.Vec3i 8 | import glm_.vec4.Vec4 9 | import glm_.vec4.Vec4b 10 | import glm_.vec4.Vec4i 11 | import glm_.vec4.Vec4ub 12 | //import gln.glf.Vertex 13 | //import gln.glf.glf 14 | import kool.* 15 | import java.nio.ByteBuffer 16 | import java.nio.FloatBuffer 17 | import java.nio.IntBuffer 18 | 19 | 20 | fun bufferOf(vertices: Collection<*>): ByteBuffer { 21 | val res: ByteBuffer 22 | when (vertices.elementAt(0)) { 23 | is Vec2 -> { 24 | res = Buffer(Vec2.size * vertices.size) 25 | for (i in vertices.indices) 26 | (vertices.elementAt(i) as Vec2).to(res, i * Vec2.size) 27 | } 28 | is Vec3 -> { 29 | res = Buffer(Vec3.size * vertices.size) 30 | for (i in vertices.indices) 31 | (vertices.elementAt(i) as Vec3).to(res, i * Vec3.size) 32 | } 33 | is Vec4 -> { 34 | res = Buffer(Vec4.size * vertices.size) 35 | for (i in vertices.indices) 36 | (vertices.elementAt(i) as Vec4).to(res, i * Vec4.size) 37 | } 38 | // is Vertex.pos2_tc2 -> { 39 | // res = Buffer(glf.pos2_tc2.stride * vertices.size) 40 | // for (i in vertices.indices) { 41 | // val v = vertices.elementAt(i) as Vertex.pos2_tc2 42 | // v.p.to(res, i * glf.pos2_tc2.stride) 43 | // v.t.to(res, i * glf.pos2_tc2.stride + Vec2.size) 44 | // } 45 | // } 46 | // is Vertex.pos3_col4ub -> { 47 | // res = Buffer(glf.pos3_col4ub.stride * vertices.size) 48 | // for (i in vertices.indices) { 49 | // val v = vertices.elementAt(i) as Vertex.pos3_col4ub 50 | // v.p.to(res, i * glf.pos2_tc2.stride) 51 | // v.c.to(res, i * glf.pos2_tc2.stride + Vec3.size) 52 | // } 53 | // } 54 | else -> throw Error() 55 | } 56 | return res 57 | } 58 | 59 | fun bufferOf(vararg elements: Any): ByteBuffer { 60 | val size = elements.sumOf { 61 | when (it) { 62 | is Float, Int, Vec4b -> Float.BYTES 63 | is Vec2 -> Vec2.size 64 | is Vec3 -> Vec3.size 65 | is Vec4 -> Vec4.size 66 | // is Vertex.pos2_tc2 -> Vec2.size * 2 67 | // is Vertex.pos3_col4ub -> Vec3.size + Vec4ub.size 68 | // is Vertex.pos3_nor3_col4 -> Vec3.size * 2 + Vec4.size 69 | else -> throw Exception("Invalid") 70 | } 71 | } 72 | val res = Buffer(size) 73 | var offset = 0 74 | for (e in elements) 75 | when (e) { 76 | is Float -> { 77 | res.putFloat(offset, e) 78 | offset += Float.BYTES 79 | } 80 | is Int -> { 81 | res.putInt(offset, e) 82 | offset += Int.BYTES 83 | } 84 | is Vec4b -> { 85 | e.to(res, offset) 86 | offset += Vec4b.size 87 | } 88 | is Vec2 -> { 89 | e.to(res, offset) 90 | offset += Vec2.size 91 | } 92 | is Vec3 -> { 93 | e.to(res, offset) 94 | offset += Vec3.size 95 | } 96 | is Vec4 -> { 97 | e.to(res, offset) 98 | offset += Vec4.size 99 | } 100 | // is Vertex.pos2_tc2 -> { 101 | // e.to(res, offset) 102 | // offset += Vec4b.size 103 | // } 104 | // is Vertex.pos3_col4ub -> Vec3.size + Vec4ub.size 105 | // is Vertex.pos3_nor3_col4 -> Vec3.size * 2 + Vec4.size 106 | else -> throw Exception("Invalid") 107 | } 108 | return res 109 | } 110 | 111 | fun floatBufferOf(vertices: Collection<*>): FloatBuffer { 112 | val res: FloatBuffer 113 | when (vertices.elementAt(0)) { 114 | is Float -> res = FloatBuffer(vertices.size) { vertices.elementAt(it) as Float } 115 | is Vec2 -> { 116 | res = FloatBuffer(Vec2.length * vertices.size) 117 | for (i in vertices.indices) 118 | (vertices.elementAt(i) as Vec2).to(res, i * Vec2.length) 119 | } 120 | is Vec3 -> { 121 | res = FloatBuffer(Vec3.length * vertices.size) 122 | for (i in vertices.indices) 123 | (vertices.elementAt(i) as Vec3).to(res, i * Vec3.length) 124 | } 125 | is Vec4 -> { 126 | res = FloatBuffer(Vec4.length * vertices.size) 127 | for (i in vertices.indices) 128 | (vertices.elementAt(i) as Vec4).to(res, i * Vec4.length) 129 | } 130 | else -> throw Error() 131 | } 132 | return res 133 | } 134 | 135 | fun intBufferOf(vertices: Collection<*>): IntBuffer { 136 | val res: IntBuffer 137 | when (vertices.elementAt(0)) { 138 | is Int -> res = IntBuffer(vertices.size) { vertices.elementAt(it) as Int } 139 | is Vec2i -> { 140 | res = IntBuffer(Vec2i.length * vertices.size) 141 | for (i in vertices.indices) 142 | (vertices.elementAt(i) as Vec2i).to(res, i * Vec2i.length) 143 | } 144 | is Vec3i -> { 145 | res = IntBuffer(Vec3i.length * vertices.size) 146 | for (i in vertices.indices) 147 | (vertices.elementAt(i) as Vec3i).to(res, i * Vec3i.length) 148 | } 149 | is Vec4i -> { 150 | res = IntBuffer(Vec4i.length * vertices.size) 151 | for (i in vertices.indices) 152 | (vertices.elementAt(i) as Vec4i).to(res, i * Vec4i.length) 153 | } 154 | else -> throw Error() 155 | } 156 | return res 157 | } 158 | 159 | fun bufferOf(s: String) = ByteBuffer(s.length) { s[it].b } -------------------------------------------------------------------------------- /core/src/main/kotlin/uno/convert/ascii85.kt: -------------------------------------------------------------------------------- 1 | package uno.convert 2 | 3 | import glm_.b 4 | import glm_.i 5 | import uno.glm.toBidDec 6 | import unsigned.toULong 7 | import java.nio.ByteBuffer 8 | 9 | object Ascii85 { 10 | 11 | private val ASCII_SHIFT = 33 12 | 13 | private val BASE85_POW = intArrayOf( 14 | 1, 15 | 85, 16 | 85 * 85, 17 | 85 * 85 * 85, 18 | 85 * 85 * 85 * 85) 19 | 20 | fun encode(payload: ByteArray): String { 21 | 22 | if (payload.isEmpty()) throw IllegalArgumentException("You must provide a non-zero length input") 23 | 24 | /* By using five ASCII characters to represent four bytes of binary data the encoded size ¹⁄₄ is larger than 25 | the original */ 26 | val stringBuff = StringBuilder(payload.size * 5 / 4) 27 | // We break the payload into int (4 bytes) 28 | val chunk = ByteArray(4) 29 | var chunkIndex = 0 30 | for (currByte in payload) { 31 | 32 | chunk[chunkIndex++] = currByte 33 | 34 | if (chunkIndex == 4) { 35 | 36 | val value = chunk.int 37 | /* Because all-zero data is quite common, an exception is made for the sake of data compression, and an 38 | all-zero group is encoded as a single character "z" instead of "!!!!!". */ 39 | if (value == 0) 40 | stringBuff.append('z') 41 | else 42 | stringBuff.append(encodeChunk(value)) 43 | 44 | chunk.fill(0) 45 | chunkIndex = 0 46 | } 47 | } 48 | 49 | // If we didn't end on 0, then we need some padding 50 | if (chunkIndex > 0) { 51 | 52 | val numPadded = chunk.size - chunkIndex 53 | chunk.fill(0, chunkIndex, chunk.size) 54 | val value = chunk.int 55 | val encodedChunk = encodeChunk(value) 56 | for (i in 0 until encodedChunk.size - numPadded) 57 | stringBuff.append(encodedChunk[i]) 58 | } 59 | 60 | return stringBuff.toString() 61 | } 62 | 63 | private fun encodeChunk(value: Int): CharArray { 64 | // transform value to unsigned long 65 | var longValue = value.toULong() 66 | val encodedChunk = CharArray(5) 67 | for (i in encodedChunk.indices) { 68 | encodedChunk[i] = (longValue / BASE85_POW[4 - i] + ASCII_SHIFT).toInt().toChar() 69 | longValue %= BASE85_POW[4 - i] 70 | } 71 | return encodedChunk 72 | } 73 | 74 | /** 75 | * This is a very simple base85 decoder. It respects the 'z' optimization for empty chunks, and strips whitespace 76 | * between characters to respect line limits. 77 | * 78 | * @param chars The input characters that are base85 encoded. 79 | * @return The binary data decoded from the input 80 | * @see [Ascii85](https://en.wikipedia.org/wiki/Ascii85) 81 | */ 82 | fun decode(chars: String): ByteArray { 83 | 84 | if (chars.isEmpty()) throw IllegalArgumentException("You must provide a non-zero length input") 85 | 86 | /* By using five ASCII characters to represent four bytes of binary data the encoded size ¹⁄₄ is larger than 87 | the original */ 88 | val decodedLength = chars.length.toBidDec() * 4.toBidDec() / 5.toBidDec() 89 | val buffer = ByteBuffer.allocate(decodedLength.i) 90 | val payload = chars 91 | /* Whitespace and new lines characters may occur anywhere to accommodate line length limitations. 92 | So lets strip it. */ 93 | .filter { it != ' ' && it != '\n' } 94 | .toByteArray() // Since Base85 is an ascii encoder, we don't need to get the bytes as UTF-8. 95 | 96 | val chunk = ByteArray(5) 97 | var chunkIndex = 0 98 | for (byte in payload) { 99 | /* Because all-zero data is quite common, an exception is made for the sake of data compression, and an 100 | all-zero group is encoded as a single character "z" instead of "!!!!!". */ 101 | if (byte == 'z'.b) { 102 | 103 | if (chunkIndex > 0) throw IllegalArgumentException("The payload is not base 85 encoded.") 104 | 105 | chunk[chunkIndex++] = '!'.b 106 | chunk[chunkIndex++] = '!'.b 107 | chunk[chunkIndex++] = '!'.b 108 | chunk[chunkIndex++] = '!'.b 109 | chunk[chunkIndex++] = '!'.b 110 | 111 | } else 112 | chunk[chunkIndex++] = byte 113 | 114 | if (chunkIndex == 5) { 115 | buffer.put(decodeChunk(chunk)) 116 | chunk.fill(0) 117 | chunkIndex = 0 118 | } 119 | } 120 | 121 | //If we didn't end on 0, then we need some padding 122 | if (chunkIndex > 0) { 123 | 124 | val numPadded = chunk.size - chunkIndex 125 | chunk.fill('u'.b, chunkIndex, chunk.size) 126 | val paddedDecode = decodeChunk(chunk) 127 | for (i in 0 until paddedDecode.size - numPadded) 128 | buffer.put(paddedDecode[i]) 129 | } 130 | 131 | buffer.flip() 132 | return buffer.array().copyOf(buffer.limit()) 133 | } 134 | 135 | private fun decodeChunk(chunk: ByteArray): ByteArray { 136 | 137 | if (chunk.size != 5) throw IllegalArgumentException("You can only decode chunks of size 5.") 138 | 139 | return (0..4) 140 | .sumOf { (chunk[it] - ASCII_SHIFT) * BASE85_POW[4 - it] } 141 | .byteArray 142 | } 143 | 144 | private val ByteArray.int: Int 145 | get() { 146 | if (size != 4) throw IllegalArgumentException("You cannot create an int without exactly 4 bytes.") 147 | return ByteBuffer.wrap(this).int 148 | } 149 | 150 | private val Int.byteArray get() = byteArrayOf(ushr(24).b, ushr(16).b, ushr(8).b, b) 151 | } -------------------------------------------------------------------------------- /awt/src/test/kotlin/uno/jawt/HelloTinyFD.java: -------------------------------------------------------------------------------- 1 | //package main; 2 | // 3 | //import org.lwjgl.PointerBuffer; 4 | //import org.lwjgl.glfw.GLFWErrorCallback; 5 | //import org.lwjgl.glfw.GLFWVidMode; 6 | //import org.lwjgl.opengl.GL; 7 | //import org.lwjgl.system.MemoryStack; 8 | // 9 | //import java.nio.*; 10 | //import java.util.*; 11 | // 12 | //import static org.lwjgl.glfw.Callbacks.*; 13 | //import static org.lwjgl.glfw.GLFW.*; 14 | //import static org.lwjgl.opengl.GL11C.*; 15 | //import static org.lwjgl.system.MemoryStack.*; 16 | //import static org.lwjgl.system.MemoryUtil.*; 17 | //import static org.lwjgl.util.tinyfd.TinyFileDialogs.*; 18 | // 19 | //public final class HelloTinyFD { 20 | // 21 | // private HelloTinyFD() { 22 | // } 23 | // 24 | // public static void main(String[] args) { 25 | // GLFWErrorCallback.createPrint().set(); 26 | // if (!glfwInit()) { 27 | // throw new IllegalStateException("Unable to initialize GLFW"); 28 | // } 29 | // 30 | // long window = glfwCreateWindow(300, 300, "Hello tiny file dialogs!", NULL, NULL); 31 | // if (window == NULL) { 32 | // throw new RuntimeException("Failed to create the GLFW window"); 33 | // } 34 | // 35 | // glfwSetKeyCallback(window, (windowHnd, key, scancode, action, mods) -> { 36 | // if (action == GLFW_RELEASE) { 37 | // return; 38 | // } 39 | // 40 | // switch (key) { 41 | // case GLFW_KEY_ESCAPE: 42 | // glfwSetWindowShouldClose(windowHnd, true); 43 | // break; 44 | // case GLFW_KEY_B: 45 | // tinyfd_beep(); 46 | // break; 47 | // case GLFW_KEY_N: 48 | // System.out.println("\nOpening notification popup..."); 49 | // System.out.println(tinyfd_notifyPopup("Please read...", "...this message.", "info")); 50 | // break; 51 | // case GLFW_KEY_1: 52 | // System.out.println("\nOpening message dialog..."); 53 | // System.out.println(tinyfd_messageBox("Please read...", "...this message.", "okcancel", "info", true) ? "OK" : "Cancel"); 54 | // break; 55 | // case GLFW_KEY_2: 56 | // System.out.println("\nOpening input box dialog..."); 57 | // System.out.println(tinyfd_inputBox("Input Value", "How old are you?", "30")); 58 | // break; 59 | // case GLFW_KEY_3: 60 | // System.out.println("\nOpening file open dialog..."); 61 | // System.out.println(tinyfd_openFileDialog("Open File(s)", "", null, null, true)); 62 | // break; 63 | // case GLFW_KEY_4: 64 | // try (MemoryStack stack = stackPush()) { 65 | // PointerBuffer aFilterPatterns = stack.mallocPointer(2); 66 | // 67 | // aFilterPatterns.put(stack.UTF8("*.jpg")); 68 | // aFilterPatterns.put(stack.UTF8("*.png")); 69 | // 70 | // aFilterPatterns.flip(); 71 | // 72 | // System.out.println("\nOpening file save dialog..."); 73 | // System.out.println(tinyfd_saveFileDialog("Save Image", "", aFilterPatterns, "Image files (*.jpg, *.png)")); 74 | // } 75 | // break; 76 | // case GLFW_KEY_5: 77 | // System.out.println("\nOpening folder select dialog..."); 78 | // System.out.println(tinyfd_selectFolderDialog("Select Folder", "")); 79 | // break; 80 | // case GLFW_KEY_6: 81 | // System.out.println("\nOpening color chooser dialog..."); 82 | // try (MemoryStack stack = stackPush()) { 83 | // ByteBuffer color = stack.malloc(3); 84 | // String hex = tinyfd_colorChooser("Choose Color", "#FF00FF", null, color); 85 | // System.out.println(hex); 86 | // if (hex != null) { 87 | // System.out.println("\tR: " + (color.get(0) & 0xFF)); 88 | // System.out.println("\tG: " + (color.get(1) & 0xFF)); 89 | // System.out.println("\tB: " + (color.get(2) & 0xFF)); 90 | // } 91 | // } 92 | // break; 93 | // } 94 | // }); 95 | // 96 | // // Center window 97 | // GLFWVidMode vidmode = Objects.requireNonNull(glfwGetVideoMode(glfwGetPrimaryMonitor())); 98 | // glfwSetWindowPos( 99 | // window, 100 | // (vidmode.width() - 300) / 2, 101 | // (vidmode.height() - 300) / 2 102 | // ); 103 | // 104 | // glfwMakeContextCurrent(window); 105 | // GL.createCapabilities(); 106 | // 107 | // glfwSwapInterval(1); 108 | // 109 | // tinyfd_messageBox("tinyfd_query", "", "ok", "info", true); 110 | // System.out.println("tiny file dialogs " + tinyfd_version + " (" + tinyfd_response() + ")"); 111 | // System.out.println(); 112 | // System.out.println(tinyfd_needs); 113 | // System.out.println(); 114 | // System.out.println("Press 1 to launch a message dialog."); 115 | // System.out.println("Press 2 to launch an input box fialog."); 116 | // System.out.println("Press 3 to launch a file open dialog."); 117 | // System.out.println("Press 4 to launch a file save dialog."); 118 | // System.out.println("Press 5 to launch a folder select dialog."); 119 | // System.out.println("Press 6 to launch a color chooser dialog."); 120 | // while (!glfwWindowShouldClose(window)) { 121 | // glfwPollEvents(); 122 | // 123 | // glClear(GL_COLOR_BUFFER_BIT); 124 | // glfwSwapBuffers(window); 125 | // } 126 | // 127 | // GL.setCapabilities(null); 128 | // 129 | // glfwFreeCallbacks(window); 130 | // glfwDestroyWindow(window); 131 | // glfwTerminate(); 132 | // 133 | // Objects.requireNonNull(glfwSetErrorCallback(null)).free(); 134 | // } 135 | // 136 | //} 137 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /awt/src/test/kotlin/uno/jawt/AbstractGears.kt: -------------------------------------------------------------------------------- 1 | package uno.jawt 2 | 3 | 4 | import glm_.d 5 | import glm_.f 6 | import glm_.func.cos 7 | import glm_.func.sin 8 | import glm_.vec2.Vec2i 9 | import org.lwjgl.opengl.GL11.* 10 | import uno.glfw.stak 11 | import kotlin.math.sqrt 12 | 13 | 14 | class AbstractGears { 15 | 16 | private val viewRotZ = 0f 17 | 18 | private var gear1 = 0 19 | private var gear2 = 0 20 | private var gear3 = 0 21 | 22 | private var angle = 0f 23 | 24 | fun init() = stak { 25 | println("GL_VENDOR: ${glGetString(GL_VENDOR)}") 26 | println("GL_RENDERER: ${glGetString(GL_RENDERER)}") 27 | println("GL_VERSION: ${glGetString(GL_VERSION)}") 28 | 29 | // setup ogl 30 | glEnable(GL_CULL_FACE) 31 | glEnable(GL_LIGHTING) 32 | glEnable(GL_LIGHT0) 33 | glEnable(GL_DEPTH_TEST) 34 | 35 | // make the gears 36 | gear1 = glGenLists(1) 37 | glNewList(gear1, GL_COMPILE) 38 | glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, it.floats(0.8f, 0.1f, 0f, 1f)) 39 | gear(1f, 4f, 1f, 20, 0.7f) 40 | glEndList() 41 | 42 | gear2 = glGenLists(1) 43 | glNewList(gear2, GL_COMPILE) 44 | glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, it.floats(0f, 0.8f, 0.2f, 1f)) 45 | gear(0.5f, 2f, 2f, 10, 0.7f) 46 | glEndList() 47 | 48 | gear3 = glGenLists(1) 49 | glNewList(gear3, GL_COMPILE) 50 | glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, it.floats(0.2f, 0.2f, 1f, 1f)) 51 | gear(1.3f, 2f, 0.5f, 10, 0.7f) 52 | glEndList() 53 | 54 | glEnable(GL_NORMALIZE) 55 | 56 | glMatrixMode(GL_MODELVIEW) 57 | glLoadIdentity() 58 | glTranslatef(0f, 0f, -40f) 59 | } 60 | 61 | fun render() { 62 | angle += 2f 63 | 64 | glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT) 65 | 66 | glPushMatrix() 67 | glRotatef(view_rotx, 1f, 0f, 0f) 68 | glRotatef(view_roty, 0f, 1f, 0f) 69 | glRotatef(viewRotZ, 0f, 0f, 1f) 70 | 71 | stak { glLightfv(GL_LIGHT0, GL_POSITION, it.floats(5f, 5f, 10f, 0f)) } 72 | 73 | glPushMatrix() 74 | glTranslatef(-3f, -2f, 0f) 75 | glRotatef(angle, 0f, 0f, 1f) 76 | glCallList(gear1) 77 | glPopMatrix() 78 | 79 | glPushMatrix() 80 | glTranslatef(3.1f, -2f, 0f) 81 | glRotatef(-2f * angle - 9f, 0f, 0f, 1f) 82 | glCallList(gear2) 83 | glPopMatrix() 84 | 85 | glPushMatrix() 86 | glTranslatef(-3.1f, 4.2f, 0f) 87 | glRotatef(-2f * angle - 25f, 0f, 0f, 1f) 88 | glCallList(gear3) 89 | glPopMatrix() 90 | 91 | glPopMatrix() 92 | } 93 | 94 | fun reshape(size: Vec2i) { 95 | 96 | glViewport(0, 0, size.x, size.y) 97 | 98 | val f = size.aspect 99 | 100 | glMatrixMode(GL_PROJECTION) 101 | glLoadIdentity() 102 | glFrustum(-1.0, 1.0, -f.d, f.d, 5.0, 100.0) 103 | glMatrixMode(GL_MODELVIEW) 104 | } 105 | 106 | fun destroy() = glDeleteLists(gear1, 3) 107 | 108 | companion object { 109 | 110 | private val view_rotx = 20f 111 | private val view_roty = 30f 112 | 113 | /** 114 | * Draw a gear wheel. You'll probably want to call this function when 115 | * building a display list since we do a lot of trig here. 116 | * 117 | * @param innerRadius radius of hole at center 118 | * @param outerRadius radius at center of teeth 119 | * @param width width of gear 120 | * @param teeth number of teeth 121 | * @param toothDepth depth of tooth 122 | */ 123 | private fun gear(innerRadius: Float, outerRadius: Float, width: Float, teeth: Int, toothDepth: Float) { 124 | var angle: Float 125 | val da = 2f * Math.PI.f / teeth.f / 4f 126 | 127 | val r1 = outerRadius - toothDepth / 2.0f 128 | val r2 = outerRadius + toothDepth / 2.0f 129 | 130 | glShadeModel(GL_FLAT) 131 | 132 | glNormal3f(0.0f, 0.0f, 1.0f) 133 | 134 | var i = 0 135 | 136 | /* draw front face */ 137 | glBegin(GL_QUAD_STRIP) 138 | while (i <= teeth) { 139 | angle = i.f * 2f * Math.PI.f / teeth 140 | 141 | glVertex3f(innerRadius * angle.cos, innerRadius * angle.sin, width * 0.5f) 142 | glVertex3f(r1 * angle.cos, r1 * angle.sin, width * 0.5f) 143 | if (i < teeth) { 144 | glVertex3f(innerRadius * angle.cos, innerRadius * angle.sin, width * 0.5f) 145 | glVertex3f(r1 * (angle + 3f * da).cos, r1 * (angle + 3f * da).sin, width * 0.5f) 146 | } 147 | i++ 148 | } 149 | glEnd() 150 | 151 | /* draw front sides of teeth */ 152 | glBegin(GL_QUADS) 153 | i = 0 154 | while (i < teeth) { 155 | angle = i.toFloat() * 2.0f * Math.PI.toFloat() / teeth 156 | 157 | glVertex3f(r1 * angle.cos, r1 * angle.sin, width * 0.5f) 158 | glVertex3f(r2 * (angle + da).cos, r2 * (angle + da).sin, width * 0.5f) 159 | glVertex3f(r2 * (angle + 2f * da).cos, r2 * (angle + 2f * da).sin, width * 0.5f) 160 | glVertex3f(r1 * (angle + 3f * da).cos, r1 * (angle + 3f * da).sin, width * 0.5f) 161 | i++ 162 | } 163 | glEnd() 164 | 165 | /* draw back face */ 166 | glBegin(GL_QUAD_STRIP) 167 | i = 0 168 | while (i <= teeth) { 169 | angle = i.f * 2f * Math.PI.f / teeth 170 | 171 | glVertex3f(r1 * angle.cos, r1 * angle.sin, -width * 0.5f) 172 | glVertex3f(innerRadius * angle.cos, innerRadius * angle.sin, -width * 0.5f) 173 | glVertex3f(r1 * (angle + 3 * da).cos, r1 * (angle + 3 * da).sin, -width * 0.5f) 174 | glVertex3f(innerRadius * angle.cos, innerRadius * angle.sin, -width * 0.5f) 175 | i++ 176 | } 177 | glEnd() 178 | 179 | /* draw back sides of teeth */ 180 | glBegin(GL_QUADS) 181 | i = 0 182 | while (i < teeth) { 183 | angle = i.f * 2f * Math.PI.f / teeth 184 | 185 | glVertex3f(r1 * (angle + 3 * da).cos, r1 * (angle + 3 * da).sin, -width * 0.5f) 186 | glVertex3f(r2 * (angle + 2 * da).cos, r2 * (angle + 2 * da).sin, -width * 0.5f) 187 | glVertex3f(r2 * (angle + da).cos, r2 * (angle + da).sin, -width * 0.5f) 188 | glVertex3f(r1 * angle.cos, r1 * angle.sin, -width * 0.5f) 189 | i++ 190 | } 191 | glEnd() 192 | 193 | /* draw outward faces of teeth */ 194 | glBegin(GL_QUAD_STRIP) 195 | i = 0 196 | while (i < teeth) { 197 | angle = i.toFloat() * 2.0f * Math.PI.toFloat() / teeth 198 | 199 | glVertex3f(r1 * angle.cos, r1 * angle.sin, width * 0.5f) 200 | glVertex3f(r1 * angle.cos, r1 * angle.sin, -width * 0.5f) 201 | 202 | var u = r2 * (angle + da).cos - r1 * angle.cos 203 | var v = r2 * (angle + da).sin - r1 * angle.sin 204 | 205 | val len = sqrt(u * u + v * v) 206 | 207 | u /= len 208 | v /= len 209 | 210 | glNormal3f(v, -u, 0f) 211 | glVertex3f(r2 * (angle + da).cos, r2 * (angle + da).sin, width * 0.5f) 212 | glVertex3f(r2 * (angle + da).cos, r2 * (angle + da).sin, -width * 0.5f) 213 | glNormal3f(angle.cos, angle.sin, 0f) 214 | glVertex3f(r2 * (angle + 2 * da).cos, r2 * (angle + 2 * da).sin, width * 0.5f) 215 | glVertex3f(r2 * (angle + 2 * da).cos, r2 * (angle + 2 * da).sin, -width * 0.5f) 216 | 217 | u = r1 * (angle + 3 * da).cos - r2 * (angle + 2 * da).cos 218 | v = r1 * (angle + 3 * da).sin - r2 * (angle + 2 * da).sin 219 | 220 | glNormal3f(v, -u, 0f) 221 | glVertex3f(r1 * (angle + 3 * da).cos, r1 * (angle + 3 * da).sin, width * 0.5f) 222 | glVertex3f(r1 * (angle + 3 * da).cos, r1 * (angle + 3 * da).sin, -width * 0.5f) 223 | glNormal3f(angle.cos, angle.sin, 0f) 224 | i++ 225 | } 226 | glVertex3f(r1 * 0f.cos, r1 * 0f.sin, width * 0.5f) 227 | glVertex3f(r1 * 0f.cos, r1 * 0f.sin, -width * 0.5f) 228 | glEnd() 229 | 230 | glShadeModel(GL_SMOOTH) 231 | 232 | /* draw inside radius cylinder */ 233 | glBegin(GL_QUAD_STRIP) 234 | i = 0 235 | while (i <= teeth) { 236 | angle = i.f * 2f * Math.PI.f / teeth 237 | 238 | glNormal3f(-angle.cos, -angle.sin, 0f) 239 | glVertex3f(innerRadius * angle.cos, innerRadius * angle.sin, -width * 0.5f) 240 | glVertex3f(innerRadius * angle.cos, innerRadius * angle.sin, width * 0.5f) 241 | i++ 242 | } 243 | glEnd() 244 | } 245 | } 246 | 247 | } -------------------------------------------------------------------------------- /gl/src/test/kotlin/examples/gears.kt: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import glm_.f 4 | import glm_.func.cos 5 | import glm_.func.sin 6 | import glm_.glm 7 | import glm_.has 8 | import glm_.vec2.Vec2i 9 | import glm_.vec3.Vec3 10 | import gln.gl 11 | import gln.glClearColor 12 | import kool.floatBufferOf 13 | import org.lwjgl.glfw.GLFW 14 | import org.lwjgl.opengl.GL11 15 | import uno.gl.GlWindow 16 | import uno.glfw.* 17 | import kotlin.math.sqrt 18 | 19 | /* program entry */ 20 | fun main() { 21 | 22 | glfw { 23 | init(installDefaultErrorCB = true) 24 | 25 | hints { 26 | framebuffer.depthBits = 16 27 | window.transparentFramebuffer = true 28 | println(context.version) 29 | } 30 | } 31 | 32 | val glfwWindow = GlfwWindow(300, "Gears") 33 | val window = GlWindow(glfwWindow, forwardCompatible = false) 34 | 35 | // Set callback functions 36 | window.framebufferSizeCB = ::reshape 37 | window.keyCB = ::key 38 | 39 | window.makeCurrent() 40 | // gladLoadGL(glfwGetProcAddress) -> in GlWindow::caps 41 | glfw.swapInterval = VSync.ON 42 | 43 | val size = window.framebufferSize 44 | reshape(window, size) 45 | 46 | // Parse command-line options 47 | init() 48 | 49 | // Main loop 50 | window.loop { 51 | // Draw gears 52 | draw() 53 | 54 | // Update animation 55 | animate() 56 | } 57 | 58 | // Terminate GLFW 59 | glfw.terminate() 60 | } 61 | 62 | var viewRot = Vec3(20f, 30f, 0f) 63 | var gear1 = 0 64 | var gear2 = 0 65 | var gear3 = 0 66 | var angle = 0f 67 | 68 | val pos = floatBufferOf(5f, 5f, 10f, 0f) 69 | val red = floatBufferOf(0.8f, 0.1f, 0f, 1f) 70 | val green = floatBufferOf(0f, 0.8f, 0.2f, 1f) 71 | val blue = floatBufferOf(0.2f, 0.2f, 1f, 1f) 72 | 73 | /* new window size */ 74 | fun reshape(window: GlfwWindow, size: Vec2i) { 75 | val h = size.y.f / size.x 76 | 77 | val znear = 5.0 78 | val zfar = 30.0 79 | val xmax = znear * 0.5 80 | 81 | gl.viewport(size) 82 | GL11.glMatrixMode(GL11.GL_PROJECTION) 83 | GL11.glLoadIdentity() 84 | GL11.glFrustum(-xmax, xmax, -xmax * h, xmax * h, znear, zfar) 85 | GL11.glMatrixMode(GL11.GL_MODELVIEW) 86 | GL11.glLoadIdentity() 87 | GL11.glTranslatef(0f, 0f, -20f) 88 | } 89 | 90 | /* change view angle, exit upon ESC */ 91 | fun key(window: GlfwWindow, k: Key, s: Int, action: InputAction, mods: Int) { 92 | 93 | if (action != InputAction.Press) return 94 | 95 | when (k) { 96 | Key.Z -> 97 | if (mods has GLFW.GLFW_MOD_SHIFT) 98 | viewRot.z -= 5f 99 | else 100 | viewRot.z += 5f 101 | 102 | Key.ESCAPE -> window.shouldClose = true 103 | Key.UP -> viewRot.x += 5f 104 | Key.DOWN -> viewRot.x -= 5f 105 | Key.LEFT -> viewRot.y += 5f 106 | Key.RIGHT -> viewRot.y -= 5f 107 | else -> {} 108 | } 109 | } 110 | 111 | /* program & OpenGL initialization */ 112 | fun init() { 113 | 114 | GL11.glLightfv(GL11.GL_LIGHT0, GL11.GL_POSITION, pos) 115 | GL11.glEnable(GL11.GL_CULL_FACE) 116 | GL11.glEnable(GL11.GL_LIGHTING) 117 | GL11.glEnable(GL11.GL_LIGHT0) 118 | GL11.glEnable(GL11.GL_DEPTH_TEST) 119 | 120 | /* make the gears */ 121 | gear1 = GL11.glGenLists(1) 122 | GL11.glNewList(gear1, GL11.GL_COMPILE) 123 | GL11.glMaterialfv(GL11.GL_FRONT, GL11.GL_AMBIENT_AND_DIFFUSE, red) 124 | gear(1f, 4f, 1f, 20, 0.7f) 125 | GL11.glEndList() 126 | 127 | gear2 = GL11.glGenLists(1) 128 | GL11.glNewList(gear2, GL11.GL_COMPILE) 129 | GL11.glMaterialfv(GL11.GL_FRONT, GL11.GL_AMBIENT_AND_DIFFUSE, green) 130 | gear(0.5f, 2f, 2f, 10, 0.7f) 131 | GL11.glEndList() 132 | 133 | gear3 = GL11.glGenLists(1) 134 | GL11.glNewList(gear3, GL11.GL_COMPILE) 135 | GL11.glMaterialfv(GL11.GL_FRONT, GL11.GL_AMBIENT_AND_DIFFUSE, blue) 136 | gear(1.3f, 2f, 0.5f, 10, 0.7f) 137 | GL11.glEndList() 138 | 139 | GL11.glEnable(GL11.GL_NORMALIZE) 140 | } 141 | 142 | /** 143 | 144 | Draw a gear wheel. You'll probably want to call this function when 145 | building a display list since we do a lot of trig here. 146 | 147 | Input: inner_radius - radius of hole at center 148 | outer_radius - radius at center of teeth 149 | width - width of gear teeth - number of teeth 150 | tooth_depth - depth of tooth 151 | 152 | **/ 153 | fun gear(innerRadius: Float, outerRadius: Float, width: Float, teeth: Int, toothDepth: Float) { 154 | 155 | val r0 = innerRadius 156 | val r1 = outerRadius - toothDepth / 2f 157 | val r2 = outerRadius + toothDepth / 2f 158 | 159 | var da = 2f * glm.πf / teeth / 4f 160 | 161 | GL11.glShadeModel(GL11.GL_FLAT) 162 | 163 | GL11.glNormal3f(0f, 0f, 1f) 164 | 165 | /* draw front face */ 166 | GL11.glBegin(GL11.GL_QUAD_STRIP) 167 | for (i in 0..teeth) { 168 | val angle = i * 2f * glm.πf / teeth 169 | GL11.glVertex3f(r0 * angle.cos, r0 * angle.sin, width * 0.5f) 170 | GL11.glVertex3f(r1 * angle.cos, r1 * angle.sin, width * 0.5f) 171 | if (i < teeth) { 172 | GL11.glVertex3f(r0 * angle.cos, r0 * angle.sin, width * 0.5f) 173 | GL11.glVertex3f(r1 * (angle + 3 * da).cos, r1 * (angle + 3 * da).sin, width * 0.5f) 174 | } 175 | } 176 | GL11.glEnd() 177 | 178 | /* draw front sides of teeth */ 179 | GL11.glBegin(GL11.GL_QUADS) 180 | da = 2f * glm.πf / teeth / 4f 181 | for (i in 0 until teeth) { 182 | val angle = i * 2f * glm.πf / teeth 183 | 184 | GL11.glVertex3f(r1 * angle.cos, r1 * angle.sin, width * 0.5f) 185 | GL11.glVertex3f(r2 * (angle + da).cos, r2 * (angle + da).sin, width * 0.5f) 186 | GL11.glVertex3f(r2 * (angle + 2 * da).cos, r2 * (angle + 2 * da).sin, width * 0.5f) 187 | GL11.glVertex3f(r1 * (angle + 3 * da).cos, r1 * (angle + 3 * da).sin, width * 0.5f) 188 | } 189 | GL11.glEnd() 190 | 191 | GL11.glNormal3f(0f, 0f, -1f) 192 | 193 | /* draw back face */ 194 | GL11.glBegin(GL11.GL_QUAD_STRIP) 195 | for (i in 0..teeth) { 196 | val angle = i * 2f * glm.πf / teeth 197 | GL11.glVertex3f(r1 * angle.cos, r1 * angle.sin, -width * 0.5f) 198 | GL11.glVertex3f(r0 * angle.cos, r0 * angle.sin, -width * 0.5f) 199 | if (i < teeth) { 200 | GL11.glVertex3f(r1 * (angle + 3 * da).cos, r1 * (angle + 3 * da).sin, -width * 0.5f) 201 | GL11.glVertex3f(r0 * (angle).cos, r0 * angle.sin, -width * 0.5f) 202 | } 203 | } 204 | GL11.glEnd() 205 | 206 | /* draw back sides of teeth */ 207 | GL11.glBegin(GL11.GL_QUADS) 208 | da = 2f * glm.πf / teeth / 4f 209 | for (i in 0 until teeth) { 210 | val angle = i * 2f * glm.πf / teeth 211 | 212 | GL11.glVertex3f(r1 * (angle + 3 * da).cos, r1 * (angle + 3 * da).sin, -width * 0.5f) 213 | GL11.glVertex3f(r2 * (angle + 2 * da).cos, r2 * (angle + 2 * da).sin, -width * 0.5f) 214 | GL11.glVertex3f(r2 * (angle + da).cos, r2 * (angle + da).sin, -width * 0.5f) 215 | GL11.glVertex3f(r1 * angle.cos, r1 * angle.sin, -width * 0.5f) 216 | } 217 | GL11.glEnd() 218 | 219 | /* draw outward faces of teeth */ 220 | GL11.glBegin(GL11.GL_QUAD_STRIP) 221 | for (i in 0 until teeth) { 222 | val angle = i * 2f * glm.πf / teeth 223 | 224 | GL11.glVertex3f(r1 * angle.cos, r1 * angle.sin, width * 0.5f) 225 | GL11.glVertex3f(r1 * angle.cos, r1 * angle.sin, -width * 0.5f) 226 | var u = r2 * (angle + da).cos - r1 * angle.cos 227 | var v = r2 * (angle + da).sin - r1 * angle.sin 228 | val len = sqrt(u * u + v * v) 229 | u /= len 230 | v /= len 231 | GL11.glNormal3f(v, -u, 0f) 232 | GL11.glVertex3f(r2 * (angle + da).cos, r2 * (angle + da).sin, width * 0.5f) 233 | GL11.glVertex3f(r2 * (angle + da).cos, r2 * (angle + da).sin, -width * 0.5f) 234 | GL11.glNormal3f(angle.cos, angle.sin, 0f) 235 | GL11.glVertex3f(r2 * (angle + 2 * da).cos, r2 * (angle + 2 * da).sin, width * 0.5f) 236 | GL11.glVertex3f(r2 * (angle + 2 * da).cos, r2 * (angle + 2 * da).sin, -width * 0.5f) 237 | u = r1 * (angle + 3 * da).cos - r2 * (angle + 2 * da).cos 238 | v = r1 * (angle + 3 * da).sin - r2 * (angle + 2 * da).sin 239 | GL11.glNormal3f(v, -u, 0f) 240 | GL11.glVertex3f(r1 * (angle + 3 * da).cos, r1 * (angle + 3 * da).sin, width * 0.5f) 241 | GL11.glVertex3f(r1 * (angle + 3 * da).cos, r1 * (angle + 3 * da).sin, -width * 0.5f) 242 | GL11.glNormal3f(angle.cos, angle.sin, 0f) 243 | } 244 | 245 | GL11.glVertex3f(r1 * 0f.cos, r1 * 0f.sin, width * 0.5f) 246 | GL11.glVertex3f(r1 * 0f.cos, r1 * 0f.sin, -width * 0.5f) 247 | 248 | GL11.glEnd() 249 | 250 | GL11.glShadeModel(GL11.GL_SMOOTH) 251 | 252 | /* draw inside radius cylinder */ 253 | GL11.glBegin(GL11.GL_QUAD_STRIP) 254 | for (i in 0..teeth) { 255 | val angle = i * 2f * glm.πf / teeth 256 | GL11.glNormal3f(-angle.cos, -angle.sin, 0f) 257 | GL11.glVertex3f(r0 * angle.cos, r0 * angle.sin, -width * 0.5f) 258 | GL11.glVertex3f(r0 * angle.cos, r0 * angle.sin, width * 0.5f) 259 | } 260 | GL11.glEnd() 261 | } 262 | 263 | /* OpenGL draw function & timing */ 264 | fun draw() { 265 | 266 | glClearColor(0f) 267 | GL11.glClear(GL11.GL_COLOR_BUFFER_BIT or GL11.GL_DEPTH_BUFFER_BIT) 268 | 269 | GL11.glPushMatrix() 270 | GL11.glRotatef(viewRot.x, 1f, 0f, 0f) 271 | GL11.glRotatef(viewRot.y, 0f, 1f, 0f) 272 | GL11.glRotatef(viewRot.z, 0f, 0f, 1f) 273 | 274 | GL11.glPushMatrix() 275 | GL11.glTranslatef(-3f, -2f, 0f) 276 | GL11.glRotatef(angle, 0f, 0f, 1f) 277 | GL11.glCallList(gear1) 278 | GL11.glPopMatrix() 279 | 280 | GL11.glPushMatrix() 281 | GL11.glTranslatef(3.1f, -2f, 0f) 282 | GL11.glRotatef(-2f * angle - 9f, 0f, 0f, 1f) 283 | GL11.glCallList(gear2) 284 | GL11.glPopMatrix() 285 | 286 | GL11.glPushMatrix() 287 | GL11.glTranslatef(-3.1f, 4.2f, 0f) 288 | GL11.glRotatef(-2f * angle - 25f, 0f, 0f, 1f) 289 | GL11.glCallList(gear3) 290 | GL11.glPopMatrix() 291 | 292 | GL11.glPopMatrix() 293 | } 294 | 295 | /* update animation parameters */ 296 | fun animate() { 297 | angle = 100f * glfw.time.f 298 | } -------------------------------------------------------------------------------- /awt/src/main/kotlin/uno/awt/LwjglCanvas.kt: -------------------------------------------------------------------------------- 1 | package uno.awt 2 | 3 | import glm_.vec2.Vec2i 4 | import org.lwjgl.glfw.GLFWErrorCallback 5 | import org.lwjgl.opengl.GLUtil 6 | import org.lwjgl.system.Callback 7 | import org.lwjgl.system.jawt.JAWTDrawingSurface 8 | import org.lwjgl.system.jawt.JAWTFunctions 9 | import uno.gl.GlWindow 10 | import uno.glfw.GlfwWindow 11 | import uno.glfw.VSync 12 | import uno.glfw.glfw 13 | import uno.kotlin.HWND 14 | import java.awt.Canvas 15 | import java.awt.EventQueue 16 | import java.awt.Graphics 17 | import java.awt.event.ComponentAdapter 18 | import java.awt.event.ComponentEvent 19 | 20 | 21 | /** 22 | * A Canvas component that uses OpenGL for rendering. 23 | * 24 | * Spasi: GLFW saves the AWT window proc somewhere and replaces it with its own on `glfwAttachWin32Window`. 25 | * It restores it back when the GLFW window is destroyed. 26 | * The difference between key and mouse events is that for key events the GLFW window proc returns `DefWindowProcW(...)`, 27 | * but for mouse events it returns 0, so the events do not propagate to AWT 28 | * 29 | * This implementation supports Windows only. 30 | */ 31 | abstract class LwjglCanvas(val glDebug: Boolean = false) : Canvas() { 32 | 33 | val awt = JAWT() 34 | 35 | lateinit var glWindow: GlWindow 36 | 37 | var swapBuffers = true 38 | var fps = true 39 | 40 | var debugProc: Callback? = null 41 | 42 | var awtDebug = false 43 | 44 | val glfwErrorCallback = GLFWErrorCallback.createPrint().set() 45 | 46 | init { 47 | glfw { 48 | init() 49 | hints.context.debug = glDebug 50 | } 51 | } 52 | 53 | private fun initInternal(hwnd: HWND) { 54 | // println("LwjglCanvas.initInternal ${Date().toInstant()}") 55 | 56 | initialized = true 57 | 58 | // glfwWindowHint can be used here to configure the GL context 59 | val glfwWindow = GlfwWindow fromWin32Window hwnd 60 | glWindow = GlWindow(glfwWindow, forwardCompatible = false).apply { 61 | makeCurrent() 62 | } 63 | 64 | if (glDebug) 65 | debugProc = GLUtil.setupDebugMessageCallback() 66 | 67 | glfw.swapInterval = VSync.OFF 68 | 69 | init() 70 | 71 | glWindow.makeCurrent(false) 72 | 73 | // println("/LwjglCanvas.initInternal ${Date().toInstant()}") 74 | } 75 | 76 | var initialized = false 77 | var resized = false 78 | var animated = true 79 | 80 | // lateinit var caps: Caps 81 | 82 | // According to jawt.h > This value may be cached 83 | lateinit var surface: JAWTDrawingSurface 84 | 85 | init { 86 | 87 | if (!awt.get()) 88 | throw IllegalStateException("GetAWT failed") 89 | 90 | // this avoids to calling the super method 91 | this.addComponentListener(object : ComponentAdapter() { 92 | override fun componentResized(e: ComponentEvent?) { 93 | // println("resized") 94 | resized = true 95 | } 96 | // Not working 97 | // override fun componentHidden(e: ComponentEvent?) { 98 | // println("hidden") 99 | // } 100 | // 101 | // override fun componentMoved(e: ComponentEvent?) { 102 | // println("moved") 103 | // } 104 | // 105 | // override fun componentShown(e: ComponentEvent?) { 106 | // println("shown") 107 | // } 108 | }) 109 | 110 | if (EventQueue.isDispatchThread()) 111 | surface = awt.getDrawingSurface(this)!! 112 | else EventQueue.invokeAndWait { 113 | surface = awt.getDrawingSurface(this)!! 114 | } 115 | } 116 | 117 | /** critical, call paint without default g.clearRect, because it causes flickering */ 118 | override fun update(g: Graphics) = paint(g)//.also { println("update") } 119 | 120 | var last = 0L 121 | var time = 0L 122 | var frames = 0 123 | 124 | override fun paint(g: Graphics) { 125 | 126 | if (awtDebug) 127 | println("paint start " + Thread.currentThread().name) 128 | 129 | // Lock the drawing surface 130 | if (surface.lock() == JawtLock.ERROR) 131 | throw IllegalStateException("ds->Lock() failed") 132 | 133 | wrapped { hwnd -> 134 | 135 | if (!initialized) 136 | initInternal(hwnd) 137 | 138 | glWindow.withinContext { 139 | 140 | if (resized) { 141 | // println("LwjglCanvas.reshape ${Date().toInstant()}") 142 | val newSize = Vec2i(width, height) 143 | glWindow.size = newSize 144 | reshape(newSize) 145 | resized = false 146 | // println("/LwjglCanvas.reshape ${Date().toInstant()}") 147 | } 148 | 149 | // println("LwjglCanvas.render ${Date().toInstant()}") 150 | if (awtDebug) 151 | println("paint before render ") 152 | 153 | render() 154 | 155 | // println("/LwjglCanvas.render ${Date().toInstant()}") 156 | if (awtDebug) 157 | println("paint end") 158 | 159 | if (swapBuffers) 160 | glWindow.swapBuffers() 161 | 162 | if (fps) { 163 | val now = System.currentTimeMillis() 164 | time += now - last 165 | last = now 166 | frames++ 167 | if (time > 1000) { 168 | time %= 1000 169 | if (awtDebug) println("fps = $frames") 170 | frames = 0 171 | } 172 | } 173 | } 174 | } 175 | 176 | if (animated) 177 | repaint() 178 | } 179 | 180 | /*override fun paint(g: Graphics) { 181 | if (awtDebug) { 182 | println("paint start " + Thread.currentThread().name) 183 | } 184 | 185 | // Lock the drawing surface 186 | val lock = JAWT_DrawingSurface_Lock(surface, surface.Lock()) 187 | if (lock has JAWT_LOCK_ERROR) 188 | throw IllegalStateException("ds->Lock() failed") 189 | 190 | try { 191 | // Get the drawing surface info 192 | val dsi = JAWT_DrawingSurface_GetDrawingSurfaceInfo(surface, surface.GetDrawingSurfaceInfo()) 193 | ?: throw IllegalStateException("ds->GetDrawingSurfaceInfo() failed") 194 | 195 | try { 196 | // Get the window platform drawing info 197 | val surfaceInfo = JAWTWin32DrawingSurfaceInfo.create(dsi.platformInfo()) 198 | 199 | val hdc = surfaceInfo.hdc() 200 | assert(hdc != NULL) 201 | 202 | if (!initialized) 203 | initInternal(HWND(surfaceInfo.hwnd())) 204 | 205 | glfwWindow.makeContextCurrent() 206 | 207 | if (resized) { 208 | // println("LwjglCanvas.reshape ${Date().toInstant()}") 209 | val newSize = Vec2i(width, height) 210 | glfwWindow.size = newSize 211 | reshape(newSize) 212 | resized = false 213 | // println("/LwjglCanvas.reshape ${Date().toInstant()}") 214 | } 215 | 216 | // println("LwjglCanvas.render ${Date().toInstant()}") 217 | if (awtDebug) { 218 | println("paint before render ") 219 | } 220 | render() 221 | // println("/LwjglCanvas.render ${Date().toInstant()}") 222 | 223 | if (swapBuffers) 224 | glfwWindow.swapBuffers() 225 | 226 | if (fps) { 227 | val now = System.currentTimeMillis() 228 | time += now - last 229 | last = now 230 | frames++ 231 | if (time > 1000) { 232 | time %= 1000 233 | println("fps = $frames") 234 | frames = 0 235 | } 236 | } 237 | 238 | glfwWindow.unmakeContextCurrent() 239 | 240 | } finally { 241 | // Free the drawing surface info 242 | JAWT_DrawingSurface_FreeDrawingSurfaceInfo(dsi, surface.FreeDrawingSurfaceInfo()) 243 | } 244 | } finally { 245 | // Unlock the drawing surface 246 | JAWT_DrawingSurface_Unlock(surface, surface.Unlock()) 247 | } 248 | 249 | if (awtDebug) { 250 | println("paint end") 251 | } 252 | 253 | if (animated) 254 | repaint() 255 | }*/ 256 | 257 | inline fun wrapped(block: (HWND) -> Unit) { 258 | 259 | try { 260 | // Get the drawing surface info 261 | val dsi = surface.info ?: throw IllegalStateException("ds->GetDrawingSurfaceInfo() failed") 262 | 263 | try { 264 | // Get the window platform drawing info 265 | val surfaceInfo = JAWTWin32DrawingSurfaceInfo(dsi) 266 | 267 | val hdc = surfaceInfo.hdc 268 | assert(hdc.isValid) 269 | 270 | block(surfaceInfo.hwnd) 271 | 272 | } finally { 273 | // Free the drawing surface info 274 | surface free dsi 275 | } 276 | } finally { 277 | // Unlock the drawing surface 278 | surface.unlock() 279 | } 280 | } 281 | 282 | fun destroyInternal() { 283 | if (awtDebug) println("destroyInternal") 284 | 285 | // glWindow.inContext { 286 | // destroy() 287 | // debugProc?.free() 288 | // } 289 | 290 | JAWTFunctions.JAWT_FreeDrawingSurface(surface, awt.FreeDrawingSurface()) 291 | awt.free() 292 | 293 | glWindow.destroy() 294 | glfw.terminate() 295 | glfwErrorCallback.free() 296 | } 297 | 298 | fun toggleAnimation() { 299 | if (animated) 300 | animated = false 301 | else { 302 | animated = true 303 | repaint() 304 | } 305 | } 306 | 307 | @JvmOverloads 308 | fun animate(animate: Boolean = true) { 309 | 310 | if (animated != animate) { 311 | 312 | animated = animate 313 | 314 | if (animate) 315 | graphics?.let { g -> 316 | if (EventQueue.isDispatchThread()) { 317 | paint(g) 318 | g.dispose() 319 | } else EventQueue.invokeAndWait { 320 | paint(g) 321 | g.dispose() 322 | } 323 | } 324 | } 325 | } 326 | 327 | // public methods to overwrite in application 328 | 329 | abstract fun init() 330 | abstract fun reshape(size: Vec2i) 331 | abstract fun render() 332 | abstract fun destroy() 333 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/uno/glfw/Hints.kt: -------------------------------------------------------------------------------- 1 | package uno.glfw 2 | 3 | import glm_.i 4 | import org.lwjgl.glfw.GLFW.* 5 | 6 | class Hints { 7 | 8 | // Window related hints 9 | 10 | val window = Window() 11 | 12 | fun window(block: Window.() -> Unit) = window.block() 13 | 14 | class Window { 15 | 16 | var resizable = true 17 | set(value) { 18 | glfwWindowHint(GLFW_RESIZABLE, value.i) 19 | field = value 20 | } 21 | 22 | var visible = true 23 | set(value) { 24 | glfwWindowHint(GLFW_VISIBLE, value.i) 25 | field = value 26 | } 27 | 28 | var decorated = true 29 | set(value) { 30 | glfwWindowHint(GLFW_DECORATED, value.i) 31 | field = value 32 | } 33 | 34 | var focused = true 35 | set(value) { 36 | glfwWindowHint(GLFW_FOCUSED, value.i) 37 | field = value 38 | } 39 | 40 | var autoIconify = true 41 | set(value) { 42 | glfwWindowHint(GLFW_AUTO_ICONIFY, value.i) 43 | field = value 44 | } 45 | 46 | var floating = false 47 | set(value) { 48 | glfwWindowHint(GLFW_FLOATING, value.i) 49 | field = value 50 | } 51 | 52 | var maximized = false 53 | set(value) { 54 | glfwWindowHint(GLFW_MAXIMIZED, value.i) 55 | field = value 56 | } 57 | 58 | var centerCursor = true 59 | set(value) { 60 | glfwWindowHint(GLFW_CENTER_CURSOR, value.i) 61 | field = value 62 | } 63 | 64 | var transparentFramebuffer = false 65 | set(value) { 66 | glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, value.i) 67 | field = value 68 | } 69 | 70 | var focusOnShow = true 71 | set(value) { 72 | glfwWindowHint(GLFW_FOCUS_ON_SHOW, value.i) 73 | field = value 74 | } 75 | 76 | var scaleToMonitor = false 77 | set(value) { 78 | glfwWindowHint(GLFW_SCALE_TO_MONITOR, value.i) 79 | field = value 80 | } 81 | 82 | var mousePassthrough = false 83 | set(value) { 84 | glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, value.i) 85 | field = value 86 | } 87 | } 88 | 89 | // Framebuffer related hints 90 | 91 | val framebuffer = Framebuffer() 92 | 93 | fun framebuffer(block: Framebuffer.() -> Unit) = framebuffer.block() 94 | class Framebuffer { 95 | 96 | var redBits = 8 97 | set(value) { 98 | glfwWindowHint(GLFW_RED_BITS, value) 99 | field = value 100 | } 101 | 102 | var greenBits = 8 103 | set(value) { 104 | glfwWindowHint(GLFW_GREEN_BITS, value) 105 | field = value 106 | } 107 | 108 | var blueBits = 8 109 | set(value) { 110 | glfwWindowHint(GLFW_BLUE_BITS, value) 111 | field = value 112 | } 113 | 114 | var alphaBits = 8 115 | set(value) { 116 | glfwWindowHint(GLFW_ALPHA_BITS, value) 117 | field = value 118 | } 119 | 120 | var depthBits = 24 121 | set(value) { 122 | glfwWindowHint(GLFW_DEPTH_BITS, value) 123 | field = value 124 | } 125 | 126 | var stencilBits = 8 127 | set(value) { 128 | glfwWindowHint(GLFW_STENCIL_BITS, value) 129 | field = value 130 | } 131 | 132 | var accumRedBits = 0 133 | set(value) { 134 | glfwWindowHint(GLFW_ACCUM_RED_BITS, value) 135 | field = value 136 | } 137 | 138 | var accumGreenBits = 0 139 | set(value) { 140 | glfwWindowHint(GLFW_ACCUM_GREEN_BITS, value) 141 | field = value 142 | } 143 | 144 | var accumBlueBits = 0 145 | set(value) { 146 | glfwWindowHint(GLFW_ACCUM_BLUE_BITS, value) 147 | field = value 148 | } 149 | 150 | var accumAlphaBits = 0 151 | set(value) { 152 | glfwWindowHint(GLFW_ACCUM_ALPHA_BITS, value) 153 | field = value 154 | } 155 | 156 | var auxBuffers = 0 157 | set(value) { 158 | glfwWindowHint(GLFW_AUX_BUFFERS, value) 159 | field = value 160 | } 161 | 162 | var stereo = false 163 | set(value) { 164 | glfwWindowHint(GLFW_STEREO, value.i) 165 | field = value 166 | } 167 | 168 | var samples = 0 169 | set(value) { 170 | glfwWindowHint(GLFW_SAMPLES, value) 171 | field = value 172 | } 173 | 174 | var srgb = false 175 | set(value) { 176 | glfwWindowHint(GLFW_SRGB_CAPABLE, value.i) 177 | field = value 178 | } 179 | 180 | var doubleBuffer = true 181 | set(value) { 182 | glfwWindowHint(GLFW_DOUBLEBUFFER, value.i) 183 | field = value 184 | } 185 | } 186 | 187 | // Monitor related hints 188 | 189 | val monitor = Monitor() 190 | 191 | fun monitor(block: Monitor.() -> Unit) = monitor.block() 192 | class Monitor { 193 | var refreshRate = GLFW_DONT_CARE 194 | set(value) { 195 | glfwWindowHint(GLFW_REFRESH_RATE, value) 196 | field = value 197 | } 198 | } 199 | 200 | // Context related hints 201 | val context = Context() 202 | 203 | fun context(block: Context.() -> Unit) = context.block() 204 | 205 | class Context { 206 | 207 | enum class ClientApi { Gl, GlEs, None } 208 | 209 | var clientApi = ClientApi.Gl 210 | set(value) { 211 | glfwWindowHint(GLFW_CLIENT_API, when (value) { 212 | ClientApi.Gl -> GLFW_OPENGL_API 213 | ClientApi.GlEs -> GLFW_OPENGL_ES_API 214 | ClientApi.None -> GLFW_NO_API 215 | }) 216 | field = value 217 | } 218 | 219 | enum class CreationApi { Native, Egl, OsMesa } 220 | 221 | var creationApi = CreationApi.Native 222 | set(value) { 223 | glfwWindowHint(GLFW_CONTEXT_CREATION_API, when (value) { 224 | CreationApi.Native -> GLFW_NATIVE_CONTEXT_API 225 | CreationApi.Egl -> GLFW_EGL_CONTEXT_API 226 | CreationApi.OsMesa -> GLFW_OSMESA_CONTEXT_API 227 | }) 228 | field = value 229 | } 230 | 231 | var version = "1.0" 232 | set(value) { 233 | check(value.length == 3 && value[0].isDigit() && value[1] == '.' && value[2].isDigit()) 234 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, Integer.parseInt(value[0].toString())) 235 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, Integer.parseInt(value[2].toString())) 236 | field = value 237 | } 238 | 239 | var major = 1 240 | set(value) { 241 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, value) 242 | field = value 243 | } 244 | 245 | var minor = 0 246 | set(value) { 247 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, value) 248 | field = value 249 | } 250 | 251 | var forwardComp = false 252 | set(value) { 253 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, value.i) 254 | field = value 255 | } 256 | 257 | var debug = false 258 | set(value) { 259 | glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, value.i) 260 | field = value 261 | } 262 | 263 | enum class Profile { Any, Compat, Core } 264 | 265 | var profile = Profile.Any 266 | set(value) { 267 | glfwWindowHint(GLFW_OPENGL_PROFILE, when (value) { 268 | Profile.Core -> GLFW_OPENGL_CORE_PROFILE 269 | Profile.Compat -> GLFW_OPENGL_COMPAT_PROFILE 270 | Profile.Any -> GLFW_OPENGL_ANY_PROFILE 271 | }) 272 | field = value 273 | } 274 | 275 | enum class Robustness { NoResetNotification, LoseContextOnReset, None } 276 | 277 | var robustness = Robustness.None 278 | set(value) { 279 | glfwWindowHint(GLFW_CONTEXT_ROBUSTNESS, when (value) { 280 | Robustness.None -> GLFW_NO_ROBUSTNESS 281 | Robustness.NoResetNotification -> GLFW_NO_RESET_NOTIFICATION 282 | Robustness.LoseContextOnReset -> GLFW_LOSE_CONTEXT_ON_RESET 283 | }) 284 | field = value 285 | } 286 | 287 | enum class ReleaseBehaviour { Any, Flush, None } 288 | 289 | var releaseBehaviour = ReleaseBehaviour.Any 290 | set(value) { 291 | glfwWindowHint(GLFW_CONTEXT_RELEASE_BEHAVIOR, when (value) { 292 | ReleaseBehaviour.Any -> GLFW_ANY_RELEASE_BEHAVIOR 293 | ReleaseBehaviour.Flush -> GLFW_RELEASE_BEHAVIOR_FLUSH 294 | ReleaseBehaviour.None -> GLFW_RELEASE_BEHAVIOR_NONE 295 | }) 296 | field = value 297 | } 298 | 299 | var noError = false 300 | set(value) { 301 | glfwWindowHint(GLFW_CONTEXT_NO_ERROR, value.i) 302 | field = value 303 | } 304 | } 305 | 306 | // macOS specific window hints 307 | val cocoa = Cocoa() 308 | 309 | fun cocoa(block: Cocoa.() -> Unit) = cocoa.block() 310 | 311 | class Cocoa { 312 | 313 | var retinaFramebuffer = true 314 | set(value) { 315 | glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, value.i) 316 | field = value 317 | } 318 | 319 | var frameName = "" 320 | set(value) { 321 | glfwWindowHintString(GLFW_COCOA_FRAME_NAME, value) 322 | field = value 323 | } 324 | 325 | var graphicsSwitching = false 326 | set(value) { 327 | glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, value.i) 328 | field = value 329 | } 330 | } 331 | 332 | // X11 specific window hints 333 | val x11 = X11() 334 | 335 | fun x11(block: X11.() -> Unit) = x11.block() 336 | 337 | class X11 { 338 | 339 | var className = "" 340 | set(value) { 341 | glfwWindowHintString(GLFW_X11_CLASS_NAME, value) 342 | field = value 343 | } 344 | 345 | var instanceName = "" 346 | set(value) { 347 | glfwWindowHintString(GLFW_X11_INSTANCE_NAME, value) 348 | field = value 349 | } 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /awt/src/test/kotlin/uno/jawt/LWJGLCanvas.java: -------------------------------------------------------------------------------- 1 | package uno.jawt; 2 | 3 | import org.lwjgl.*; 4 | import org.lwjgl.opengl.*; 5 | import org.lwjgl.system.*; 6 | import org.lwjgl.system.jawt.JAWT; 7 | import org.lwjgl.system.jawt.*; 8 | import org.lwjgl.system.linux.*; 9 | import uno.jawt.AbstractGears; 10 | 11 | import java.awt.*; 12 | import java.awt.event.*; 13 | import java.nio.*; 14 | import java.util.*; 15 | 16 | import static org.lwjgl.glfw.GLFW.*; 17 | import static org.lwjgl.glfw.GLFWNativeWin32.*; 18 | import static org.lwjgl.opengl.GL11.*; 19 | import static org.lwjgl.opengl.GLX13.*; 20 | import static org.lwjgl.system.MemoryStack.*; 21 | import static org.lwjgl.system.MemoryUtil.*; 22 | import static org.lwjgl.system.jawt.JAWTFunctions.*; 23 | 24 | /** 25 | * A Canvas component that uses OpenGL for rendering. 26 | * 27 | *

This implementation supports Windows only.

28 | */ 29 | @SuppressWarnings("serial") 30 | public class LWJGLCanvas extends Canvas { 31 | 32 | private final JAWT awt; 33 | 34 | private JAWTDrawingSurface ds; 35 | 36 | private final AbstractGears gears; 37 | 38 | private long context; 39 | 40 | private GLCapabilities caps; 41 | 42 | public LWJGLCanvas() { 43 | awt = JAWT.calloc(); 44 | awt.version(JAWT_VERSION_1_4); 45 | if (!JAWT_GetAWT(awt)) { 46 | throw new IllegalStateException("GetAWT failed"); 47 | } 48 | 49 | gears = new AbstractGears(); 50 | 51 | // AWT event listeners are invoked in the EDT 52 | 53 | addComponentListener(new ComponentAdapter() { 54 | @Override public void componentResized(ComponentEvent e) { 55 | System.out.println(e); 56 | if (context != NULL) { 57 | paint(); 58 | } 59 | } 60 | @Override public void componentMoved(ComponentEvent e) { 61 | System.out.println(e); 62 | } 63 | @Override public void componentShown(ComponentEvent e) { 64 | System.out.println(e); 65 | } 66 | @Override public void componentHidden(ComponentEvent e) { 67 | System.out.println(e); 68 | } 69 | }); 70 | addFocusListener(new FocusListener() { 71 | @Override public void focusGained(FocusEvent e) { 72 | System.out.println(e); 73 | } 74 | @Override public void focusLost(FocusEvent e) { 75 | System.out.println(e); 76 | } 77 | }); 78 | addKeyListener(new KeyAdapter() { 79 | @Override public void keyPressed(KeyEvent e) { 80 | System.out.println(e); 81 | } 82 | @Override public void keyTyped(KeyEvent e) { 83 | System.out.println(e); 84 | } 85 | @Override public void keyReleased(KeyEvent e) { 86 | System.out.println(e); 87 | } 88 | }); 89 | addMouseListener(new MouseAdapter() { 90 | @Override public void mouseClicked(MouseEvent e) { 91 | System.out.println(e); 92 | } 93 | @Override public void mousePressed(MouseEvent e) { 94 | System.out.println(e); 95 | } 96 | @Override public void mouseReleased(MouseEvent e) { 97 | System.out.println(e); 98 | } 99 | @Override public void mouseEntered(MouseEvent e) { 100 | System.out.println(e); 101 | } 102 | @Override public void mouseExited(MouseEvent e) { 103 | System.out.println(e); 104 | } 105 | @Override public void mouseWheelMoved(MouseWheelEvent e) { 106 | System.out.println(e); 107 | } 108 | @Override public void mouseDragged(MouseEvent e) { 109 | System.out.println(e); 110 | } 111 | @Override public void mouseMoved(MouseEvent e) { 112 | System.out.println(e); 113 | } 114 | }); 115 | addMouseMotionListener(new MouseMotionListener() { 116 | @Override public void mouseDragged(MouseEvent e) { 117 | System.out.println(e); 118 | } 119 | @Override public void mouseMoved(MouseEvent e) { 120 | System.out.println(e); 121 | } 122 | }); 123 | addMouseWheelListener(System.out::println); 124 | } 125 | 126 | @Override 127 | public void update(Graphics g) { 128 | paint(g); 129 | } 130 | 131 | @Override 132 | public void paint(Graphics g) { 133 | paint(); 134 | // repaint(); 135 | } 136 | 137 | private void paint() { 138 | if (ds == null) { 139 | // Get the drawing surface 140 | ds = JAWT_GetDrawingSurface(this, awt.GetDrawingSurface()); 141 | if (ds == null) { 142 | throw new IllegalStateException("awt->GetDrawingSurface() failed"); 143 | } 144 | } 145 | 146 | // Lock the drawing surface 147 | int lock = JAWT_DrawingSurface_Lock(ds, ds.Lock()); 148 | if ((lock & JAWT_LOCK_ERROR) != 0) { 149 | throw new IllegalStateException("ds->Lock() failed"); 150 | } 151 | 152 | try { 153 | // Get the drawing surface info 154 | JAWTDrawingSurfaceInfo dsi = JAWT_DrawingSurface_GetDrawingSurfaceInfo(ds, ds.GetDrawingSurfaceInfo()); 155 | if (dsi == null) { 156 | throw new IllegalStateException("ds->GetDrawingSurfaceInfo() failed"); 157 | } 158 | 159 | try { 160 | switch (Platform.get()) { 161 | case LINUX: 162 | // Get the platform-specific drawing info 163 | JAWTX11DrawingSurfaceInfo dsi_x11 = JAWTX11DrawingSurfaceInfo.create(dsi.platformInfo()); 164 | 165 | long drawable = dsi_x11.drawable(); 166 | if (drawable == NULL) { 167 | break; 168 | } 169 | 170 | if (context == NULL) { 171 | createContextGLX(dsi_x11); 172 | gears.init(); 173 | } else { 174 | if (!glXMakeCurrent(dsi_x11.display(), drawable, context)) { 175 | throw new IllegalStateException("glXMakeCurrent() failed"); 176 | } 177 | GL.setCapabilities(caps); 178 | } 179 | 180 | render(getWidth(), getHeight()); 181 | glXSwapBuffers(dsi_x11.display(), drawable); 182 | 183 | glXMakeCurrent(dsi_x11.display(), NULL, NULL); 184 | GL.setCapabilities(null); 185 | break; 186 | case WINDOWS: 187 | // Get the platform-specific drawing info 188 | JAWTWin32DrawingSurfaceInfo dsi_win = JAWTWin32DrawingSurfaceInfo.create(dsi.platformInfo()); 189 | 190 | long hdc = dsi_win.hdc(); 191 | if (hdc == NULL) { 192 | break; 193 | } 194 | 195 | // The render method is invoked in the EDT 196 | if (context == NULL) { 197 | createContextGLFW(dsi_win); 198 | gears.init(); 199 | } else { 200 | glfwMakeContextCurrent(context); 201 | GL.setCapabilities(caps); 202 | } 203 | 204 | try (MemoryStack stack = stackPush()) { 205 | IntBuffer pw = stack.mallocInt(1); 206 | IntBuffer ph = stack.mallocInt(1); 207 | 208 | glfwGetFramebufferSize(context, pw, ph); 209 | 210 | render(pw.get(0), ph.get(0)); 211 | } 212 | glfwSwapBuffers(context); 213 | 214 | glfwMakeContextCurrent(NULL); 215 | GL.setCapabilities(null); 216 | break; 217 | } 218 | } finally { 219 | // Free the drawing surface info 220 | JAWT_DrawingSurface_FreeDrawingSurfaceInfo(dsi, ds.FreeDrawingSurfaceInfo()); 221 | } 222 | } finally { 223 | // Unlock the drawing surface 224 | JAWT_DrawingSurface_Unlock(ds, ds.Unlock()); 225 | } 226 | } 227 | 228 | private void render(int width, int height) { 229 | glViewport(0, 0, width, height); 230 | 231 | float f = height / (float)width; 232 | 233 | glMatrixMode(GL_PROJECTION); 234 | glLoadIdentity(); 235 | glFrustum(-1.0f, 1.0f, -f, f, 5.0f, 100.0f); 236 | glMatrixMode(GL_MODELVIEW); 237 | 238 | gears.render(); 239 | } 240 | 241 | private void createContextGLFW(JAWTWin32DrawingSurfaceInfo dsi_win) { 242 | // glfwWindowHint can be used here to configure the GL context 243 | context = glfwAttachWin32Window(dsi_win.hwnd(), NULL); 244 | if (context == NULL) { 245 | throw new IllegalStateException("Failed to attach win32 window."); 246 | } 247 | 248 | // Any callbacks registered here will work. But care must be taken because 249 | // the callbacks are NOT invoked in the EDT, but in an AWT thread that 250 | // does the event polling. Many GLFW functions that require main thread 251 | // invocation, should only be called in that thread. 252 | 253 | // Because of how input focus is implemented in AWT, it is recommended that AWT 254 | // KeyListeners are always used for keyboard input. 255 | 256 | glfwMakeContextCurrent(context); 257 | caps = GL.createCapabilities(); 258 | } 259 | 260 | // Simplest possible context creation. 261 | private void createContextGLX(JAWTX11DrawingSurfaceInfo dsi_x11) { 262 | long display = dsi_x11.display(); 263 | long drawable = dsi_x11.drawable(); 264 | 265 | PointerBuffer configs = Objects.requireNonNull(glXChooseFBConfig(display, 0, (IntBuffer)null)); 266 | 267 | long config = NULL; 268 | for (int i = 0; i < configs.remaining(); i++) { 269 | XVisualInfo vi = Objects.requireNonNull(glXGetVisualFromFBConfig(display, configs.get(i))); 270 | if (vi.visualid() == dsi_x11.visualID()) { 271 | config = configs.get(i); 272 | break; 273 | } 274 | } 275 | 276 | context = glXCreateNewContext(display, config, GLX_RGBA_TYPE, NULL, true); 277 | if (context == NULL) { 278 | throw new IllegalStateException("glXCreateContext() failed"); 279 | } 280 | 281 | if (!glXMakeCurrent(display, drawable, context)) { 282 | throw new IllegalStateException("glXMakeCurrent() failed"); 283 | } 284 | 285 | caps = GL.createCapabilities(); 286 | } 287 | 288 | public void destroy() { 289 | // Free the drawing surface 290 | JAWT_FreeDrawingSurface(ds, awt.FreeDrawingSurface()); 291 | 292 | awt.free(); 293 | 294 | if (context != NULL) { 295 | glfwDestroyWindow(context); 296 | } 297 | } 298 | 299 | } -------------------------------------------------------------------------------- /core/src/test/resources/main/proggyClean_compressedBase85: -------------------------------------------------------------------------------- 1 | 7])#######hV0qs'/###[),##/l:$#Q6>##5[n42>c-TH`->>#/e>11NNV=Bv(*:.F?uu#(gRU.o0XGH`$vhLG1hxt9?W`#,5LsCp#-i>.r$<$6pD>Lb';9Crc6tgXmKVeU2cD4Eo3R/2*>]b(MC;$jPfY.;h^`IWM9Qo#t'X#(v#Y9w0#1D$CIf;W'#pWUPXOuxXuU(H9M(1=Ke$$'5F%)]0^#0X@U.a$FBjVQTSDgEKnIS7EM9>ZY9w0#L;>>#Mx&4Mvt//L[MkA#W@lK.N'[0#7RL_&#w+F%HtG9M#XL` 3 | N&.,GM4Pg;--VsM.M0rJfLH2eTM`*oJMHRC`NkfimM2J,W-jXS:)r0wK#@Fge$U>`w'N7G#$#fB#$E^$#:9:hk+eOe--6x)F7*E%?76%^GMHePW-Z5l'&GiF#$956:rS?dA#fiK:)Yr+`�j@'DbG&#^$PG.Ll+DNa&VZ>1i%h1S9u5o@YaaW$e+bROPOpxTO7Stwi1::iB1q)C_=dV26 5 | J;2,]7op$]uQr@_V7$q^%lQwtuHY]=DX,n3L#0PHDO4f9>dC@O>HBuKPpP*E,N+b3L#lpR/MrTEH.IAQk.a>D[.e;mc.x]Ip.PH^'/aqUO/$1WxLoW0[iLAw=4h(9.`GCRUxHPeR`5Mjol(dUWxZa(>ST 6 | rPkrJiWx`5U7F#.g*jrohGg`cg:lSTvEY/EV_7H4Q9[Z%cnv;JQYZ5q.l7Zeas:HOIZOB?Ggv:[7MI2k).'2($5FNP&EQ(,)U]W]+fh18.vsai00);D3@4ku5P?DP8aJt+;qUM]=+b'8@;mViBKx0DE[-auGl8:PJ&Dj+M6OC]O^((##]`0i)drT;-7X`=-H3[igUnPG-NZlo.#k@h#=Ork$m>a>$-?Tm$UV(?#P6YY#'/ 7 | ###xe7q.73rI3*pP/$1>s9)W,JrM7SN]'/4C#v$U`0#V.[0>xQsH$fEmPMgY2u7Kh(G%siIfLSoS+MK2eTM$=5,M8p`A.;_R%#u[K#$x4AG8.kK/HSB==-'Ie/QTtG?-.*^N-4B/ZM_3YlQC7(p7q)&](`6_c)$/*JL(L-^(]$wIM`dPtOdGA,U3:w2M-0+WomX2u7lqM2iEumMTcsF?-aT=Z 8 | -97UEnXglEn1K-bnEO`guFt(c%=;Am_Qs@jLooI&NX;]0#j4#F14;gl8-GQpgwhrq8'=l_f-b49'UOqkLu7-##oDY2L(te+Mch&gLYtJ,MEtJfLh'x'M=$CS-ZZ%P]8bZ>#S?YY#%Q&q'3^Fw&?D)UDNrocM3A76//oL?#h7gl85[qW/NDOk%16ij;+:1a'iNIdb-ou8.P*w,v5#EI$TWS>Pot-R*H'-SEpA:g)f+O$%%`kA#G=8RMmG1&O`>to8b 9 | C]T&$,n.LoO>29sp3dt-52U%VM#q7'DHpg+#Z9%H[Ket`e;)f#Km8&+DC$I46>#Kr]]u-[=99tts1.qb#q72g1WJO81q+eN'03'eM>&1XxY-caEnOj%2n8)),?ILR5^.Ibn<-X-Mq7[a82Lq:F&#ce+S9wsCK*x`569E8ew'He]h:sI[2LM$[guka3ZRd6:t%IG:;$%YiJ:Nq=?eAw;/:nnDq0(CYcMpG)qLN4$##&J-XTt,%OVU4)S1+R-#dg0/Nn?Ku1^0f$B*P:Rowwm-`0PKjYDDM'3]d39VZHEl4,.j']Pk-M.h^&:0FACm$maq-&sgw0t7/6(^xtk%LuH88Fj-ekm>GA#_>568x6(OFRl-IZp`&b,_P'$MhLbxfc$mj`,O;&%W2m`Zh:/)Uetw:aJ%]K9h:TcF]u_-Sj9,VK3M. 14 | *'&0D[Ca]J9gp8,kAW]%(?A%R$f<->Zts'^kn=-^@c4%-pY6qI%J%1IGxfLU9CP8cbPlXv);C=b),<2mOvP8up,UVf3839acAWAW-W?#ao/^#%KYo8fRULNd2.>%m]UK:n%r$'sw]J;5pAoO_#2mO3n,'=H5(etHg*`+RLgv>=4U8guD$I%D:W>-r5V*%j*W:Kvej.Lp$'?;++O'>()jLR-^u68PH 15 | m8ZFWe+ej8h:9r6L*0//c&iH&R8pRbA#Kjm%upV1g:a_#Ur7FuA#(tRh#.Y5K+@?3<-8m0$PEn;J:rh6?I6uG<-`wMU'ircp0LaE_OtlMb&1#6T.#FDKu#1Lw%u%+GM+X'e?YLfjM[VO0MbuFp7;>Q&#WIo)0@F%q7c#4XAXN-U&VBpqB>0ie&jhZ[?iLR@@_AvA-iQC(=ksRZRVp7`.=+NpBC%rh&3]R:8XDmE5^V8O(x<-+k?'(^](H.aREZSi,#1:[IXaZFOm<-ui#qUq2$##Ri;u75OK#(RtaW-K-F`S+cF]uN`-KMQ%rP/Xri.LRcB##=YL3BgM/3MD?@f&1'BW-)Ju#bmmWCMkk&#TR`C,5d>g)F;t,4:@_l8G/5h4vUd%&%950:VXD'QdWoY-F$BtUwmfe$YqL'8(PWX(P?^@Po3$##`MSs?DWBZ/S>+4%>fX,VWv/w'KD`LP5IbH;rTV>n3cEK8U#bX]l-/V+^lj3;vlMb&[5YQ8#pekX9JP3XUC72L,,?+Ni&co7ApnO*5NK,((W-i:$,kp'UDAO(G0Sq7MVjJsbIu)' 19 | Z,*[>br5fX^:FPAWr-m2KgLQ_nN6'8uTGT5g)uLv:873UpTLgH+#FgpH'_o1780Ph8KmxQJ8#H72L4@768@Tm&Qh4CB/5OvmA&,Q&QbUoi$a_%3M01H)4x7I^&KQVgtFnV+;[Pc>[m4k//,]1?#`VY[Jr*3&&slRfLiVZJ:]?=K3Sw=[$=uRB?3xk48@aege0jT6' 20 | N#(q%.O=?2S]u*(m<-V8J'(1)G][68hW$5'q[GC&5j`TE?m'esFGNRM)j,ffZ?-qx8;->g4t*:CIP/[Qap7/9'#(1sao7w-.qNUdkJ)tCF&#B^;xGvn2r9FEPFFFcL@.iFNkTve$m%#QvQS8U@)2Z+3K:AKM5isZ88+dKQ)W6>J%CL`.d*(B`-n8D9oK-XV1q['-5k'cAZ69e;D_?$ZPP&s^+7 21 | ])$*$#@QYi9,5P r+$%CE=68>K8r0=dSC%%(@p7.m7jilQ02'0-VWAgTlGW'b)Tq7VT9q^*^$$.:&N@@$&)WHtPm*5_rO0&e%K&#-30j(E4#'Zb.o/(Tpm$>K'f@[PvFl,hfINTNU6u'0pao7%XUp9]5.>%h 22 | `8_=VYbxuel.NTSsJfLacFu3B'lQSu/m6-Oqem8T+oE--$0a/k]uj9EwsG>%veR*hv^BFpQj:K'#SJ,sB-'#](j.Lg92rTw-*n%@/;39rrJF,l#qV%OrtBeC6/,;qB3ebNW[?,Hqj2L.1NP&GjUR=1D8QaS3Up&@*9wP?+lo7b?@%'k4`p0Z$22%K3+iCZj?XJN4Nm&+YF]u@-W$U%VEQ/,,>>#)D#%8cY#YZ?=,`Wdxu/ae&#w6)R89tI#6@s'(6Bf7a&?S=^ZI_kS&ai`&=tE72L_D,;^R)7[$so8lKN%5/$(vdfq7+ebA#u1p]ovUKW&Y%q]'>$1@-[xfn$7ZTp7 24 | mM,G,Ko7a&Gu%G[RMxJs[0MM%wci.LFDK)(%:_i2B5CsR8&9Z&#=mPEnm0f`<&c)QL5uJ#%u%lJj+D-r;BoFDoS97h5g)E#o:&S4weDF,9^Hoe`h*L+_a*NrLW-1pG_&2UdB86e%B/:= 25 | >)N4xeW.*wft-;$'58-ESqr#U`'6AQ]m&6/`Z>#S?YY#Vc;r7U2&326d=w&H####?TZ`*4?&.MK?LP8Vxg>$[QXc%QJv92.(Db*B)gb*BM9dM*hJMAo*c&#b0v=Pjer]$gG&JXDf->'StvU7505l9$AFvgYRI^&<^b68?j#q9QX4SM'RO#&sL1IM.rJfLUAj221]d##DW=m83u5;'bYx,*Sl0hL(W;;$doB&O/TQ:(Z^xBdLjL< 26 | Lni;''X.`$#8+1GD:k$YUWsbn8ogh6rxZ2Z9]%nd+>V#*8U_72Lh+2Q8Cj0i:6hp&$C/:p(HK>T8Y[gHQ4`4)'$Ab(Nof%V'8hL&#SfD07&6D@M.*J:;$-rv29'M]8qMv-tLp,'886iaC=Hb*YJoKJ,(j%K=H`K.v9HggqBIiZu'QvBT.#=)0ukruV&.)3=(^1`o*Pj4<-#MJ+gLq9-##@HuZPN0]u:h7.T..G:;$/Usj(T7`Q8tT72LnYl<-qx8;-HV7Q-&Xdx%1a,hC=0u+HlsV>nuIQL-5_>@kXQtMacfD.m-VAb8;IReM3$wf0''hra*so568'Ip&vRs849'MRYSp%:t:h5qSgwpEr$B>Q,;s(C#$)`svQuF$##-D,##,g68@2[T;.XSdN9Qe)rpt._K-#5wF)sP'##p#C0c%-Gb%hd+<-j'Ai 31 | *x&&HMkT]C'OSl##5RG[JXaHN;d'uA#x._U;.`PU@(Z3dt4r152@:v,'R.Sj'w#0<-;kPI)FfJ&#AYJ&#//)>-k=m=*XnK$>=)72L]0I%>.G690a:$##<,);?;72#?x9+d;^V'9;jY@;)br#q^YQpx:X#Te$Z^'=-=bGhLf:D6&bNwZ9-ZD#n^9HhLMr5G;']d&6'wYmTFmLq9wI>P(9mI[>kC-ekLC/R&CH+s'B 32 | ;K-M6$EB%is00:+A4[7xks.LrNk0&E)wILYF@2L'0Nb$+pv<(2.768/FrY&h$^3i&@+G%JT'<-,v`3;_)I9M^AE]CN?Cl2AZg+%4iTpT3$U4O]GKx'm9)b@p7YsvK3w^YR-CdQ*:Ir<($u&)#(&?L9Rg3H)4fiEp^iI9O8KnTj,]H?D*r7'M;PwZ9K0E^k&-cpI;.p/6_vwoFMV<->#%Xi.LxVnrU(4&8/P+:hLSKj$#U%]49t'I:rgMi'FL@a:0Y-uA[39',(vbma*hU%<-SRF`Tt:542R_VV$p@[p8DV[A,?1839FWdFTi1O*H&#(AL8[ 34 | _P%.M>v^-))qOT*F5Cq0`Ye%+$B6i:7@0IXSsDiWP,##P`%/L-S(qw%sf/@%#B6;/U7K]uZbi^Oc^2n%t<)'mEVE''n`WnJra 35 | $^TKvX5B>;_aSEK',(hwa0:i4G?.Bci.(X[?b*($,=-n<.Q%`(X=?+@Am*Js0&=3bh8K]mL69=Lb,OcZV/);TTm8VI;?%OtJ<(b4mq7M6:u?KRdFl*:xP?Yb.5)%w_I?7uk5JC+FS(m#i'k.'a0i)9<7b'fs'59hq$*5Uhv##pi^8+hIEBF`nvo`;'l0.^S1<-wUK2/Coh58KKhLjM=SO*rfO`+qC`W-On.=AJ56>>i2@2LH6A:&5q`?9I3@@'04&p2/LVa*T-4<-i3;M9UvZd+N7>b*eIwg:CC)c<>nO&#$(>.Z-I&J(Q0Hd5Q%7Co-b`-cP)hI;*_F]u`Rb[.j8_Q/<&>uu+VsH$sM9TA%?)(vmJ80),P7E>)tjD%2L=-t#fK[%`v=Q8WlA2);Sa>gXm8YB`1d@K#n]76-a$U,mF%Ul:#/'xoFM9QX-$.QN'>[%$Z$uF6pA6Ki2O5:8w*vP1<-1`[G,)-m#>0`P&#eb#.3i)rtB61(o'$?X3B2Qft^ae_5tKL9MUe9b*sLEQ9 39 | 5C&`=G?@Mj=wh*'3E>=-<)Gt*Iw)'QG:`@IwOf7&]1i'S01B+Ev/Nac#9S;=;YQpg_6U`*kVY39xK,[/6Aj7:'1Bm-_1EYfa1+o&o4hp7KN_Q(OlIo@S%;jVdn0'1h19w,WQhLI)3S#f$2(eb,jr*b;3Vw]*7NH%$c4Vs 40 | ,eD9>XW8?N]o+(*pgC%/72LV-uW%iewS8W6m2rtCpo'RS1R84=@paTKt)>=%&1[)*vp'u+x,VrwN;&]kuO9JDbg=pO$J*.jVe;u'm0dr9l,<*wMK*Oe=g8lV_KEBFkO'oU]^=[-792#ok,)i]lR8qQ2oA8wcRCZ^7w/Njh;?.stX?Q1>S1q4Bn$)K1<-rGdO'$Wr.Lc.CG 41 | )$/*JL4tNR/,SVO3,aUw'DJN:)Ss;wGn9A32ijw%FL+Z0Fn.U9;reSq)bmI32U==5ALuG&#Vf1398/pVo1*c-(aY168o<`JsSbk-,1N;$>0:OUas(3:8Z972LSfF8eb=c-;>SPw7.6hn3m`9^Xkn(r.qS[0;T%&Qc=+STRxX'q1BNk3&*eu2;&8q$&x>Q#Q7^Tf+6<(d%ZVmj2bDi%.3L2n+4W'$PiDDG)g,r%+?,$@?uou5tSe2aN_AQU*'IAOURQ##V^Fv-XFbGM7Fl(N<3DhLGF%q.1rC$#:T__&Pi68%0xi_&[qFJ(77j_&JWoF.V735&T,[R*:xFR*K5>>#`bW-?4Ne_&6Ne_&6Ne_&n`kr-#GJcM6X;uM6X;uM(.a..^2TkL%oR(#;u.T%fAr%4tJ8 43 | &><1=GHZ_+m9/#H1F^R#SC#*N=BA9(D?v[UiFY>>^8p,KKF.W]L29uLkLlu/+4Tw$)F./^n3+rlo+DB;5sIYGNk+i1t-69Jg--0pao7Sm#K)pdHW&;LuDNH@H>#/X-TI(;P>#,Gc>#0Su>#4`1?#8lC?#xL$#B.`$#F:r$#JF.%#NR@%#R_R%#Vke%#Zww%#_-4^Rh%Sflr-k'MS.o?.5/sWel/wpEM0%3'/1)K^f1-d>G21&v(35>V`39V7A4=onx4A1OY5EI0;6Ibgr6M$HS7Q<)58C5w,;WoA*#[%T*#`1g*#d=#+#hI5+#lUG+#pbY+#tnl+#x$),#&1;,#*=M,#.I`,#2Ur,#6b.-#;w[H#iQ 45 | tA#m^0B#qjBB#uvTB##-hB#'9$C#+E6C#/QHC#3^ZC#7jmC#;v)D#?,)4kMYD4lVu`4m`:&5niUA5@(A5BA1]PBB:xlBCC=2CDLXMCEUtiCf&0g2'tN?PGT4CPGT4CPGT4CPGT4CPGT4CPGT4CPGT4CPGT4CPGT4CPGT4CPGT4CPGT4CPGT4CP-qekC`.9kEg^+F$kwViFJTB&5KTB&5K 47 | TB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5o,^<-28ZI'O?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xp;7q-#lLYI:xvD=# -------------------------------------------------------------------------------- /gl/src/test/kotlin/tests/Cursor.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(ExperimentalUnsignedTypes::class, ExperimentalUnsignedTypes::class) 2 | 3 | package tests 4 | 5 | import glm_.* 6 | import glm_.func.sin 7 | import glm_.vec2.Vec2 8 | import glm_.vec2.Vec2d 9 | import glm_.vec2.Vec2i 10 | import gln.* 11 | import gln.draw.DrawMode 12 | import gln.draw.glDrawArrays 13 | import kool.floatBufferOf 14 | import kool.free 15 | import org.lwjgl.glfw.GLFW 16 | import org.lwjgl.opengl.GL11 17 | import uno.gl.GlWindow 18 | import uno.glfw.* 19 | import java.awt.Color 20 | import kotlin.math.abs 21 | import kotlin.math.floor 22 | import kotlin.math.sqrt 23 | 24 | const val CURSOR_FRAME_COUNT = 60 25 | 26 | val vertexShaderText = """ 27 | #version 110 28 | uniform mat4 MVP; 29 | attribute vec2 vPos; 30 | void main() 31 | { 32 | gl_Position = MVP * vec4(vPos, 0.0, 1.0); 33 | }""".trimIndent() 34 | 35 | val fragmentShaderText = """ 36 | #version 110 37 | void main() 38 | { 39 | gl_FragColor = vec4(1.0); 40 | }""".trimIndent() 41 | 42 | var cursor = Vec2d() 43 | var swapInterval = VSync.ON 44 | var waitEvents = true 45 | var animateCursor = false 46 | var trackCursor = false 47 | val standardCursors = Array(10) { GlfwCursor.NULL } 48 | var trackingCursor: GlfwCursor = GlfwCursor.NULL 49 | 50 | fun star(x: Int, y: Int, t: Float): Float { 51 | 52 | val c = 64 / 2f 53 | 54 | val i = 0.25f * (2f * glm.πf * t).sin + 0.75f 55 | val k = 64 * 0.046875f * i 56 | 57 | val dist = sqrt((x - c) * (x - c) + (y - c) * (y - c)) 58 | 59 | val sAlpha = 1f - dist / c 60 | val xAlpha = if (x.f == c) c else k / abs(x - c) 61 | val yAlpha = if (y.f == c) c else k / abs(y - c) 62 | 63 | return 0f max (1f min (i * sAlpha * 0.2f + sAlpha * xAlpha * yAlpha)) 64 | } 65 | 66 | fun createCursorFrame(t: Float): GlfwCursor { 67 | var i = 0 68 | val buffer = UByteArray(64 * 64 * 4) 69 | val image = GlfwImage(64, buffer) 70 | 71 | for (y in 0 until image.width) 72 | for (x in 0 until image.height) { 73 | buffer[i++] = 255.toUByte() 74 | buffer[i++] = 255.toUByte() 75 | buffer[i++] = 255.toUByte() 76 | buffer[i++] = (255 * star(x, y, t)).toUInt().toUByte() 77 | } 78 | 79 | return GlfwCursor(image, image.width / 2, image.height / 2) 80 | } 81 | 82 | fun createTrackingCursor(): GlfwCursor { 83 | var i = 0 84 | val buffer = UByteArray(32 * 32 * 4) 85 | val image = GlfwImage(32, buffer) 86 | 87 | for (y in 0 until image.width) 88 | for (x in 0 until image.height) 89 | if (x == 7 || y == 7) { 90 | buffer[i++] = 255.toUByte() 91 | buffer[i++] = 0.toUByte() 92 | buffer[i++] = 0.toUByte() 93 | buffer[i++] = 255.toUByte() 94 | } else { 95 | buffer[i++] = 0.toUByte() 96 | buffer[i++] = 0.toUByte() 97 | buffer[i++] = 0.toUByte() 98 | buffer[i++] = 0.toUByte() 99 | } 100 | return GlfwCursor(image, 7) 101 | } 102 | 103 | fun cursorPositionCB(window: GlfwWindow, pos: Vec2d) { 104 | println("%.3f: Cursor position: ${pos.x.f} ${pos.y.f} (%+f %+f)".format(glfw.time, pos.x - cursor.x, pos.y - cursor.y)) 105 | 106 | cursor.x = pos.x 107 | cursor.y = pos.y 108 | } 109 | 110 | var pos = Vec2i() 111 | var size = Vec2i() 112 | fun keyCB(window: GlfwWindow, key: Key, scancode: Int, action: InputAction, mods: Int) { 113 | 114 | if (action != InputAction.Press) 115 | return 116 | 117 | when (key) { 118 | Key.A -> { 119 | animateCursor = !animateCursor 120 | if (!animateCursor) 121 | window.cursor = GlfwCursor.NULL 122 | } 123 | 124 | Key.ESCAPE -> { 125 | val mode = window.cursorMode 126 | if (mode != GlfwWindow.CursorMode.Disabled && mode != GlfwWindow.CursorMode.Captured) 127 | window.shouldClose = true 128 | 129 | /* FALLTHROUGH */ 130 | } 131 | 132 | Key.N -> { 133 | window.cursorMode = GlfwWindow.CursorMode.Normal 134 | cursor = window.cursorPos 135 | println("(( cursor is normal ))") 136 | } 137 | 138 | Key.D -> { 139 | window.cursorMode = GlfwWindow.CursorMode.Disabled 140 | println("(( cursor is disabled ))") 141 | } 142 | 143 | Key.H -> { 144 | window.cursorMode = GlfwWindow.CursorMode.Hidden 145 | println("(( cursor is hidden ))") 146 | } 147 | 148 | Key.C -> { 149 | window.cursorMode = GlfwWindow.CursorMode.Captured 150 | println("(( cursor is captured ))") 151 | } 152 | 153 | Key.R -> { 154 | if (glfw.rawMouseMotionSupported) { 155 | if (window.rawMouseMotion) { 156 | window.rawMouseMotion = false 157 | println("(( raw input is disabled ))") 158 | } else { 159 | window.rawMouseMotion = true 160 | println("(( raw input is enabled ))") 161 | } 162 | } 163 | } 164 | 165 | Key.SPACE -> { 166 | swapInterval = if (swapInterval == VSync.ON) VSync.OFF else VSync.ON 167 | println("(( swap interval: $swapInterval ))") 168 | glfw.swapInterval = swapInterval 169 | } 170 | 171 | Key.W -> { 172 | waitEvents = !waitEvents 173 | println("(( ${if (waitEvents) "wait" else "poll"}ing for events ))") 174 | } 175 | 176 | Key.T -> { 177 | trackCursor = !trackCursor 178 | window.cursor = if (trackCursor) trackingCursor else GlfwCursor.NULL 179 | } 180 | 181 | Key.P -> { 182 | var pos = window.cursorPos 183 | 184 | println("Query before set: %f %f (%+f %+f)".format(pos.x, pos.y, pos.x - cursor.x, pos.y - cursor.y)) 185 | cursor put pos 186 | window.cursorPos = cursor 187 | pos = window.cursorPos 188 | 189 | println("Query after set: %f %f (%+f %+f)".format(pos.x, pos.y, pos.x - cursor.x, pos.y - cursor.y)) 190 | cursor put pos 191 | } 192 | 193 | Key.UP -> { 194 | window.cursorPos = Vec2d() 195 | cursor = window.cursorPos 196 | } 197 | 198 | Key.DOWN -> { 199 | val size = window.size 200 | window.cursorPos = Vec2d(size.x - 1, size.y - 1) 201 | cursor = window.cursorPos 202 | } 203 | 204 | Key.`0` -> window.cursor = GlfwCursor.NULL 205 | 206 | Key.`1`, Key.`2`, Key.`3`, Key.`4`, Key.`5`, Key.`6`, Key.`7`, Key.`8`, Key.`9` -> { 207 | var index = key.i - Key.`1`.i 208 | if (mods has GLFW.GLFW_MOD_SHIFT) 209 | index += 9 210 | 211 | if (index < standardCursors.size) 212 | window.cursor = standardCursors[index] 213 | } 214 | 215 | Key.F11, Key.ENTER -> { 216 | 217 | if (mods != GLFW.GLFW_MOD_ALT) 218 | return 219 | 220 | if (window.monitor.isValid) 221 | window.setMonitor(GlfwMonitor.NULL, pos, size, 0) 222 | else { 223 | val monitor = glfw.primaryMonitor 224 | val mode = monitor.videoMode 225 | pos = window.pos 226 | size = window.size 227 | window.setMonitor(monitor, Vec2i(), mode.size, mode.refreshRate) 228 | } 229 | 230 | cursor = window.cursorPos 231 | } 232 | else -> {} 233 | } 234 | } 235 | 236 | fun main() { 237 | // int i 238 | // GLFWwindow * window 239 | val starCursors = Array(CURSOR_FRAME_COUNT) { GlfwCursor.NULL } 240 | var currentFrame = GlfwCursor.NULL 241 | // GLuint vertex_buffer, vertex_shader, fragment_shader, program 242 | // GLint mvp_location, vpos_location 243 | 244 | glfw.init(installDefaultErrorCB = true) 245 | 246 | trackingCursor = createTrackingCursor() 247 | if (trackingCursor.isNotValid) 248 | glfw.terminate() 249 | 250 | for (i in 0 until CURSOR_FRAME_COUNT) { 251 | starCursors[i] = createCursorFrame(i / CURSOR_FRAME_COUNT.f) 252 | if (starCursors[i].isNotValid) 253 | glfw.terminate() 254 | } 255 | 256 | for (i in standardCursors.indices) 257 | standardCursors[i] = glfw.createStandardCursor(glfw.CursorShape.values()[i]) 258 | 259 | glfw.hints.context { 260 | major = 2 261 | minor = 0 262 | } 263 | 264 | val glfwWindow = GlfwWindow(640, 480, "Cursor Test") 265 | val window = GlWindow(glfwWindow) 266 | 267 | window.makeCurrent() 268 | // gladLoadGL(glfwGetProcAddress) 269 | 270 | val vertexBuffer = gl.genBuffers() 271 | vertexBuffer.bind(BufferTarget.ARRAY) 272 | 273 | val vertexShader = gl.createShader(ShaderType.VERTEX_SHADER).apply { 274 | source(vertexShaderText) 275 | compile() 276 | } 277 | val fragmentShader = gl.createShader(ShaderType.FRAGMENT_SHADER).apply { 278 | source(fragmentShaderText) 279 | compile() 280 | } 281 | val program = gl.createProgram().apply { 282 | attach(vertexShader) 283 | attach(fragmentShader) 284 | link() 285 | } 286 | val mvpLocation = program getUniformLocation "MVP" 287 | val vposLocation = program getAttribLocation "vPos" 288 | 289 | gl.enableVertexAttribArray(vposLocation) 290 | gl.vertexAttribPointer(vposLocation, Vec2.length, VertexAttrType.FLOAT, false, Vec2.size, 0) 291 | program.use() 292 | 293 | cursor = window.cursorPos 294 | println("Cursor position: ${cursor.x} ${cursor.y}") 295 | 296 | window.cursorPosCB = ::cursorPositionCB 297 | window.keyCB = ::keyCB 298 | 299 | while (!window.shouldClose) { 300 | 301 | glClearColor(Color.BLACK) 302 | GL11.glClear(GL11.GL_COLOR_BUFFER_BIT) 303 | 304 | if (trackCursor) { 305 | 306 | val wndSize = window.size 307 | val fbSize = window.framebufferSize 308 | 309 | glViewport(fbSize) 310 | 311 | val scale = fbSize.x.f / wndSize.x 312 | val vertices = floatBufferOf(0.5f, 313 | (fbSize.y - floor(cursor.y * scale) - 1f + 0.5f).f, 314 | fbSize.x +0.5f, 315 | (fbSize.y - floor(cursor.y * scale) - 1f + 0.5f).f, 316 | floor (cursor.x * scale).f + 0.5f, 317 | 0.5f, 318 | floor (cursor.x * scale).f + 0.5f, 319 | fbSize.y +0.5f) 320 | 321 | gl.bufferData(BufferTarget.ARRAY, vertices, Usage.STREAM_DRAW) 322 | vertices.free() 323 | 324 | val mvp = glm.ortho(0f, fbSize.x.f, 0f, fbSize.y.f, 0f, 1f) 325 | gl.uniform(mvpLocation, mvp) 326 | 327 | glDrawArrays(DrawMode.LINES, 4) 328 | } 329 | 330 | window.swapBuffers() 331 | 332 | if (animateCursor) { 333 | val i = (glfw.time * 30).i % CURSOR_FRAME_COUNT 334 | if (currentFrame != starCursors[i]) { 335 | window.cursor = starCursors[i] 336 | currentFrame = starCursors[i] 337 | } 338 | } else 339 | currentFrame = GlfwCursor.NULL 340 | 341 | if (waitEvents) { 342 | if (animateCursor) 343 | glfw.waitEventsTimeout(1.0 / 30.0) 344 | else 345 | glfw.waitEvents() 346 | } else 347 | glfw.pollEvents() 348 | 349 | // Workaround for an issue with msvcrt and mintty 350 | // fflush(stdout) 351 | } 352 | 353 | window.destroy() 354 | 355 | for (cursor in starCursors) 356 | cursor.destroy() 357 | 358 | for (cursor in standardCursors) 359 | cursor.destroy() 360 | 361 | glfw.terminate() 362 | } 363 | --------------------------------------------------------------------------------