diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index f01153a1b7..72419f26ce 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -31,7 +31,6 @@ dependencies { implementation(libs.proguard.gradle) implementation(libs.gson) implementation(libs.gradle.publish.plugin) - implementation(libs.korlibs.serialization) implementation(libs.kotlin.gradle.plugin) implementation(libs.android.build.gradle) testImplementation(libs.junit) diff --git a/buildSrc/src/main/kotlin/korlibs/korge/gradle/KorgeExtension.kt b/buildSrc/src/main/kotlin/korlibs/korge/gradle/KorgeExtension.kt index 86344aef52..ae6d0fa097 100644 --- a/buildSrc/src/main/kotlin/korlibs/korge/gradle/KorgeExtension.kt +++ b/buildSrc/src/main/kotlin/korlibs/korge/gradle/KorgeExtension.kt @@ -2,7 +2,6 @@ package korlibs.korge.gradle import groovy.text.* import korlibs.* -import korlibs.io.serialization.yaml.* import korlibs.korge.gradle.processor.* import korlibs.korge.gradle.targets.* import korlibs.korge.gradle.targets.android.* @@ -178,7 +177,7 @@ open class KorgeExtension( fun loadYaml(file: File) { val korgeYamlString = file.takeIfExists()?.readText() ?: return try { - val info = korlibs.io.serialization.yaml.Yaml.read(korgeYamlString).dyn + val info = korlibs.korge.gradle.util.Yaml.read(korgeYamlString).dyn info["id"].toStringOrNull()?.let { this.id = it } author( diff --git a/buildSrc/src/main/kotlin/korlibs/korge/gradle/targets/js/JavaScriptDeno.kt b/buildSrc/src/main/kotlin/korlibs/korge/gradle/targets/js/JavaScriptDeno.kt new file mode 100644 index 0000000000..4b22d02025 --- /dev/null +++ b/buildSrc/src/main/kotlin/korlibs/korge/gradle/targets/js/JavaScriptDeno.kt @@ -0,0 +1,197 @@ +package korlibs.korge.gradle.targets.js + +import korlibs.korge.gradle.targets.* +import korlibs.korge.gradle.util.* +import org.gradle.api.* +import org.gradle.api.internal.tasks.testing.* +import org.gradle.api.tasks.* +import org.gradle.api.tasks.testing.* +import java.io.* + +fun Project.configureDenoTest() { + afterEvaluate { + if (tasks.findByName("compileTestDevelopmentExecutableKotlinJs") == null) return@afterEvaluate + + val jsDenoTest = project.tasks.createThis("jsDenoTest") { + } + } +} + + +fun Project.configureDenoRun() { + afterEvaluate { + if (tasks.findByName("compileDevelopmentExecutableKotlinJs") == null) return@afterEvaluate + + val baseRunFileNameBase = project.fullPathName().trim(':').replace(':', '-') + val baseRunFileName = "$baseRunFileNameBase.mjs" + val runFile = File(rootProject.rootDir, "build/js/packages/$baseRunFileNameBase/kotlin/$baseRunFileName") + + val runDeno = project.tasks.createThis("runDeno") { + group = GROUP_KORGE_RUN + dependsOn("compileDevelopmentExecutableKotlinJs") + commandLine("deno", "run", "--unstable-ffi", "--unstable-webgpu", "-A", runFile) + workingDir(runFile.parentFile.absolutePath) + } + + val packageDeno = project.tasks.createThis("packageDeno") { + group = GROUP_KORGE_PACKAGE + dependsOn("compileDevelopmentExecutableKotlinJs") + commandLine("deno", "compile", "--unstable-ffi", "--unstable-webgpu", "-A", runFile) + workingDir(runFile.parentFile.absolutePath) + } + } +} + +open class DenoTestTask : AbstractTestTask() { +//open class DenoTestTask : KotlinTest() { + + //var isDryRun by org.jetbrains.kotlin.gradle.utils.property { false } + + init { + this.group = "verification" + this.dependsOn("compileTestDevelopmentExecutableKotlinJs") + } + + //@Option(option = "tests", description = "Specify tests to execute as a filter") + //@Input + //var tests: String = "" + + init { + this.reports { + it.junitXml.outputLocation.set(project.file("build/test-results/jsDenoTest/")) + it.html.outputLocation.set(project.file("build/reports/tests/jsDenoTest/")) + } + binaryResultsDirectory.set(project.file("build/test-results/jsDenoTest/binary")) + //reports.enabledReports["junitXml"]!!.optional + //reports.junitXml.outputLocation.opt + //reports.enabledReports.clear() + //reports.junitXml.outputLocation.set(project.file("build/deno-test-results")) + } + + override fun createTestExecuter(): TestExecuter { + return DenoTestExecuter(this.project, this.filter) + } + //override fun createTestExecuter(): TestExecuter = TODO() + override fun createTestExecutionSpec(): TestExecutionSpec = DenoTestExecutionSpec() + + init { + outputs.upToDateWhen { false } + } + + class DenoTestExecuter(val project: Project, val filter: TestFilter) : TestExecuter { + private fun Project.fullPathName(): String { + //KotlinTest + if (this.parent == null) return this.name + return this.parent!!.fullPathName() + ":" + this.name + } + + override fun execute(testExecutionSpec: DenoTestExecutionSpec, testResultProcessor: TestResultProcessor) { + val baseTestFileNameBase = this.project.fullPathName().trim(':').replace(':', '-') + "-test" + val baseTestFileName = "$baseTestFileNameBase.mjs" + val runFile = File(this.project.rootProject.rootDir, "build/js/packages/$baseTestFileNameBase/kotlin/$baseTestFileName.deno.mjs") + + runFile.parentFile.mkdirs() + runFile.writeText( + //language=js + """ + var describeStack = [] + globalThis.describe = (name, callback) => { describeStack.push(name); try { callback() } finally { describeStack.pop() } } + globalThis.it = (name, callback) => { return Deno.test({ name: describeStack.join(".") + "." + name, fn: callback}) } + globalThis.xit = (name, callback) => { return Deno.test({ name: describeStack.join(".") + "." + name, ignore: true, fn: callback}) } + function exists(path) { try { Deno.statSync(path); return true } catch (e) { return false } } + // Polyfill required for kotlinx-coroutines that detects window + window.postMessage = (message, targetOrigin) => { const ev = new Event('message'); ev.source = window; ev.data = message; window.dispatchEvent(ev); } + const file = './${baseTestFileName}'; + if (exists(file)) await import(file) + """.trimIndent()) + + //testResultProcessor.started() + val process = ProcessBuilder(buildList { + add("deno") + add("test") + add("--unstable-ffi") + add("--unstable-webgpu") + add("-A") + if (filter.includePatterns.isEmpty()) { + add("--filter=${filter.includePatterns.joinToString(",")}") + } + add("--junit-path=${project.file("build/test-results/jsDenoTest/junit.xml").absolutePath}") + add(runFile.absolutePath) + }).directory(runFile.parentFile) + .start() + var id = 0 + val buffered = process.inputStream.bufferedReader() + var capturingOutput = false + var currentTestId: String? = null + var currentTestExtra: String = "ok" + var failedCount = 0 + + fun flush() { + if (currentTestId != null) { + try { + val type = when { + currentTestExtra.contains("skip", ignoreCase = true) || currentTestExtra.contains("ignored", ignoreCase = true) -> TestResult.ResultType.SKIPPED + currentTestExtra.contains("error", ignoreCase = true) || currentTestExtra.contains("failed", ignoreCase = true) -> TestResult.ResultType.FAILURE + currentTestExtra.contains("ok", ignoreCase = true) -> TestResult.ResultType.SUCCESS + else -> TestResult.ResultType.SUCCESS + } + if (type == TestResult.ResultType.FAILURE) { + testResultProcessor.output(currentTestId, DefaultTestOutputEvent(TestOutputEvent.Destination.StdErr, "FAILED\n")) + testResultProcessor.failure(currentTestId, DefaultTestFailure.fromTestFrameworkFailure(Exception("FAILED").also { it.stackTrace = arrayOf() }, null)) + failedCount++ + } + testResultProcessor.completed(currentTestId, TestCompleteEvent(System.currentTimeMillis(), type)) + } catch (e: Throwable) { + //System.err.println("COMPLETED_ERROR: ${e}") + e.printStackTrace() + } + currentTestId = null + } + } + + testResultProcessor.started(DefaultTestSuiteDescriptor("deno", "deno"), TestStartEvent(System.currentTimeMillis())) + + for (line in buffered.lines()) { + println("::: $line") + when { + line == "------- output -------" -> { + capturingOutput = true + } + line == "----- output end -----" -> { + capturingOutput = false + } + capturingOutput -> { + testResultProcessor.output(currentTestId, DefaultTestOutputEvent(TestOutputEvent.Destination.StdOut, "$line\n")) + } + line.contains("...") -> { + //DefaultNestedTestSuiteDescriptor() + flush() + val (name, extra) = line.split("...").map { it.trim() } + //currentTestId = "$name${id++}" + currentTestId = "deno.myid${id++}" + //val demo = CompositeId("Unit", "Name${id++}") + //val descriptor = DefaultTestMethodDescriptor(currentTestId, name.substringBeforeLast('.'), name.substringAfterLast('.')) + + val descriptor = DefaultTestMethodDescriptor(currentTestId, name.substringBeforeLast('.'), name) + currentTestExtra = extra + testResultProcessor.started( + descriptor, + TestStartEvent(System.currentTimeMillis()) + ) + } + } + } + flush() + + testResultProcessor.completed("deno", TestCompleteEvent(System.currentTimeMillis(), if (failedCount == 0) TestResult.ResultType.SUCCESS else TestResult.ResultType.FAILURE)) + + process.waitFor() + System.err.print(process.errorStream.readBytes().decodeToString()) + } + + override fun stopNow() { + } + } + + class DenoTestExecutionSpec : TestExecutionSpec +} diff --git a/buildSrc/src/main/kotlin/korlibs/korge/gradle/targets/js/JavaScriptRun.kt b/buildSrc/src/main/kotlin/korlibs/korge/gradle/targets/js/JavaScriptRun.kt index 914b1a7c19..cdb9871c2d 100644 --- a/buildSrc/src/main/kotlin/korlibs/korge/gradle/targets/js/JavaScriptRun.kt +++ b/buildSrc/src/main/kotlin/korlibs/korge/gradle/targets/js/JavaScriptRun.kt @@ -64,67 +64,6 @@ fun Project.fullPathName(): String { return this.parent!!.fullPathName() + ":" + this.name } -fun Project.configureDenoTest() { - afterEvaluate { - if (tasks.findByName("compileTestDevelopmentExecutableKotlinJs") == null) return@afterEvaluate - - val jsDenoTest = project.tasks.createThis("jsDenoTest") { - group = "verification" - - dependsOn("compileTestDevelopmentExecutableKotlinJs") - - val baseTestFileNameBase = project.fullPathName().trim(':').replace(':', '-') + "-test" - val baseTestFileName = "$baseTestFileNameBase.mjs" - - val runFile = File(rootProject.rootDir, "build/js/packages/$baseTestFileNameBase/kotlin/$baseTestFileName.deno.mjs") - - commandLine("deno", "test", "--unstable-ffi", "--unstable-webgpu", "-A", runFile) - workingDir(runFile.parentFile.absolutePath) - - doFirst { - runFile.parentFile.mkdirs() - runFile.writeText( - //language=js - """ - var describeStack = [] - globalThis.describe = (name, callback) => { describeStack.push(name); try { callback() } finally { describeStack.pop() } } - globalThis.it = (name, callback) => { return Deno.test({ name: describeStack.join(".") + "." + name, fn: callback}) } - globalThis.xit = (name, callback) => { return Deno.test({ name: describeStack.join(".") + "." + name, ignore: true, fn: callback}) } - function exists(path) { try { Deno.statSync(path); return true } catch (e) { return false } } - // Polyfill required for kotlinx-coroutines that detects window - window.postMessage = (message, targetOrigin) => { const ev = new Event('message'); ev.source = window; ev.data = message; window.dispatchEvent(ev); } - const file = './$baseTestFileName'; - if (exists(file)) await import(file) - """.trimIndent()) - } - } - } -} - -fun Project.configureDenoRun() { - afterEvaluate { - if (tasks.findByName("compileDevelopmentExecutableKotlinJs") == null) return@afterEvaluate - - val baseRunFileNameBase = project.fullPathName().trim(':').replace(':', '-') - val baseRunFileName = "$baseRunFileNameBase.mjs" - val runFile = File(rootProject.rootDir, "build/js/packages/$baseRunFileNameBase/kotlin/$baseRunFileName") - - val runDeno = project.tasks.createThis("runDeno") { - group = GROUP_KORGE_RUN - dependsOn("compileDevelopmentExecutableKotlinJs") - commandLine("deno", "run", "--unstable-ffi", "--unstable-webgpu", "-A", runFile) - workingDir(runFile.parentFile.absolutePath) - } - - val packageDeno = project.tasks.createThis("packageDeno") { - group = GROUP_KORGE_PACKAGE - dependsOn("compileDevelopmentExecutableKotlinJs") - commandLine("deno", "compile", "--unstable-ffi", "--unstable-webgpu", "-A", runFile) - workingDir(runFile.parentFile.absolutePath) - } - } -} - fun Project.configureJavascriptRun() { val runJsRelease = project.tasks.createThis(name = "runJsRelease") { group = GROUP_KORGE_RUN diff --git a/buildSrc/src/main/kotlin/korlibs/korge/gradle/util/Yaml.kt b/buildSrc/src/main/kotlin/korlibs/korge/gradle/util/Yaml.kt new file mode 100644 index 0000000000..825f450e06 --- /dev/null +++ b/buildSrc/src/main/kotlin/korlibs/korge/gradle/util/Yaml.kt @@ -0,0 +1,363 @@ +package korlibs.korge.gradle.util + +import kotlin.collections.set + +object Yaml { + fun decode(str: String): Any? = read(ListReader(tokenize(str)), level = 0) + fun read(str: String): Any? = read(ListReader(tokenize(str)), level = 0) + + private fun parseStr(toks: List): Any? { + if (toks.size == 1 && toks[0] is Token.STR) return toks[0].ustr + return parseStr(toks.joinToString("") { it.ustr }) + } + + private fun parseStr(str: String) = when (str) { + "null" -> null + "true" -> true + "false" -> false + else -> str.toIntOrNull() ?: str.toDoubleOrNull() ?: str + } + + //const val TRACE = true + const val TRACE = false + private val EMPTY_SET = setOf() + private val SET_COMMA_END_ARRAY = setOf(",", "]") + + private fun read(s: ListReader, level: Int): Any? = s.run { + var list: ArrayList? = null + var map: MutableMap? = null + var lastMapKey: String? = null + var lastMapValue: Any? = null + + val levelStr = if (TRACE) " ".repeat(level) else "" + + linehandle@ while (s.hasMore) { + val token = s.peek() + val line = token as? Token.LINE + val lineLevel = line?.level + if (TRACE && line != null) println("${levelStr}LINE($lineLevel)") + if (lineLevel != null && lineLevel > level) { + // child level + val res = read(s, lineLevel) + if (list != null) { + if (TRACE) println("${levelStr}CHILD.list.add: $res") + list.add(res) + } else { + if (TRACE) println("${levelStr}CHILD.return: $res") + return res + } + } else if (lineLevel != null && lineLevel < level) { + // parent level + if (TRACE) println("${levelStr}PARENT: level < line.level") + break + } else { + // current level + if (line != null) s.read() + if (s.eof) break + val item = s.peek() + when (item.str) { + "-" -> { + if (s.read().str != "-") invalidOp + if (list == null) { + list = arrayListOf() + if (map != null && lastMapKey != null && lastMapValue == null) { + map[lastMapKey] = list + } + } + if (TRACE) println("${levelStr}LIST_ITEM...") + val res = read(s, level + 1) + if (TRACE) println("${levelStr}LIST_ITEM: $res") + list.add(res) + } + "[" -> { + if (s.read().str != "[") invalidOp + val olist = arrayListOf() + array@ while (s.peek().str != "]") { + olist += readOrString(s, level, SET_COMMA_END_ARRAY, supportNonSpaceSymbols = false) + val p = s.peek().str + when (p) { + "," -> { s.read(); continue@array } + "]" -> break@array + else -> invalidOp("Unexpected '$p'") + } + } + if (s.read().str != "]") invalidOp + return olist + } + else -> { + val keyIds = s.readId() + val sp = s.peekOrNull() ?: Token.EOF + if (s.eof || (sp.str != ":" || (sp is Token.SYMBOL && !sp.isNextWhite))) { + val key = parseStr(keyIds) + if (TRACE) println("${levelStr}LIT: $key") + return key + } else { + val key = parseStr(keyIds).toString() + if (map == null) map = LinkedHashMap() + if (s.read().str != ":") invalidOp + if (TRACE) println("${levelStr}MAP[$key]...") + val next = s.peekOrNull() + val nextStr = next?.str + val hasSpaces = next is Token.SYMBOL && next.isNextWhite + val nextIsSpecialSymbol = nextStr == "[" || nextStr == "{" || (nextStr == "-" && hasSpaces) + val value = readOrString(s, level, EMPTY_SET, supportNonSpaceSymbols = !nextIsSpecialSymbol) + lastMapKey = key + lastMapValue = value + map[key] = value + list = null + if (TRACE) println("${levelStr}MAP[$key]: $value") + } + } + } + } + } + + if (TRACE) println("${levelStr}RETURN: list=$list, map=$map") + + return map ?: list + } + + private fun ListReader.readId(): List { + val tokens = arrayListOf() + while (hasMore) { + val token = peek() + if (token is Token.ID || token is Token.STR || ((token is Token.SYMBOL) && token.str == "-") || ((token is Token.SYMBOL) && token.str == ":" && !token.isNextWhite)) { + tokens.add(token) + read() + } else { + break + } + } + return tokens + } + + private fun readOrString(s: ListReader, level: Int, delimiters: Set, supportNonSpaceSymbols: Boolean): Any? { + val sp = s.peek() + return if (sp is Token.ID || (supportNonSpaceSymbols && sp is Token.SYMBOL && !sp.isNextWhite)) { + var str = "" + str@while (s.hasMore) { + val p = s.peek() + if (p is Token.LINE) break@str + if (p.str in delimiters) break@str + str += s.read().str + } + parseStr(str) + } else { + read(s, level + 1) + } + } + + fun tokenize(str: String): List = StrReader(str.replace("\r\n", "\n")).tokenize() + + private fun StrReader.tokenize(): List { + val out = arrayListOf() + + val s = this + var str = "" + fun flush() { + if (str.isNotBlank() && str.isNotEmpty()) { + out += Token.ID(str.trim()); str = "" + } + } + + val indents = ArrayList() + linestart@ while (hasMore) { + // Line start + flush() + val indentStr = readWhile(kotlin.Char::isWhitespace).replace("\t", " ") + if (indentStr.contains('\n')) continue@linestart // ignore empty lines with possible additional indent + val indent = indentStr.length + if (indents.isEmpty() || indent > indents.last()) { + indents += indent + } else { + while (indents.isNotEmpty() && indent < indents.last()) indents.removeAt(indents.size - 1) + if (indents.isEmpty()) invalidOp + } + val indentLevel = indents.size - 1 + while (out.isNotEmpty() && out.last() is Token.LINE) out.removeAt(out.size - 1) + out += Token.LINE(indentStr, indentLevel) + while (hasMore) { + val c = read() + when (c) { + ':', '-', '[', ']', ',' -> { + flush(); out += Token.SYMBOL("$c", peekChar()) + } + '#' -> { + if (str.lastOrNull()?.isWhitespaceFast() == true || (str == "" && out.lastOrNull() is Token.LINE)) { + flush(); readUntilLineEnd(); skip(); continue@linestart + } else { + str += c + } + } + '\n' -> { + flush(); continue@linestart + } + '"', '\'' -> { + flush() + val last = out.lastOrNull() + //println("out=$last, c='$c', reader=$this") + if (last is Token.SYMBOL && (last.str == ":" || last.str == "[" || last.str == "{" || last.str == "," || last.str == "-")) { + s.unread() + //println(" -> c='$c', reader=$this") + out += Token.STR(s.readStringLit()) + } else { + str += c + } + } + else -> str += c + } + } + } + flush() + return out + } + + interface Token { + val str: String + val ustr get() = str + + object EOF : Token { + override val str: String = "" + } + + data class LINE(override val str: String, val level: Int) : Token { + override fun toString(): String = "LINE($level)" + } + + data class ID(override val str: String) : Token + data class STR(override val str: String) : Token { + override val ustr = str.unquote() + } + + data class SYMBOL(override val str: String, val next: Char) : Token { + val isNextWhite: Boolean get() = next == ' ' || next == '\t' || next == '\n' || next == '\r' + } + } + + private fun StrReader.readUntilLineEnd() = this.readUntil { it == '\n' } + + private val invalidOp: Nothing get() = throw RuntimeException() + private fun invalidOp(msg: String): Nothing = throw RuntimeException(msg) + + private class ListReader(val list: List, val ctx: T? = null) { + class OutOfBoundsException(val list: ListReader<*>, val pos: Int) : RuntimeException() + var position = 0 + val eof: Boolean get() = position >= list.size + val hasMore: Boolean get() = position < list.size + fun peekOrNull(): T? = list.getOrNull(position) + fun peek(): T = list.getOrNull(position) ?: throw OutOfBoundsException(this, position) + fun skip(count: Int = 1) = this.apply { this.position += count } + fun read(): T = peek().apply { skip(1) } + override fun toString(): String = "ListReader($list)" + } + + private class StrReader(val str: String, var pos: Int = 0) { + val length get() = str.length + val hasMore get() = pos < length + + inline fun skipWhile(f: (Char) -> Boolean) { while (hasMore && f(peek())) skip() } + fun skipUntil(f: (Char) -> Boolean): Unit = skipWhile { !f(it) } + + // @TODO: https://youtrack.jetbrains.com/issue/KT-29577 + private fun posSkip(count: Int): Int { + val out = this.pos + this.pos += count + return out + } + + fun skip() = skip(1) + fun peek(): Char = if (hasMore) this.str[this.pos] else '\u0000' + fun peekChar(): Char = peek() + fun read(): Char = if (hasMore) this.str[posSkip(1)] else '\u0000' + fun unread() = skip(-1) + + fun substr(start: Int, len: Int = length - pos): String { + val start = (start).coerceIn(0, length) + val end = (start + len).coerceIn(0, length) + return this.str.substring(start, end) + } + + fun skip(count: Int) = this.apply { this.pos += count } + fun peek(count: Int): String = this.substr(this.pos, count) + fun read(count: Int): String = this.peek(count).also { skip(count) } + + private inline fun readBlock(callback: () -> Unit): String { + val start = pos + callback() + val end = pos + return substr(start, end - start) + } + + fun readWhile(f: (Char) -> Boolean): String = readBlock { skipWhile(f) } + fun readUntil(f: (Char) -> Boolean): String = readBlock { skipUntil(f) } + + fun readStringLit(reportErrors: Boolean = true): String { + val out = StringBuilder() + val quotec = read() + when (quotec) { + '"', '\'' -> Unit + else -> throw RuntimeException("Invalid string literal") + } + var closed = false + while (hasMore) { + val c = read() + if (c == '\\') { + val cc = read() + out.append( + when (cc) { + '\\' -> '\\'; '/' -> '/'; '\'' -> '\''; '"' -> '"' + 'b' -> '\b'; 'f' -> '\u000c'; 'n' -> '\n'; 'r' -> '\r'; 't' -> '\t' + 'u' -> read(4).toInt(0x10).toChar() + else -> throw RuntimeException("Invalid char '$cc'") + } + ) + } else if (c == quotec) { + closed = true + break + } else { + out.append(c) + } + } + if (!closed && reportErrors) { + throw RuntimeException("String literal not closed! '${this.str}'") + } + return out.toString() + } + + override fun toString(): String = "StrReader(str=${str.length}, pos=$pos, range='${str.substring(pos.coerceIn(str.indices), (pos + 10).coerceIn(str.indices)).replace("\n", "\\n")}')" + } + + private fun Char.isWhitespaceFast(): Boolean = this == ' ' || this == '\t' || this == '\r' || this == '\n' + private fun String.isQuoted(): Boolean = this.startsWith('"') && this.endsWith('"') + private fun String.unquote(): String = if (isQuoted()) this.substring(1, this.length - 1).unescape() else this + private fun String.unescape(): String { + val out = StringBuilder(this.length) + var n = 0 + while (n < this.length) { + val c = this[n++] + when (c) { + '\\' -> { + val c2 = this[n++] + when (c2) { + '\\' -> out.append('\\') + '"' -> out.append('\"') + 'n' -> out.append('\n') + 'r' -> out.append('\r') + 't' -> out.append('\t') + 'x', 'u' -> { + val N = if (c2 == 'u') 4 else 2 + val chars = this.substring(n, n + N) + n += N + out.append(chars.toInt(16).toChar()) + } + else -> { + out.append("\\$c2") + } + } + } + else -> out.append(c) + } + } + return out.toString() + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fb8eedc772..c10ed52dc1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,7 +15,7 @@ kotlinx-coroutines = "1.9.0-RC" kotlinx-serialization = "1.7.0" kotlinx-atomicfu = "0.24.0" -korlibs = "6.0.0-alpha7" +korlibs = "6.0.0-alpha9" #korlibs = "999.0.0.999" kotlinx-benchmark = "0.4.7" @@ -29,6 +29,7 @@ gradle-publish-plugin = "1.1.0" # https://repo.maven.apache.org/maven2/org/jetbrains/intellij/deps/intellij-coverage-agent/ [libraries] +korlibs-all = { module = "com.soywiz:korlibs-all", version.ref = "korlibs" } korlibs-audio = { module = "com.soywiz:korlibs-audio", version.ref = "korlibs" } korlibs-image = { module = "com.soywiz:korlibs-image", version.ref = "korlibs" } korlibs-inject = { module = "com.soywiz:korlibs-inject", version.ref = "korlibs" } diff --git a/korge-core/build.gradle.kts b/korge-core/build.gradle.kts index ee7adc1729..53a8160a0c 100644 --- a/korge-core/build.gradle.kts +++ b/korge-core/build.gradle.kts @@ -11,11 +11,12 @@ project.extensions.extraProperties.properties.apply { } dependencies { - commonMainApi(libs.korlibs.audio) - commonMainApi(libs.korlibs.image) - commonMainApi(libs.korlibs.inject) - commonMainApi(libs.korlibs.template) - commonMainApi(libs.korlibs.time) + commonMainApi(libs.korlibs.all) + //commonMainApi(libs.korlibs.audio) + //commonMainApi(libs.korlibs.image) + //commonMainApi(libs.korlibs.inject) + //commonMainApi(libs.korlibs.template) + //commonMainApi(libs.korlibs.time) commonMainApi(libs.kotlinx.atomicfu) commonMainApi(libs.kotlinx.coroutines.core) //commonTestApi(project(":korge-test")) diff --git a/korge-core/src/korlibs/event/Key.kt b/korge-core/src/korlibs/event/Key.kt index 15aa93a632..a1ffb4dace 100644 --- a/korge-core/src/korlibs/event/Key.kt +++ b/korge-core/src/korlibs/event/Key.kt @@ -1,5 +1,7 @@ package korlibs.event +import korlibs.datastructure.* + enum class Key { SPACE, APOSTROPHE, COMMA, MINUS, PLUS, PERIOD, SLASH, N0, N1, N2, N3, N4, N5, N6, N7, N8, N9, N11, N12, @@ -145,3 +147,26 @@ enum class Key { @Deprecated("", ReplaceWith("SUPER", "korlibs.event.Key.SUPER")) val RIGHT_SUPER get() = SUPER } } + +class IntToKeyMap() { + val mapping: IntMap = IntMap() + //val keys = LinkedHashMap() + + operator fun contains(id: Int): Boolean = id in mapping + operator fun get(id: Int): Key? = mapping[id] + operator fun set(id: Int, value: Key) { mapping[id] = value } + + fun map(value: Int, key: Key) { + this.mapping[value] = key + } + fun map(start: Int, keys: ClosedRange) { + val count = keys.endInclusive.ordinal - keys.start.ordinal + 1 + for (n in 0 until count) { + map(start + n, Key.entries[keys.start.ordinal]) + } + } + + companion object { + operator fun invoke(block: IntToKeyMap.() -> Unit): IntToKeyMap = IntToKeyMap().apply(block) + } +} diff --git a/korge-core/src/korlibs/graphics/gl/AGOpengl.kt b/korge-core/src/korlibs/graphics/gl/AGOpengl.kt index bde53ddcd3..cdc2ef052a 100644 --- a/korge-core/src/korlibs/graphics/gl/AGOpengl.kt +++ b/korge-core/src/korlibs/graphics/gl/AGOpengl.kt @@ -19,6 +19,8 @@ val ENABLE_VERTEX_ARRAY_OBJECTS = Environment["DISABLE_VERTEX_ARRAY_OBJECTS"] != //val ENABLE_UNIFORM_BLOCKS = false class AGOpengl(val gl: KmlGl, var context: KmlGlContext? = null) : AG() { + constructor(context: KmlGlContext) : this(context.gl, context) + val contextsToFree = linkedSetOf() class ShaderException(val str: String, val error: String, val errorInt: Int, val gl: KmlGl, val debugName: String?, val type: Int, val shaderReturnInt: Int) : diff --git a/korge-core/src/korlibs/kgl/KmlBufferExt.kt b/korge-core/src/korlibs/kgl/KmlBufferExt.kt index b5018398e8..3e2c6acc39 100644 --- a/korge-core/src/korlibs/kgl/KmlBufferExt.kt +++ b/korge-core/src/korlibs/kgl/KmlBufferExt.kt @@ -5,19 +5,20 @@ import korlibs.memory.* fun Buffer.toAsciiString(): String { var out = "" for (n in 0 until size) { - val b = getInt8(n) + val b = getS8(n) if (b == 0.toByte()) break out += b.toInt().toChar() } + //println("BUFFER.toAsciiString: $out") return out } fun Buffer.putAsciiString(str: String): Buffer { var n = 0 for (c in str) { - if (size >= n) setInt8(n++, c.code) + if (size >= n) set8(n++, c.code.toByte()) } - if (size >= n) setInt8(n++, 0) + if (size >= n) set8(n++, 0) return this } diff --git a/korge-core/src/korlibs/sdl/SDL.kt b/korge-core/src/korlibs/sdl/SDL.kt index a709e6c067..f87ce57faf 100644 --- a/korge-core/src/korlibs/sdl/SDL.kt +++ b/korge-core/src/korlibs/sdl/SDL.kt @@ -2,11 +2,19 @@ package korlibs.sdl import korlibs.annotations.* import korlibs.ffi.* +import korlibs.image.bitmap.* +import korlibs.kgl.* +import korlibs.math.* +import korlibs.memory.* +import korlibs.platform.* val SDLPath by lazy { //"https://github.com/libsdl-org/SDL/releases/download/release-2.30.5/SDL2-2.30.5-win32-x64.zip" //"SDL" - "C:\\temp\\SDL2.dll" + when { + Platform.isMac -> "/Library/Frameworks/SDL2.framework/SDL2" + else -> "SDL2.dll" + } } @KeepNames @@ -23,14 +31,432 @@ open class SDL : FFILib(SDLPath) { // https://wiki.libsdl.org/SDL2/SDL_Event // SDL_Event const val SDL_QUIT = 0x100 + const val SDL_WINDOWEVENT = 0x200 + const val SDL_KEYDOWN = 0x300 + const val SDL_KEYUP = 0x301 + const val SDL_MOUSEMOTION = 0x400 + const val SDL_MOUSEBUTTONDOWN = 0x401 + const val SDL_MOUSEBUTTONUP = 0x402 + const val SDL_MOUSEWHEEL = 0x403 + } val SDL_Init by func<(flags: Int) -> Int>() val SDL_CreateWindow by func<(title: String, x: Int, y: Int, w: Int, h: Int, flags: Int) -> FFIPointer>() val SDL_SetWindowTitle by func<(window: FFIPointer, title: String) -> Unit>() val SDL_SetWindowSize by func<(window: FFIPointer, w: Int, h: Int) -> Unit>() + val SDL_SetWindowPosition by func<(window: FFIPointer, x: Int, y: Int) -> Unit>() val SDL_ShowWindow by func<(window: FFIPointer) -> Unit>() val SDL_RaiseWindow by func<(window: FFIPointer) -> Unit>() val SDL_PollEvent by func<(event: FFIPointer) -> Boolean>() val SDL_Quit by func<() -> Unit>() + + // OpenGL related functions + val SDL_GL_CreateContext by func<(window: FFIPointer) -> FFIPointer>() + val SDL_GL_DeleteContext by func<(context: FFIPointer) -> Unit>() + val SDL_GL_MakeCurrent by func<(window: FFIPointer?, context: FFIPointer?) -> Int>() + val SDL_GL_SwapWindow by func<(window: FFIPointer) -> Unit>() +} + + +private typealias GLboolean = Int // Check https://github.com/korlibs/korge/issues/268#issuecomment-729056184 for details +private typealias GLint = Int +private typealias GLuint = Int +private typealias GLsizei = Int +private typealias GLenum = Int +private typealias GLbitfield = Int +private typealias GLfloat = Float +private typealias GLvoid = Unit +private typealias GLvoidPtr = FFIPointer? +private typealias GLintPtr = FFIPointer? +private typealias GLenumPtr = FFIPointer? +private typealias GLuintPtr = FFIPointer? +private typealias GLfloatPtr = FFIPointer? +private typealias GLbooleanPtr = FFIPointer? +private typealias GLsizeiPtr = FFIPointer? +private typealias GLcharPtr = FFIPointer? +private typealias GLvoidPtrPtr = FFIPointer? +private typealias GLcharPtrPtr = FFIPointer? +private typealias GLString = String? + + + +@KeepNames +open class OpenGL : FFILib("/System/Library/Frameworks/OpenGL.framework/OpenGL") { + companion object { + const val GL_COLOR_BUFFER_BIT = 0x4000 + } + + //val glClearColor by func<(r: Float, g: Float, b: Float, a: Float) -> Unit>() + //val glClear by func<(mask: Int) -> Unit>() + //val glViewport by func<(x: Int, y: Int, width: Int, height: Int) -> Unit>() + //val glFlush by func<() -> Unit>() + //val glFinish by func<() -> Unit>() + + val glDrawArraysInstanced by func<(GLenum, GLint, GLsizei, GLint) -> GLvoid>() + val glDrawElementsInstanced by func<(GLenum, GLsizei, GLenum, GLvoidPtr, GLint) -> GLvoid>() + val glVertexAttribDivisor by func<(GLint, GLint) -> GLvoid>() + + val glTexSubImage2D by func<(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLvoidPtr) -> GLvoid>() + val glTexImage2D by func<(GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, GLvoidPtr) -> GLvoid>() + + val glTexParameteri by func<(GLenum, GLenum, GLint) -> GLvoid>() + val glTexParameterf by func<(GLenum, GLenum, GLfloat) -> GLvoid>() + + val glTexParameterfv by func<(GLenum, GLenum, GLintPtr) -> GLvoid>() + val glTexParameteriv by func<(GLenum, GLenum, GLfloatPtr) -> GLvoid>() + + val glStencilOp by func<(GLenum, GLenum, GLenum) -> GLvoid>() + val glStencilMask by func<(GLuint) -> GLvoid>() + val glReadPixels by func<(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLvoidPtr) -> GLvoid>() + val glStencilFunc by func<(GLenum, GLint, GLuint) -> GLvoid>() + + val glScissor by func<(GLint, GLint, GLsizei, GLsizei) -> GLvoid>() + val glPolygonOffset by func<(GLfloat, GLfloat) -> GLvoid>() + val glPixelStorei by func<(GLenum, GLint) -> GLvoid>() + val glLineWidth by func<(GLfloat) -> GLvoid>() + + val glGetTexParameterfv by func<(GLenum, GLenum, GLfloatPtr) -> GLvoid>() + val glGetTexParameteriv by func<(GLenum, GLenum, GLintPtr) -> GLvoid>() + + val glGetError by func<() -> GLenum>() + val glGetFloatv by func<(GLenum, GLfloatPtr) -> GLvoid>() + val glGetIntegerv by func<(GLenum, GLintPtr) -> GLvoid>() + val glGetBooleanv by func<(GLenum, GLbooleanPtr) -> GLvoid>() + val glGenTextures by func<(GLsizei, GLuintPtr) -> GLvoid>() + val glDepthRange by func<(GLfloat, GLfloat) -> GLvoid>() + val glDepthRangef by func<(GLfloat, GLfloat) -> GLvoid>() + val glClearDepth by func<(GLfloat) -> GLvoid>() + val glClearDepthf by func<(GLfloat) -> GLvoid>() + val glDrawArrays by func<(GLenum, GLint, GLsizei) -> GLvoid>() + val glDrawElements by func<(GLenum, GLsizei, GLenum, GLvoidPtr) -> GLvoid>() + val glFrontFace by func<(GLenum) -> GLvoid>() + val glFinish by func<() -> GLvoid>() + val glFlush by func<() -> GLvoid>() + val glEnable by func<(GLenum) -> GLvoid>() + val glIsEnabled by func<(GLenum) -> GLboolean>() + val glDisable by func<(GLenum) -> GLvoid>() + val glDepthMask by func<(GLboolean) -> GLvoid>() + val glDepthFunc by func<(GLenum) -> GLvoid>() + val glDeleteTextures by func<(GLsizei, GLuintPtr) -> GLvoid>() + val glCullFace by func<(GLenum) -> GLvoid>() + val glCopyTexImage2D by func<(GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLsizei, GLint) -> GLvoid>() + val glCopyTexSubImage2D by func<(GLenum, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei) -> GLvoid>() + val glColorMask by func<(GLboolean, GLboolean, GLboolean, GLboolean) -> GLvoid>() + val glClearStencil by func<(GLint) -> GLvoid>() + val glClearColor by func<(GLfloat, GLfloat, GLfloat, GLfloat) -> GLvoid>() + val glClear by func<(GLbitfield) -> GLvoid>() + val glBindTexture by func<(GLenum, GLuint) -> GLvoid>() + val glHint by func<(GLenum, GLenum) -> GLvoid>() + val glViewport by func<(GLint, GLint, GLsizei, GLsizei) -> GLvoid>() + + val glGetString by func<(GLenum) -> GLString>() + val glGetStringi by func<(GLenum, GLuint) -> GLString>() + val glVertexAttribPointer by func<(GLuint, GLint, GLenum, GLboolean, GLsizei, GLvoidPtr) -> Unit>() + val glVertexAttrib4fv by func<(GLuint, GLfloatPtr) -> Unit>() + val glActiveTexture by func<(GLenum) -> Unit>() + val glAttachShader by func<(GLuint, GLuint) -> Unit>() + val glBindAttribLocation by func<(GLuint, GLuint, GLString) -> Unit>() + val glBindBuffer by func<(GLenum, GLuint) -> GLvoid>() + val glBindFramebuffer by func<(GLenum, GLuint) -> GLvoid>() + val glBindRenderbuffer by func<(GLenum, GLuint) -> GLvoid>() + val glBlendColor by func<(GLfloat, GLfloat, GLfloat, GLfloat) -> GLvoid>() + val glBlendEquation by func<(GLenum) -> GLvoid>() + val glBlendEquationSeparate by func<(GLenum, GLenum) -> GLvoid>() + val glBlendFunc by func<(GLenum, GLenum) -> GLvoid>() + val glBlendFuncSeparate by func<(GLenum, GLenum, GLenum, GLenum) -> GLvoid>() + val glBufferData by func<(GLenum, GLsizeiPtr, GLvoidPtr, GLenum) -> GLvoid>() + val glBufferSubData by func<(GLenum, GLsizeiPtr, GLsizeiPtr, GLvoidPtr) -> GLvoid>() + val glCheckFramebufferStatus by func<(GLenum) -> GLenum>() + val glCompileShader by func<(GLuint) -> GLvoid>() + val glCompressedTexImage2D by func<(GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, GLvoidPtr) -> GLvoid>() + val glCompressedTexSubImage2D by func<(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLsizei, GLvoidPtr) -> GLvoid>() + val glCreateProgram by func<() -> GLuint>() + val glCreateShader by func<(GLenum) -> GLuint>() + val glDeleteBuffers by func<(GLsizei, GLuintPtr) -> GLvoid>() + val glDeleteFramebuffers by func<(GLsizei, GLuintPtr) -> GLvoid>() + val glDeleteProgram by func<(GLuint) -> GLvoid>() + val glDeleteRenderbuffers by func<(GLsizei, GLuintPtr) -> GLvoid>() + val glDeleteShader by func<(GLuint) -> GLvoid>() + val glDetachShader by func<(GLuint, GLuint) -> GLvoid>() + val glDisableVertexAttribArray by func<(GLuint) -> GLvoid>() + val glEnableVertexAttribArray by func<(GLuint) -> GLvoid>() + val glFramebufferRenderbuffer by func<(GLenum, GLenum, GLenum, GLuint) -> GLvoid>() + val glFramebufferTexture2D by func<(GLenum, GLenum , GLenum, GLuint, GLuint) -> GLvoid>() + val glGenBuffers by func<(GLsizei, GLuintPtr) -> GLvoid>() + val glGenerateMipmap by func<(GLenum) -> GLvoid>() + val glGenFramebuffers by func<(GLsizei, GLuintPtr) -> GLvoid>() + val glGenRenderbuffers by func<(GLsizei, GLuintPtr) -> GLvoid>() + val glGetActiveAttrib by func<(GLuint, GLuint, GLsizei, GLsizeiPtr, GLintPtr, GLenumPtr, GLcharPtr) -> GLvoid>() + val glGetActiveUniform by func<(GLuint, GLuint, GLsizei, GLsizeiPtr, GLintPtr, GLenumPtr, GLcharPtr) -> GLvoid>() + val glGetAttachedShaders by func<(GLuint, GLsizei, GLsizeiPtr, GLuintPtr) -> GLvoid>() + val glGetAttribLocation by func<(GLuint, GLString) -> GLint>() + val glGetUniformLocation by func<(GLuint, GLString) -> GLint>() + val glGetBufferParameteriv by func<(GLenum, GLenum, GLintPtr) -> GLvoid>() + val glGetFramebufferAttachmentParameteriv by func<(GLenum, GLenum, GLenum, GLintPtr) -> GLvoid>() + //val glGetProgramInfoLog by func<(GLuint, GLsizei, GLsizeiPtr, GLString) -> GLvoid>() + val glGetProgramInfoLog by func<(GLuint, GLsizei, GLsizeiPtr, GLcharPtr) -> GLvoid>() + val glGetRenderbufferParameteriv by func<(GLenum, GLenum, GLintPtr) -> GLvoid>() + val glGetProgramiv by func<(GLuint, GLenum, GLintPtr) -> GLvoid>() + val glGetShaderiv by func<(GLuint, GLenum, GLintPtr) -> GLvoid>() + val glGetShaderInfoLog by func<(GLuint, GLsizei, GLsizeiPtr, GLcharPtr) -> GLvoid>() + val glGetShaderPrecisionFormat by func<(GLenum, GLenumPtr, GLintPtr) -> GLvoid>() + val glGetShaderSource by func<(GLuint, GLsizei, GLsizeiPtr, GLcharPtr) -> GLvoid>() + val glGetUniformfv by func<(GLuint, GLint, GLfloatPtr) -> GLvoid>() + val glGetUniformiv by func<(GLuint, GLint, GLintPtr) -> GLvoid>() + val glGetVertexAttribfv by func<(GLuint, GLenum, GLfloatPtr) -> GLvoid>() + val glGetVertexAttribiv by func<(GLuint, GLenum, GLintPtr) -> GLvoid>() + val glGetVertexAttribPointerv by func<(GLuint, GLenum, GLvoidPtrPtr) -> GLvoid>() + val glIsBuffer by func<(GLuint) -> GLboolean>() + val glIsFramebuffer by func<(GLuint) -> GLboolean>() + val glIsProgram by func<(GLuint) -> GLboolean>() + val glIsRenderbuffer by func<(GLuint) -> GLboolean>() + val glIsTexture by func<(GLuint) -> GLboolean>() + val glIsShader by func<(GLuint) -> GLboolean>() + val glLinkProgram by func<(GLuint) -> GLvoid>() + val glReleaseShaderCompiler by func<() -> GLvoid>() + val glRenderbufferStorage by func<(GLenum, GLenum, GLsizei, GLsizei) -> GLvoid>() + val glSampleCoverage by func<(GLfloat, GLboolean) -> GLvoid>() + val glShaderBinary by func<(GLsizei, GLuintPtr, GLenum, GLvoidPtr, GLsizei) -> GLvoid>() + val glShaderSource by func<(GLuint, GLsizei, GLcharPtrPtr, GLintPtr) -> GLvoid>() + val glStencilFuncSeparate by func<(GLenum, GLenum, GLint, GLuint) -> GLvoid>() + val glStencilMaskSeparate by func<(GLenum, GLuint) -> GLvoid>() + val glStencilOpSeparate by func<(GLenum, GLenum, GLenum, GLenum) -> GLvoid>() + val glUniform1f by func<(GLint, GLfloat) -> GLvoid>() + val glUniform2f by func<(GLint, GLfloat, GLfloat) -> GLvoid>() + val glUniform3f by func<(GLint, GLfloat, GLfloat, GLfloat) -> GLvoid>() + val glUniform4f by func<(GLint, GLfloat, GLfloat, GLfloat, GLfloat) -> GLvoid>() + val glUniform1i by func<(GLint, GLint) -> GLvoid>() + val glUniform2i by func<(GLint, GLint, GLint) -> GLvoid>() + val glUniform3i by func<(GLint, GLint, GLint, GLint) -> GLvoid>() + val glUniform4i by func<(GLint, GLint, GLint, GLint, GLint) -> GLvoid>() + val glUniform1fv by func<(GLint, GLsizei, GLfloatPtr) -> GLvoid>() + val glUniform2fv by func<(GLint, GLsizei, GLfloatPtr) -> GLvoid>() + val glUniform3fv by func<(GLint, GLsizei, GLfloatPtr) -> GLvoid>() + val glUniform4fv by func<(GLint, GLsizei, GLfloatPtr) -> GLvoid>() + val glUniform1iv by func<(GLint, GLsizei, GLintPtr) -> GLvoid>() + val glUniform2iv by func<(GLint, GLsizei, GLintPtr) -> GLvoid>() + val glUniform3iv by func<(GLint, GLsizei, GLintPtr) -> GLvoid>() + val glUniform4iv by func<(GLint, GLsizei, GLintPtr) -> GLvoid>() + val glUniformMatrix2fv by func<(GLint, GLsizei, GLboolean, GLfloatPtr) -> GLvoid>() + val glUniformMatrix3fv by func<(GLint, GLsizei, GLboolean, GLfloatPtr) -> GLvoid>() + val glUniformMatrix4fv by func<(GLint, GLsizei, GLboolean, GLfloatPtr) -> GLvoid>() + val glUseProgram by func<(GLuint) -> GLvoid>() + val glValidateProgram by func<(GLuint) -> GLvoid>() + val glVertexAttrib1f by func<(GLuint, GLfloat) -> GLvoid>() + val glVertexAttrib2f by func<(GLuint, GLfloat, GLfloat) -> GLvoid>() + val glVertexAttrib3f by func<(GLuint, GLfloat, GLfloat, GLfloat) -> GLvoid>() + val glVertexAttrib4f by func<(GLuint, GLfloat, GLfloat, GLfloat, GLfloat) -> GLvoid>() + val glVertexAttrib1fv by func<(GLuint, GLfloatPtr) -> GLvoid>() + val glVertexAttrib2fv by func<(GLuint, GLfloatPtr) -> GLvoid>() + val glVertexAttrib3fv by func<(GLuint, GLfloatPtr) -> GLvoid>() + + val glBindBufferRange by func<(GLenum, GLuint, GLuint, GLintPtr, GLsizeiPtr) -> GLvoid>() + val glGetUniformBlockIndex by func<(GLuint, GLString) -> GLint>() + val glUniformBlockBinding by func<(GLuint, GLuint, GLuint) -> GLvoid>() + val glGenVertexArrays by func<(GLsizei, GLuintPtr) -> GLvoid>() + val glDeleteVertexArrays by func<(GLsizei, GLuintPtr) -> GLvoid>() + val glBindVertexArray by func<(GLuint) -> GLvoid>() +} + +class KmlGlOpenGL(val gl: OpenGL = OpenGL()) : KmlGl() { + override val gles: Boolean get() = true + + fun Int.toBool(): Boolean = this != 0 + + fun Int.convert(): FFIPointer? = CreateFFIPointer(this.toLong()) + fun Long.convert(): FFIPointer? = CreateFFIPointer(this) + + override fun activeTexture(texture: Int): Unit = gl.glActiveTexture(texture) + override fun attachShader(program: Int, shader: Int): Unit = gl.glAttachShader(program, shader) + override fun bindAttribLocation(program: Int, index: Int, name: String): Unit = gl.glBindAttribLocation(program, index, name) + override fun bindBuffer(target: Int, buffer: Int): Unit = gl.glBindBuffer(target, buffer) + override fun bindFramebuffer(target: Int, framebuffer: Int): Unit = gl.glBindFramebuffer(target, framebuffer) + override fun bindRenderbuffer(target: Int, renderbuffer: Int): Unit = gl.glBindRenderbuffer(target, renderbuffer) + override fun bindTexture(target: Int, texture: Int): Unit = gl.glBindTexture(target, texture) + override fun blendColor(red: Float, green: Float, blue: Float, alpha: Float): Unit = gl.glBlendColor(red, green, blue, alpha) + override fun blendEquation(mode: Int): Unit = gl.glBlendEquation(mode) + override fun blendEquationSeparate(modeRGB: Int, modeAlpha: Int): Unit = gl.glBlendEquationSeparate(modeRGB, modeAlpha) + override fun blendFunc(sfactor: Int, dfactor: Int): Unit = gl.glBlendFunc(sfactor, dfactor) + override fun blendFuncSeparate(sfactorRGB: Int, dfactorRGB: Int, sfactorAlpha: Int, dfactorAlpha: Int): Unit = gl.glBlendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha) + override fun bufferData(target: Int, size: Int, data: Buffer, usage: Int): Unit = gl.glBufferData(target, size.convert(), data.pointer, usage) + override fun bufferSubData(target: Int, offset: Int, size: Int, data: Buffer): Unit = gl.glBufferSubData(target, offset.convert(), size.convert(), data.pointer) + override fun checkFramebufferStatus(target: Int): Int = gl.glCheckFramebufferStatus(target) + override fun clear(mask: Int): Unit = gl.glClear(mask) + override fun clearColor(red: Float, green: Float, blue: Float, alpha: Float): Unit = gl.glClearColor(red, green, blue, alpha) + override fun clearDepthf(d: Float): Unit = gl.glClearDepthf(d) + override fun clearStencil(s: Int): Unit = gl.glClearStencil(s) + override fun colorMask(red: Boolean, green: Boolean, blue: Boolean, alpha: Boolean): Unit = gl.glColorMask(red.toInt(), green.toInt(), blue.toInt(), alpha.toInt()) + override fun compileShader(shader: Int): Unit = gl.glCompileShader(shader) + override fun compressedTexImage2D(target: Int, level: Int, internalformat: Int, width: Int, height: Int, border: Int, imageSize: Int, data: Buffer): Unit = + gl.glCompressedTexImage2D(target, level, internalformat, width, height, border, imageSize, data.pointer) + override fun compressedTexSubImage2D(target: Int, level: Int, xoffset: Int, yoffset: Int, width: Int, height: Int, format: Int, imageSize: Int, data: Buffer): Unit { + gl.glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data.pointer) + } + override fun copyTexImage2D(target: Int, level: Int, internalformat: Int, x: Int, y: Int, width: Int, height: Int, border: Int): Unit = gl.glCopyTexImage2D(target, level, internalformat, x, y, width, height, border) + override fun copyTexSubImage2D(target: Int, level: Int, xoffset: Int, yoffset: Int, x: Int, y: Int, width: Int, height: Int): Unit = gl.glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height) + override fun createProgram(): Int = gl.glCreateProgram() + override fun createShader(type: Int): Int = gl.glCreateShader(type) + override fun cullFace(mode: Int): Unit = gl.glCullFace(mode) + override fun deleteBuffers(n: Int, items: Buffer): Unit = gl.glDeleteBuffers(n, items.pointer) + override fun deleteFramebuffers(n: Int, items: Buffer): Unit = gl.glDeleteFramebuffers(n, items.pointer) + override fun deleteProgram(program: Int): Unit = gl.glDeleteProgram(program) + override fun deleteRenderbuffers(n: Int, items: Buffer): Unit = gl.glDeleteRenderbuffers(n, items.pointer) + override fun deleteShader(shader: Int): Unit = gl.glDeleteShader(shader) + override fun deleteTextures(n: Int, items: Buffer): Unit = gl.glDeleteTextures(n, items.pointer) + override fun depthFunc(func: Int): Unit = gl.glDepthFunc(func) + override fun depthMask(flag: Boolean): Unit = gl.glDepthMask(flag.toInt()) + override fun depthRangef(n: Float, f: Float): Unit = gl.glDepthRangef(n, f) + override fun detachShader(program: Int, shader: Int): Unit = gl.glDetachShader(program, shader) + override fun disable(cap: Int): Unit = gl.glDisable(cap) + override fun disableVertexAttribArray(index: Int): Unit = gl.glDisableVertexAttribArray(index) + override fun drawArrays(mode: Int, first: Int, count: Int): Unit = gl.glDrawArrays(mode, first, count) + override fun drawElements(mode: Int, count: Int, type: Int, indices: Int): Unit = gl.glDrawElements(mode, count, type, indices.convert()) + override fun enable(cap: Int): Unit = gl.glEnable(cap) + override fun enableVertexAttribArray(index: Int): Unit = gl.glEnableVertexAttribArray(index) + override fun finish(): Unit = gl.glFinish() + override fun flush(): Unit = gl.glFlush() + override fun framebufferRenderbuffer(target: Int, attachment: Int, renderbuffertarget: Int, renderbuffer: Int): Unit = gl.glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer) + override fun framebufferTexture2D(target: Int, attachment: Int, textarget: Int, texture: Int, level: Int): Unit = gl.glFramebufferTexture2D(target, attachment, textarget, texture, level) + override fun frontFace(mode: Int): Unit = gl.glFrontFace(mode) + override fun genBuffers(n: Int, buffers: Buffer): Unit = gl.glGenBuffers(n, buffers.pointer) + override fun generateMipmap(target: Int): Unit = gl.glGenerateMipmap(target) + override fun genFramebuffers(n: Int, framebuffers: Buffer): Unit = gl.glGenFramebuffers(n, framebuffers.pointer) + override fun genRenderbuffers(n: Int, renderbuffers: Buffer): Unit = gl.glGenRenderbuffers(n, renderbuffers.pointer) + override fun genTextures(n: Int, textures: Buffer): Unit = gl.glGenTextures(n, textures.pointer) + override fun getActiveAttrib(program: Int, index: Int, bufSize: Int, length: Buffer, size: Buffer, type: Buffer, name: Buffer): Unit = gl.glGetActiveAttrib(program, index, bufSize, length.pointer, size.pointer, type.pointer, name.pointer) + override fun getActiveUniform(program: Int, index: Int, bufSize: Int, length: Buffer, size: Buffer, type: Buffer, name: Buffer): Unit = gl.glGetActiveUniform(program, index, bufSize, length.pointer, size.pointer, type.pointer, name.pointer) + override fun getAttachedShaders(program: Int, maxCount: Int, count: Buffer, shaders: Buffer): Unit = gl.glGetAttachedShaders(program, maxCount, count.pointer, shaders.pointer) + override fun getAttribLocation(program: Int, name: String): Int = gl.glGetAttribLocation(program, name) + override fun getUniformLocation(program: Int, name: String): Int = gl.glGetUniformLocation(program, name) + override fun getBooleanv(pname: Int, data: Buffer): Unit = gl.glGetBooleanv(pname, data.pointer) + override fun getBufferParameteriv(target: Int, pname: Int, params: Buffer): Unit = gl.glGetBufferParameteriv(target, pname, params.pointer) + override fun getError(): Int = gl.glGetError() + override fun getFloatv(pname: Int, data: Buffer): Unit = gl.glGetFloatv(pname, data.pointer) + override fun getFramebufferAttachmentParameteriv(target: Int, attachment: Int, pname: Int, params: Buffer): Unit { gl.glGetFramebufferAttachmentParameteriv(target, attachment, pname, params.pointer) } + override fun getIntegerv(pname: Int, data: Buffer): Unit = gl.glGetIntegerv(pname, data.pointer) + override fun getProgramInfoLog(program: Int, bufSize: Int, length: Buffer, infoLog: Buffer): Unit { + gl.glGetProgramInfoLog(program, bufSize, length.pointer, infoLog.pointer) + } + override fun getRenderbufferParameteriv(target: Int, pname: Int, params: Buffer): Unit = gl.glGetRenderbufferParameteriv(target, pname, params.pointer) + override fun getProgramiv(program: Int, pname: Int, params: Buffer): Unit = gl.glGetProgramiv(program, pname, params.pointer) + override fun getShaderiv(shader: Int, pname: Int, params: Buffer): Unit = gl.glGetShaderiv(shader, pname, params.pointer) + override fun getShaderInfoLog(shader: Int, bufSize: Int, length: Buffer, infoLog: Buffer): Unit { + gl.glGetShaderInfoLog(shader, bufSize, length.pointer, infoLog.pointer) + } + override fun getShaderPrecisionFormat(shadertype: Int, precisiontype: Int, range: Buffer, precision: Buffer): Unit = Unit + override fun getShaderSource(shader: Int, bufSize: Int, length: Buffer, source: Buffer): Unit { + gl.glGetShaderSource(shader, bufSize, length.pointer, source.pointer) + } + override fun getString(name: Int): String = gl.glGetString(name) ?: "" + override fun getTexParameterfv(target: Int, pname: Int, params: Buffer): Unit = gl.glGetTexParameterfv(target, pname, params.pointer) + override fun getTexParameteriv(target: Int, pname: Int, params: Buffer): Unit = gl.glGetTexParameteriv(target, pname, params.pointer) + override fun getUniformfv(program: Int, location: Int, params: Buffer): Unit = gl.glGetUniformfv(program, location, params.pointer) + override fun getUniformiv(program: Int, location: Int, params: Buffer): Unit = gl.glGetUniformiv(program, location, params.pointer) + override fun getVertexAttribfv(index: Int, pname: Int, params: Buffer): Unit = gl.glGetVertexAttribfv(index, pname, params.pointer) + override fun getVertexAttribiv(index: Int, pname: Int, params: Buffer): Unit = gl.glGetVertexAttribiv(index, pname, params.pointer) + override fun getVertexAttribPointerv(index: Int, pname: Int, pointer: Buffer): Unit = gl.glGetVertexAttribPointerv(index, pname, pointer.pointer) + override fun hint(target: Int, mode: Int): Unit = gl.glHint(target, mode) + override fun isBuffer(buffer: Int): Boolean = gl.glIsBuffer(buffer).toBool() + override fun isEnabled(cap: Int): Boolean = gl.glIsEnabled(cap).toBool() + override fun isFramebuffer(framebuffer: Int): Boolean = gl.glIsFramebuffer(framebuffer).toBool() + override fun isProgram(program: Int): Boolean = gl.glIsProgram(program).toBool() + override fun isRenderbuffer(renderbuffer: Int): Boolean = gl.glIsRenderbuffer(renderbuffer).toBool() + override fun isShader(shader: Int): Boolean = gl.glIsShader(shader).toBool() + override fun isTexture(texture: Int): Boolean = gl.glIsTexture(texture).toBool() + override fun lineWidth(width: Float): Unit = gl.glLineWidth(width) + override fun linkProgram(program: Int): Unit = gl.glLinkProgram(program) + override fun pixelStorei(pname: Int, param: Int): Unit = gl.glPixelStorei(pname, param) + override fun polygonOffset(factor: Float, units: Float): Unit = gl.glPolygonOffset(factor, units) + override fun readPixels(x: Int, y: Int, width: Int, height: Int, format: Int, type: Int, pixels: Buffer): Unit = + gl.glReadPixels(x, y, width, height, format, type, pixels.pointer) + override fun releaseShaderCompiler(): Unit = Unit + override fun renderbufferStorage(target: Int, internalformat: Int, width: Int, height: Int): Unit = gl.glRenderbufferStorage(target, internalformat, width, height) + override fun sampleCoverage(value: Float, invert: Boolean): Unit = gl.glSampleCoverage(value, invert.toInt()) + override fun scissor(x: Int, y: Int, width: Int, height: Int): Unit = gl.glScissor(x, y, width, height) + override fun shaderBinary(count: Int, shaders: Buffer, binaryformat: Int, binary: Buffer, length: Int): Unit = throw KmlGlException("shaderBinary not implemented in Native") + override fun shaderSource(shader: Int, string: String): Unit { + CreateFFIMemory(8).usePointer { lengthsPtr -> + val bytes = "$string\u0000".encodeToByteArray() + lengthsPtr.set32(bytes.size - 1) + CreateFFIMemory(bytes).usePointer { strPtr -> + CreateFFIMemory(8).usePointer { strPtrPtr -> + strPtrPtr.setFFIPointer(strPtr) + gl.glShaderSource(shader, 1, strPtrPtr, lengthsPtr) + } + } + } + } + override fun stencilFunc(func: Int, ref: Int, mask: Int): Unit = gl.glStencilFunc(func, ref, mask) + override fun stencilFuncSeparate(face: Int, func: Int, ref: Int, mask: Int): Unit = gl.glStencilFuncSeparate(face, func, ref, mask) + override fun stencilMask(mask: Int): Unit = gl.glStencilMask(mask) + override fun stencilMaskSeparate(face: Int, mask: Int): Unit = gl.glStencilMaskSeparate(face, mask) + override fun stencilOp(fail: Int, zfail: Int, zpass: Int): Unit = gl.glStencilOp(fail, zfail, zpass) + override fun stencilOpSeparate(face: Int, sfail: Int, dpfail: Int, dppass: Int): Unit = gl.glStencilOpSeparate(face, sfail, dpfail, dppass) + override fun texImage2D(target: Int, level: Int, internalformat: Int, width: Int, height: Int, border: Int, format: Int, type: Int, pixels: Buffer?): Unit { + gl.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels?.pointer) + } + override fun texImage2D(target: Int, level: Int, internalformat: Int, format: Int, type: Int, data: NativeImage): Unit { + val buffer = Buffer(data.width * data.height * 4) + buffer.setArrayLE(0, data.toBMP32IfRequired().ints) + texImage2D(target, level, internalformat, data.width, data.height, 0, format, type, buffer) + } + override fun texParameterf(target: Int, pname: Int, param: Float): Unit = gl.glTexParameterf(target, pname, param) + override fun texParameterfv(target: Int, pname: Int, params: Buffer): Unit = gl.glTexParameterfv(target, pname, params.pointer) + override fun texParameteri(target: Int, pname: Int, param: Int): Unit = gl.glTexParameteri(target, pname, param) + override fun texParameteriv(target: Int, pname: Int, params: Buffer): Unit = gl.glTexParameteriv(target, pname, params.pointer) + override fun texSubImage2D(target: Int, level: Int, xoffset: Int, yoffset: Int, width: Int, height: Int, format: Int, type: Int, pixels: Buffer): Unit { + //gl.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels) + TODO() + } + override fun uniform1f(location: Int, v0: Float): Unit = gl.glUniform1f(location, v0) + override fun uniform1fv(location: Int, count: Int, value: Buffer): Unit = gl.glUniform1fv(location, count, value.pointer) + override fun uniform1i(location: Int, v0: Int): Unit = gl.glUniform1i(location, v0) + override fun uniform1iv(location: Int, count: Int, value: Buffer): Unit = gl.glUniform1iv(location, count, value.pointer) + override fun uniform2f(location: Int, v0: Float, v1: Float): Unit = gl.glUniform2f(location, v0, v1) + override fun uniform2fv(location: Int, count: Int, value: Buffer): Unit = gl.glUniform2fv(location, count, value.pointer) + override fun uniform2i(location: Int, v0: Int, v1: Int): Unit = gl.glUniform2i(location, v0, v1) + override fun uniform2iv(location: Int, count: Int, value: Buffer): Unit = gl.glUniform2iv(location, count, value.pointer) + override fun uniform3f(location: Int, v0: Float, v1: Float, v2: Float): Unit = gl.glUniform3f(location, v0, v1, v2) + override fun uniform3fv(location: Int, count: Int, value: Buffer): Unit = gl.glUniform3fv(location, count, value.pointer) + override fun uniform3i(location: Int, v0: Int, v1: Int, v2: Int): Unit = gl.glUniform3i(location, v0, v1, v2) + override fun uniform3iv(location: Int, count: Int, value: Buffer): Unit = gl.glUniform3iv(location, count, value.pointer) + override fun uniform4f(location: Int, v0: Float, v1: Float, v2: Float, v3: Float): Unit = gl.glUniform4f(location, v0, v1, v2, v3) + override fun uniform4fv(location: Int, count: Int, value: Buffer): Unit = gl.glUniform4fv(location, count, value.pointer) + override fun uniform4i(location: Int, v0: Int, v1: Int, v2: Int, v3: Int): Unit = gl.glUniform4i(location, v0, v1, v2, v3) + override fun uniform4iv(location: Int, count: Int, value: Buffer): Unit = gl.glUniform4iv(location, count, value.pointer) + override fun uniformMatrix2fv(location: Int, count: Int, transpose: Boolean, value: Buffer): Unit = gl.glUniformMatrix2fv(location, count, transpose.toInt(), value.pointer) + override fun uniformMatrix3fv(location: Int, count: Int, transpose: Boolean, value: Buffer): Unit = gl.glUniformMatrix3fv(location, count, transpose.toInt(), value.pointer) + override fun uniformMatrix4fv(location: Int, count: Int, transpose: Boolean, value: Buffer): Unit = gl.glUniformMatrix4fv(location, count, transpose.toInt(), value.pointer) + override fun useProgram(program: Int): Unit = gl.glUseProgram(program) + override fun validateProgram(program: Int): Unit = gl.glValidateProgram(program) + override fun vertexAttrib1f(index: Int, x: Float): Unit = gl.glVertexAttrib1f(index, x) + override fun vertexAttrib1fv(index: Int, v: Buffer): Unit = gl.glVertexAttrib1fv(index, v.pointer) + override fun vertexAttrib2f(index: Int, x: Float, y: Float): Unit = gl.glVertexAttrib2f(index, x, y) + override fun vertexAttrib2fv(index: Int, v: Buffer): Unit = gl.glVertexAttrib2fv(index, v.pointer) + override fun vertexAttrib3f(index: Int, x: Float, y: Float, z: Float): Unit = gl.glVertexAttrib3f(index, x, y, z) + override fun vertexAttrib3fv(index: Int, v: Buffer): Unit = gl.glVertexAttrib3fv(index, v.pointer) + override fun vertexAttrib4f(index: Int, x: Float, y: Float, z: Float, w: Float): Unit = gl.glVertexAttrib4f(index, x, y, z, w) + override fun vertexAttrib4fv(index: Int, v: Buffer): Unit = gl.glVertexAttrib4fv(index, v.pointer) + override fun vertexAttribPointer(index: Int, size: Int, type: Int, normalized: Boolean, stride: Int, pointer: Long): Unit = + gl.glVertexAttribPointer(index, size, type, normalized.toInt(), stride, pointer.convert()) + override fun viewport(x: Int, y: Int, width: Int, height: Int): Unit = gl.glViewport(x, y, width, height) + + override val isInstancedSupported: Boolean get() = true + + override fun drawArraysInstanced(mode: Int, first: Int, count: Int, instancecount: Int) = gl.glDrawArraysInstanced(mode, first, count, instancecount) + override fun drawElementsInstanced(mode: Int, count: Int, type: Int, indices: Int, instancecount: Int) = gl.glDrawElementsInstanced(mode, count, type, indices.convert(), instancecount) + override fun vertexAttribDivisor(index: Int, divisor: Int) = gl.glVertexAttribDivisor(index, divisor) + + override val isUniformBuffersSupported: Boolean get() = true + + override fun bindBufferRange(target: Int, index: Int, buffer: Int, offset: Int, size: Int) = gl.glBindBufferRange(target, index, buffer, offset.convert(), size.convert()) + override fun getUniformBlockIndex(program: Int, name: String): Int = gl.glGetUniformBlockIndex(program, name).toInt() + override fun uniformBlockBinding(program: Int, uniformBlockIndex: Int, uniformBlockBinding: Int) = gl.glUniformBlockBinding(program, uniformBlockIndex, uniformBlockBinding) + + override val isVertexArraysSupported: Boolean get() = true + + override fun genVertexArrays(n: Int, arrays: Buffer) = gl.glGenVertexArrays(n, arrays.pointer) + override fun deleteVertexArrays(n: Int, arrays: Buffer) = gl.glDeleteBuffers(n, arrays.pointer) + override fun bindVertexArray(array: Int) = gl.glBindVertexArray(array) } diff --git a/korge-core/src/korlibs/sdl/SDLGameWindow.kt b/korge-core/src/korlibs/sdl/SDLGameWindow.kt index 68b32cde3d..0dc787a55d 100644 --- a/korge-core/src/korlibs/sdl/SDLGameWindow.kt +++ b/korge-core/src/korlibs/sdl/SDLGameWindow.kt @@ -2,7 +2,9 @@ package korlibs.sdl import korlibs.ffi.* import korlibs.graphics.* +import korlibs.graphics.gl.* import korlibs.graphics.log.* +import korlibs.kgl.* import korlibs.math.geom.* import korlibs.render.* @@ -17,21 +19,30 @@ abstract class SDLGameWindow( size.width.toInt(), size.height.toInt(), SDL.SDL_WINDOW_RESIZABLE or SDL.SDL_WINDOW_OPENGL ) + val glContext = sdl.SDL_GL_CreateContext(window) + init { sdl.SDL_ShowWindow(window) } - override val ag: AG = AGDummy() + override val ag: AGOpengl = AGOpengl(object : KmlGlContext(window, KmlGlOpenGL()) { + override fun set() { sdl.SDL_GL_MakeCurrent(window as FFIPointer, glContext) } + override fun unset() { sdl.SDL_GL_MakeCurrent(window as FFIPointer, null) } + override fun swap() { sdl.SDL_GL_SwapWindow(window as FFIPointer) } + override fun close() { sdl.SDL_GL_DeleteContext(glContext) } + }) + val gl: KmlGl = ag.gl + + override var title: String = "" + set(value) { + field = value + sdl.SDL_SetWindowTitle(window, title) + } - //override var title: String = "" - // set(value) { - // field = value - // sdl.SDL_SetWindowTitle(window, title) - // } -// - //override fun setSize(width: Int, height: Int) { - // sdl.SDL_SetWindowSize(window, width, height) - //} + override fun setSize(width: Int, height: Int) { + sdl.SDL_SetWindowSize(window, width, height) + sdl.SDL_SetWindowPosition(window, SDL.SDL_WINDOWPOS_CENTERED, SDL.SDL_WINDOWPOS_CENTERED) + } fun init() { //SDL.SDL_WINDOWPOS_CENTERED @@ -44,17 +55,44 @@ abstract class SDLGameWindow( while (sdl.SDL_PollEvent(it)) { val type = it.getS32(0) when (type) { + SDL.SDL_WINDOWEVENT -> { + println("WINDOW EVENT") + } + SDL.SDL_MOUSEMOTION -> { + println("MOUSE MOTION") + } + SDL.SDL_MOUSEBUTTONDOWN -> { + println("MOUSE DOWN") + } + SDL.SDL_MOUSEBUTTONUP -> { + println("MOUSE UP") + } SDL.SDL_QUIT -> { close() } + else -> { + println("EVENT: type=$type") + } } - println("EVENT: type=$type") } } + + sdl.SDL_GL_MakeCurrent(window, glContext) + //gl.viewport(0, 0, 200, 200); + gl.clearColor(1f, 0f, 0f, 1f) + gl.clear(OpenGL.GL_COLOR_BUFFER_BIT) + //gl.flush() + //sdl.SDL_GL_SwapWindow(window) + //sdl.SDL_GL_MakeCurrent(window, null) + } + + fun afterFrame() { + sdl.SDL_GL_SwapWindow(window) } override fun close(exitCode: Int) { super.close(exitCode) + sdl.SDL_GL_DeleteContext(glContext) sdl.SDL_Quit() } } diff --git a/korge-core/src@js/korlibs/render/DenoJsGameWindow.kt b/korge-core/src@js/korlibs/render/DenoJsGameWindow.kt index f893052b4a..ff8ebffd36 100644 --- a/korge-core/src@js/korlibs/render/DenoJsGameWindow.kt +++ b/korge-core/src@js/korlibs/render/DenoJsGameWindow.kt @@ -22,6 +22,7 @@ class DenoJsGameWindow( //println("INTERVAL") updateSDLEvents() frame() + afterFrame() }, 16) //CompletableDeferred().await() diff --git a/korge-core/src@jvm/korlibs/render/win32/Win32.kt b/korge-core/src@jvm/korlibs/render/win32/Win32.kt index 9d050dd4c4..8347fd6e6e 100644 --- a/korge-core/src@jvm/korlibs/render/win32/Win32.kt +++ b/korge-core/src@jvm/korlibs/render/win32/Win32.kt @@ -1,12 +1,12 @@ package korlibs.render.win32 -import korlibs.event.Key import com.sun.jna.Native import com.sun.jna.Pointer import com.sun.jna.Structure import com.sun.jna.platform.win32.* import com.sun.jna.ptr.PointerByReference import com.sun.jna.win32.W32APIOptions +import korlibs.event.* import java.nio.Buffer object Win32 : MyKernel32 by MyKernel32, @@ -237,87 +237,17 @@ internal const val VK_SNAPSHOT = 0x2C /* PRINT SCREEN key */ internal const val VK_INSERT = 0x2D /* INS key */ internal const val VK_DELETE = 0x2E /* DEL key */ internal const val VK_HELP = 0x2F /* HELP key */ -internal const val VK_KEY_0 = 0x30 /* '0' key */ -internal const val VK_KEY_1 = 0x31 /* '1' key */ -internal const val VK_KEY_2 = 0x32 /* '2' key */ -internal const val VK_KEY_3 = 0x33 /* '3' key */ -internal const val VK_KEY_4 = 0x34 /* '4' key */ -internal const val VK_KEY_5 = 0x35 /* '5' key */ -internal const val VK_KEY_6 = 0x36 /* '6' key */ -internal const val VK_KEY_7 = 0x37 /* '7' key */ -internal const val VK_KEY_8 = 0x38 /* '8' key */ -internal const val VK_KEY_9 = 0x39 /* '9' key */ -internal const val VK_KEY_A = 0x41 /* 'A' key */ -internal const val VK_KEY_B = 0x42 /* 'B' key */ -internal const val VK_KEY_C = 0x43 /* 'C' key */ -internal const val VK_KEY_D = 0x44 /* 'D' key */ -internal const val VK_KEY_E = 0x45 /* 'E' key */ -internal const val VK_KEY_F = 0x46 /* 'F' key */ -internal const val VK_KEY_G = 0x47 /* 'G' key */ -internal const val VK_KEY_H = 0x48 /* 'H' key */ -internal const val VK_KEY_I = 0x49 /* 'I' key */ -internal const val VK_KEY_J = 0x4A /* 'J' key */ -internal const val VK_KEY_K = 0x4B /* 'K' key */ -internal const val VK_KEY_L = 0x4C /* 'L' key */ -internal const val VK_KEY_M = 0x4D /* 'M' key */ -internal const val VK_KEY_N = 0x4E /* 'N' key */ -internal const val VK_KEY_O = 0x4F /* 'O' key */ -internal const val VK_KEY_P = 0x50 /* 'P' key */ -internal const val VK_KEY_Q = 0x51 /* 'Q' key */ -internal const val VK_KEY_R = 0x52 /* 'R' key */ -internal const val VK_KEY_S = 0x53 /* 'S' key */ -internal const val VK_KEY_T = 0x54 /* 'T' key */ -internal const val VK_KEY_U = 0x55 /* 'U' key */ -internal const val VK_KEY_V = 0x56 /* 'V' key */ -internal const val VK_KEY_W = 0x57 /* 'W' key */ -internal const val VK_KEY_X = 0x58 /* 'X' key */ -internal const val VK_KEY_Y = 0x59 /* 'Y' key */ -internal const val VK_KEY_Z = 0x5A /* 'Z' key */ internal const val VK_LWIN = 0x5B /* Left Windows key (Microsoft Natural keyboard) */ internal const val VK_RWIN = 0x5C /* Right Windows key (Natural keyboard) */ internal const val VK_APPS = 0x5D /* Applications key (Natural keyboard) */ internal const val VK_POWER = 0x5E /* Power key */ internal const val VK_SLEEP = 0x5F /* Computer Sleep key */ -internal const val VK_NUMPAD0 = 0x60 /* Numeric keypad '0' key */ -internal const val VK_NUMPAD1 = 0x61 /* Numeric keypad '1' key */ -internal const val VK_NUMPAD2 = 0x62 /* Numeric keypad '2' key */ -internal const val VK_NUMPAD3 = 0x63 /* Numeric keypad '3' key */ -internal const val VK_NUMPAD4 = 0x64 /* Numeric keypad '4' key */ -internal const val VK_NUMPAD5 = 0x65 /* Numeric keypad '5' key */ -internal const val VK_NUMPAD6 = 0x66 /* Numeric keypad '6' key */ -internal const val VK_NUMPAD7 = 0x67 /* Numeric keypad '7' key */ -internal const val VK_NUMPAD8 = 0x68 /* Numeric keypad '8' key */ -internal const val VK_NUMPAD9 = 0x69 /* Numeric keypad '9' key */ internal const val VK_MULTIPLY = 0x6A /* Multiply key */ internal const val VK_ADD = 0x6B /* Add key */ internal const val VK_SEPARATOR = 0x6C /* Separator key */ internal const val VK_SUBTRACT = 0x6D /* Subtract key */ internal const val VK_DECIMAL = 0x6E /* Decimal key */ internal const val VK_DIVIDE = 0x6F /* Divide key */ -internal const val VK_F1 = 0x70 /* F1 key */ -internal const val VK_F2 = 0x71 /* F2 key */ -internal const val VK_F3 = 0x72 /* F3 key */ -internal const val VK_F4 = 0x73 /* F4 key */ -internal const val VK_F5 = 0x74 /* F5 key */ -internal const val VK_F6 = 0x75 /* F6 key */ -internal const val VK_F7 = 0x76 /* F7 key */ -internal const val VK_F8 = 0x77 /* F8 key */ -internal const val VK_F9 = 0x78 /* F9 key */ -internal const val VK_F10 = 0x79 /* F10 key */ -internal const val VK_F11 = 0x7A /* F11 key */ -internal const val VK_F12 = 0x7B /* F12 key */ -internal const val VK_F13 = 0x7C /* F13 key */ -internal const val VK_F14 = 0x7D /* F14 key */ -internal const val VK_F15 = 0x7E /* F15 key */ -internal const val VK_F16 = 0x7F /* F16 key */ -internal const val VK_F17 = 0x80 /* F17 key */ -internal const val VK_F18 = 0x81 /* F18 key */ -internal const val VK_F19 = 0x82 /* F19 key */ -internal const val VK_F20 = 0x83 /* F20 key */ -internal const val VK_F21 = 0x84 /* F21 key */ -internal const val VK_F22 = 0x85 /* F22 key */ -internal const val VK_F23 = 0x86 /* F23 key */ -internal const val VK_F24 = 0x87 /* F24 key */ internal const val VK_NUMLOCK = 0x90 /* NUM LOCK key */ internal const val VK_SCROLL = 0x91 /* SCROLL LOCK key */ internal const val VK_LSHIFT = 0xA0 /* Left SHIFT key */ @@ -387,7 +317,17 @@ internal const val VK_PA1 = 0xFD /* PA1 key */ internal const val VK_OEM_CLEAR = 0xFE /* Clear key */ internal const val VK_NONE = 0xFF /* no key */ -val VK_TABLE: Map = mapOf( +internal const val VK_KEY_0 = 0x30 /* '0' key */ +internal const val VK_KEY_A = 0x41 /* 'A' key */ +internal const val VK_NUMPAD0 = 0x60 /* Numeric keypad '0' key */ + +val VK_TABLE: IntToKeyMap = IntToKeyMap { + map(VK_KEY_0, Key.N0..Key.N9) + map(VK_NUMPAD0, Key.NUMPAD0..Key.NUMPAD9) + map(VK_KEY_A, Key.A..Key.Z) +} +/* += mapOf( KBDEXT to Key.UNKNOWN, KBDMULTIVK to Key.UNKNOWN, KBDSPECIAL to Key.UNKNOWN, @@ -590,3 +530,4 @@ val VK_TABLE: Map = mapOf( VK_OEM_CLEAR to Key.UNKNOWN, VK_NONE to Key.UNKNOWN ) +*/ diff --git a/korge-core/test@js/korlibs/webgpu/WebGPUTest.kt b/korge-core/test@js/korlibs/webgpu/WebGPUTest.kt index 9a92e92569..0590857ce1 100644 --- a/korge-core/test@js/korlibs/webgpu/WebGPUTest.kt +++ b/korge-core/test@js/korlibs/webgpu/WebGPUTest.kt @@ -6,6 +6,7 @@ import korlibs.image.format.* import korlibs.io.async.* import korlibs.io.net.* import korlibs.math.geom.* +import korlibs.platform.* import kotlinx.browser.* import kotlinx.coroutines.* import org.khronos.webgl.* @@ -115,6 +116,8 @@ class WebGPUTest { // https://github.com/denoland/webgpu-examples/blob/e8498aa0e001168b77762dde8a1f5fca30c551a7/hello-triangle/mod.ts @Test fun testOffscreen() = suspendTest { + if (Platform.isJsDenoJs) return@suspendTest + val dimensions = SizeInt(200, 200) val adapter: GPUAdapter = navigator.gpu.requestAdapter().await() ?: (return@suspendTest Unit.also { diff --git a/korge-gradle-plugin-common/build.gradle.kts b/korge-gradle-plugin-common/build.gradle.kts index 623137a88b..27470f1c33 100644 --- a/korge-gradle-plugin-common/build.gradle.kts +++ b/korge-gradle-plugin-common/build.gradle.kts @@ -19,7 +19,7 @@ dependencies { //implementation(localGroovy()) //implementation("org.eclipse.jgit:org.eclipse.jgit:6.3.0.202209071007-r") implementation(libs.jgit) - implementation(libs.korlibs.serialization) + //implementation(libs.korlibs.serialization) testImplementation(libs.bundles.kotlin.test) } diff --git a/korge-gradle-plugin-common/src/main/kotlin/com/soywiz/kproject/model/NewKProjectModel.kt b/korge-gradle-plugin-common/src/main/kotlin/com/soywiz/kproject/model/NewKProjectModel.kt index 0683b9be1e..b42e2b44c6 100644 --- a/korge-gradle-plugin-common/src/main/kotlin/com/soywiz/kproject/model/NewKProjectModel.kt +++ b/korge-gradle-plugin-common/src/main/kotlin/com/soywiz/kproject/model/NewKProjectModel.kt @@ -1,7 +1,7 @@ package com.soywiz.kproject.model import com.soywiz.kproject.internal.* -import korlibs.io.serialization.yaml.* +import com.soywiz.kproject.util.* //enum class NewKProjectType { LIBRARY, EXECUTABLE } diff --git a/korge-gradle-plugin-common/src/main/kotlin/com/soywiz/kproject/util/Yaml.kt b/korge-gradle-plugin-common/src/main/kotlin/com/soywiz/kproject/util/Yaml.kt new file mode 100644 index 0000000000..aeb576ce92 --- /dev/null +++ b/korge-gradle-plugin-common/src/main/kotlin/com/soywiz/kproject/util/Yaml.kt @@ -0,0 +1,363 @@ +package com.soywiz.kproject.util + +import kotlin.collections.set + +object Yaml { + fun decode(str: String): Any? = read(ListReader(tokenize(str)), level = 0) + fun read(str: String): Any? = read(ListReader(tokenize(str)), level = 0) + + private fun parseStr(toks: List): Any? { + if (toks.size == 1 && toks[0] is Token.STR) return toks[0].ustr + return parseStr(toks.joinToString("") { it.ustr }) + } + + private fun parseStr(str: String) = when (str) { + "null" -> null + "true" -> true + "false" -> false + else -> str.toIntOrNull() ?: str.toDoubleOrNull() ?: str + } + + //const val TRACE = true + const val TRACE = false + private val EMPTY_SET = setOf() + private val SET_COMMA_END_ARRAY = setOf(",", "]") + + private fun read(s: ListReader, level: Int): Any? = s.run { + var list: ArrayList? = null + var map: MutableMap? = null + var lastMapKey: String? = null + var lastMapValue: Any? = null + + val levelStr = if (TRACE) " ".repeat(level) else "" + + linehandle@ while (s.hasMore) { + val token = s.peek() + val line = token as? Token.LINE + val lineLevel = line?.level + if (TRACE && line != null) println("${levelStr}LINE($lineLevel)") + if (lineLevel != null && lineLevel > level) { + // child level + val res = read(s, lineLevel) + if (list != null) { + if (TRACE) println("${levelStr}CHILD.list.add: $res") + list.add(res) + } else { + if (TRACE) println("${levelStr}CHILD.return: $res") + return res + } + } else if (lineLevel != null && lineLevel < level) { + // parent level + if (TRACE) println("${levelStr}PARENT: level < line.level") + break + } else { + // current level + if (line != null) s.read() + if (s.eof) break + val item = s.peek() + when (item.str) { + "-" -> { + if (s.read().str != "-") invalidOp + if (list == null) { + list = arrayListOf() + if (map != null && lastMapKey != null && lastMapValue == null) { + map[lastMapKey] = list + } + } + if (TRACE) println("${levelStr}LIST_ITEM...") + val res = read(s, level + 1) + if (TRACE) println("${levelStr}LIST_ITEM: $res") + list.add(res) + } + "[" -> { + if (s.read().str != "[") invalidOp + val olist = arrayListOf() + array@ while (s.peek().str != "]") { + olist += readOrString(s, level, SET_COMMA_END_ARRAY, supportNonSpaceSymbols = false) + val p = s.peek().str + when (p) { + "," -> { s.read(); continue@array } + "]" -> break@array + else -> invalidOp("Unexpected '$p'") + } + } + if (s.read().str != "]") invalidOp + return olist + } + else -> { + val keyIds = s.readId() + val sp = s.peekOrNull() ?: Token.EOF + if (s.eof || (sp.str != ":" || (sp is Token.SYMBOL && !sp.isNextWhite))) { + val key = parseStr(keyIds) + if (TRACE) println("${levelStr}LIT: $key") + return key + } else { + val key = parseStr(keyIds).toString() + if (map == null) map = LinkedHashMap() + if (s.read().str != ":") invalidOp + if (TRACE) println("${levelStr}MAP[$key]...") + val next = s.peekOrNull() + val nextStr = next?.str + val hasSpaces = next is Token.SYMBOL && next.isNextWhite + val nextIsSpecialSymbol = nextStr == "[" || nextStr == "{" || (nextStr == "-" && hasSpaces) + val value = readOrString(s, level, EMPTY_SET, supportNonSpaceSymbols = !nextIsSpecialSymbol) + lastMapKey = key + lastMapValue = value + map[key] = value + list = null + if (TRACE) println("${levelStr}MAP[$key]: $value") + } + } + } + } + } + + if (TRACE) println("${levelStr}RETURN: list=$list, map=$map") + + return map ?: list + } + + private fun ListReader.readId(): List { + val tokens = arrayListOf() + while (hasMore) { + val token = peek() + if (token is Token.ID || token is Token.STR || ((token is Token.SYMBOL) && token.str == "-") || ((token is Token.SYMBOL) && token.str == ":" && !token.isNextWhite)) { + tokens.add(token) + read() + } else { + break + } + } + return tokens + } + + private fun readOrString(s: ListReader, level: Int, delimiters: Set, supportNonSpaceSymbols: Boolean): Any? { + val sp = s.peek() + return if (sp is Token.ID || (supportNonSpaceSymbols && sp is Token.SYMBOL && !sp.isNextWhite)) { + var str = "" + str@while (s.hasMore) { + val p = s.peek() + if (p is Token.LINE) break@str + if (p.str in delimiters) break@str + str += s.read().str + } + parseStr(str) + } else { + read(s, level + 1) + } + } + + fun tokenize(str: String): List = StrReader(str.replace("\r\n", "\n")).tokenize() + + private fun StrReader.tokenize(): List { + val out = arrayListOf() + + val s = this + var str = "" + fun flush() { + if (str.isNotBlank() && str.isNotEmpty()) { + out += Token.ID(str.trim()); str = "" + } + } + + val indents = ArrayList() + linestart@ while (hasMore) { + // Line start + flush() + val indentStr = readWhile(kotlin.Char::isWhitespace).replace("\t", " ") + if (indentStr.contains('\n')) continue@linestart // ignore empty lines with possible additional indent + val indent = indentStr.length + if (indents.isEmpty() || indent > indents.last()) { + indents += indent + } else { + while (indents.isNotEmpty() && indent < indents.last()) indents.removeAt(indents.size - 1) + if (indents.isEmpty()) invalidOp + } + val indentLevel = indents.size - 1 + while (out.isNotEmpty() && out.last() is Token.LINE) out.removeAt(out.size - 1) + out += Token.LINE(indentStr, indentLevel) + while (hasMore) { + val c = read() + when (c) { + ':', '-', '[', ']', ',' -> { + flush(); out += Token.SYMBOL("$c", peekChar()) + } + '#' -> { + if (str.lastOrNull()?.isWhitespaceFast() == true || (str == "" && out.lastOrNull() is Token.LINE)) { + flush(); readUntilLineEnd(); skip(); continue@linestart + } else { + str += c + } + } + '\n' -> { + flush(); continue@linestart + } + '"', '\'' -> { + flush() + val last = out.lastOrNull() + //println("out=$last, c='$c', reader=$this") + if (last is Token.SYMBOL && (last.str == ":" || last.str == "[" || last.str == "{" || last.str == "," || last.str == "-")) { + s.unread() + //println(" -> c='$c', reader=$this") + out += Token.STR(s.readStringLit()) + } else { + str += c + } + } + else -> str += c + } + } + } + flush() + return out + } + + interface Token { + val str: String + val ustr get() = str + + object EOF : Token { + override val str: String = "" + } + + data class LINE(override val str: String, val level: Int) : Token { + override fun toString(): String = "LINE($level)" + } + + data class ID(override val str: String) : Token + data class STR(override val str: String) : Token { + override val ustr = str.unquote() + } + + data class SYMBOL(override val str: String, val next: Char) : Token { + val isNextWhite: Boolean get() = next == ' ' || next == '\t' || next == '\n' || next == '\r' + } + } + + private fun StrReader.readUntilLineEnd() = this.readUntil { it == '\n' } + + private val invalidOp: Nothing get() = throw RuntimeException() + private fun invalidOp(msg: String): Nothing = throw RuntimeException(msg) + + private class ListReader(val list: List, val ctx: T? = null) { + class OutOfBoundsException(val list: ListReader<*>, val pos: Int) : RuntimeException() + var position = 0 + val eof: Boolean get() = position >= list.size + val hasMore: Boolean get() = position < list.size + fun peekOrNull(): T? = list.getOrNull(position) + fun peek(): T = list.getOrNull(position) ?: throw OutOfBoundsException(this, position) + fun skip(count: Int = 1) = this.apply { this.position += count } + fun read(): T = peek().apply { skip(1) } + override fun toString(): String = "ListReader($list)" + } + + private class StrReader(val str: String, var pos: Int = 0) { + val length get() = str.length + val hasMore get() = pos < length + + inline fun skipWhile(f: (Char) -> Boolean) { while (hasMore && f(peek())) skip() } + fun skipUntil(f: (Char) -> Boolean): Unit = skipWhile { !f(it) } + + // @TODO: https://youtrack.jetbrains.com/issue/KT-29577 + private fun posSkip(count: Int): Int { + val out = this.pos + this.pos += count + return out + } + + fun skip() = skip(1) + fun peek(): Char = if (hasMore) this.str[this.pos] else '\u0000' + fun peekChar(): Char = peek() + fun read(): Char = if (hasMore) this.str[posSkip(1)] else '\u0000' + fun unread() = skip(-1) + + fun substr(start: Int, len: Int = length - pos): String { + val start = (start).coerceIn(0, length) + val end = (start + len).coerceIn(0, length) + return this.str.substring(start, end) + } + + fun skip(count: Int) = this.apply { this.pos += count } + fun peek(count: Int): String = this.substr(this.pos, count) + fun read(count: Int): String = this.peek(count).also { skip(count) } + + private inline fun readBlock(callback: () -> Unit): String { + val start = pos + callback() + val end = pos + return substr(start, end - start) + } + + fun readWhile(f: (Char) -> Boolean): String = readBlock { skipWhile(f) } + fun readUntil(f: (Char) -> Boolean): String = readBlock { skipUntil(f) } + + fun readStringLit(reportErrors: Boolean = true): String { + val out = StringBuilder() + val quotec = read() + when (quotec) { + '"', '\'' -> Unit + else -> throw RuntimeException("Invalid string literal") + } + var closed = false + while (hasMore) { + val c = read() + if (c == '\\') { + val cc = read() + out.append( + when (cc) { + '\\' -> '\\'; '/' -> '/'; '\'' -> '\''; '"' -> '"' + 'b' -> '\b'; 'f' -> '\u000c'; 'n' -> '\n'; 'r' -> '\r'; 't' -> '\t' + 'u' -> read(4).toInt(0x10).toChar() + else -> throw RuntimeException("Invalid char '$cc'") + } + ) + } else if (c == quotec) { + closed = true + break + } else { + out.append(c) + } + } + if (!closed && reportErrors) { + throw RuntimeException("String literal not closed! '${this.str}'") + } + return out.toString() + } + + override fun toString(): String = "StrReader(str=${str.length}, pos=$pos, range='${str.substring(pos.coerceIn(str.indices), (pos + 10).coerceIn(str.indices)).replace("\n", "\\n")}')" + } + + private fun Char.isWhitespaceFast(): Boolean = this == ' ' || this == '\t' || this == '\r' || this == '\n' + private fun String.isQuoted(): Boolean = this.startsWith('"') && this.endsWith('"') + private fun String.unquote(): String = if (isQuoted()) this.substring(1, this.length - 1).unescape() else this + private fun String.unescape(): String { + val out = StringBuilder(this.length) + var n = 0 + while (n < this.length) { + val c = this[n++] + when (c) { + '\\' -> { + val c2 = this[n++] + when (c2) { + '\\' -> out.append('\\') + '"' -> out.append('\"') + 'n' -> out.append('\n') + 'r' -> out.append('\r') + 't' -> out.append('\t') + 'x', 'u' -> { + val N = if (c2 == 'u') 4 else 2 + val chars = this.substring(n, n + N) + n += N + out.append(chars.toInt(16).toChar()) + } + else -> { + out.append("\\$c2") + } + } + } + else -> out.append(c) + } + } + return out.toString() + } +} diff --git a/korge-gradle-plugin-common/src/test/kotlin/com/soywiz/kproject/model/NewKProjectModelTest.kt b/korge-gradle-plugin-common/src/test/kotlin/com/soywiz/kproject/model/NewKProjectModelTest.kt index 8d19e3d57c..0d8b7de0db 100644 --- a/korge-gradle-plugin-common/src/test/kotlin/com/soywiz/kproject/model/NewKProjectModelTest.kt +++ b/korge-gradle-plugin-common/src/test/kotlin/com/soywiz/kproject/model/NewKProjectModelTest.kt @@ -1,7 +1,6 @@ package com.soywiz.kproject.model -import com.soywiz.kproject.internal.* -import korlibs.io.serialization.yaml.* +import com.soywiz.kproject.util.* import kotlin.test.* class NewKProjectModelTest { diff --git a/korge-gradle-plugin/build.gradle.kts b/korge-gradle-plugin/build.gradle.kts index 692094854c..e88abc2a5b 100644 --- a/korge-gradle-plugin/build.gradle.kts +++ b/korge-gradle-plugin/build.gradle.kts @@ -18,7 +18,6 @@ group = RootKorlibsPlugin.KORGE_GRADLE_PLUGIN_GROUP dependencies { implementation(kotlin("gradle-plugin-api")) implementation(project(":korge-gradle-plugin-common")) - implementation(libs.korlibs.serialization) } gradlePlugin { diff --git a/korge-gradle-plugin/src/main/kotlin/com/soywiz/kproject/KProjectPlugin.kt b/korge-gradle-plugin/src/main/kotlin/com/soywiz/kproject/KProjectPlugin.kt index 3495081f51..815c1a5c06 100644 --- a/korge-gradle-plugin/src/main/kotlin/com/soywiz/kproject/KProjectPlugin.kt +++ b/korge-gradle-plugin/src/main/kotlin/com/soywiz/kproject/KProjectPlugin.kt @@ -22,7 +22,7 @@ class KProjectPlugin : Plugin { val depsKprojectYml = File(project.rootProject.rootDir, "deps.kproject.yml") val info = when { - depsKprojectYml.exists() -> korlibs.io.serialization.yaml.Yaml.decode(depsKprojectYml.readText()) + depsKprojectYml.exists() -> Yaml.decode(depsKprojectYml.readText()) else -> null }.dyn val info2 = NewKProjectModel.loadFile(depsKprojectYml.fileRef) diff --git a/korge-ipc/build.gradle.kts b/korge-ipc/build.gradle.kts index 57c6c2f1b4..7ed6fea0d8 100644 --- a/korge-ipc/build.gradle.kts +++ b/korge-ipc/build.gradle.kts @@ -58,11 +58,11 @@ korlibs.NativeTools.groovyConfigurePublishing(project, false) korlibs.NativeTools.groovyConfigureSigning(project) dependencies { - //implementation(libs.kotlinx.coroutines.core) + implementation(libs.kotlinx.coroutines.core) implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.0") - implementation(libs.korlibs.datastructure.core) - implementation(libs.korlibs.memory) - implementation(libs.korlibs.io.stream) + //implementation(libs.korlibs.all) + //implementation(libs.korlibs.datastructure.core) + //implementation(libs.korlibs.io.stream) testImplementation(libs.bundles.kotlin.test) } diff --git a/korge-ipc/src/main/kotlin/korlibs/korge/ipc/KorgeIPCSocket.kt b/korge-ipc/src/main/kotlin/korlibs/korge/ipc/KorgeIPCSocket.kt index 7ec8adaef5..2f975d44b7 100644 --- a/korge-ipc/src/main/kotlin/korlibs/korge/ipc/KorgeIPCSocket.kt +++ b/korge-ipc/src/main/kotlin/korlibs/korge/ipc/KorgeIPCSocket.kt @@ -1,7 +1,5 @@ package korlibs.korge.ipc -import korlibs.io.stream.* -import korlibs.memory.* import kotlinx.serialization.* import kotlinx.serialization.Serializable import kotlinx.serialization.json.* @@ -179,8 +177,8 @@ class IPCPacket( return IPCPacket(type, data) } - operator fun invoke(type: Int, block: SyncOutputStream.() -> Unit): IPCPacket { - return IPCPacket(type, MemorySyncStreamToByteArray(128, block)) + operator fun invoke(type: Int, block: OutputStream.() -> Unit): IPCPacket { + return IPCPacket(type, ByteArrayOutputStream(128).apply(block).toByteArray()) } inline fun fromJson(type: Int, value: T): IPCPacket = @@ -244,14 +242,50 @@ class IPCPacket( } } -fun IPCPacket.Companion.genericEventGen(kind: String, eventData: ByteArray): ByteArray = MemorySyncStreamToByteArray(16 + kind.length + 4 + eventData.size + 4) { +private fun InputStream.readU8(): Int { + return read().also { + if (it < 0) error("EOF") + } +} + +private fun InputStream.readU_VL(): Int { + var result = readU8() + if ((result and 0x80) == 0) return result + result = (result and 0x7f) or (readU8() shl 7) + if ((result and 0x4000) == 0) return result + result = (result and 0x3fff) or (readU8() shl 14) + if ((result and 0x200000) == 0) return result + result = (result and 0x1fffff) or (readU8() shl 21) + if ((result and 0x10000000) == 0) return result + result = (result and 0xfffffff) or (readU8() shl 28) + return result +} + +private fun InputStream.readBytes(size: Int): ByteArray { + return readNBytes(size) +} + +private fun OutputStream.writeU_VL(v: Int) { + var value = v + while (true) { + val c = value and 0x7f + value = value ushr 7 + if (value == 0) { + write(c) + break + } + write(c or 0x80) + } +} + +fun IPCPacket.Companion.genericEventGen(kind: String, eventData: ByteArray): ByteArray = ByteArrayOutputStream(16 + kind.length + 4 + eventData.size + 4).apply { val kindBytes = kind.encodeToByteArray() writeU_VL(kindBytes.size); writeBytes(kindBytes) writeU_VL(eventData.size); writeBytes(eventData) -} +}.toByteArray() fun IPCPacket.Companion.genericEventParse(data: ByteArray): Pair { - val s = data.openFastStream() + val s = ByteArrayInputStream(data) val type = s.readBytes(s.readU_VL()).decodeToString() val data = s.readBytes(s.readU_VL()) return type to data @@ -280,8 +314,8 @@ fun IPCPacket.Companion.keyPacket(type: Int, keyCode: Int, char: Int): IPCPacket fun IPCPacket.Companion.mousePacket( type: Int, x: Int, y: Int, button: Int, scrollX: Float = 0f, scrollY: Float = 0f, scrollZ: Float = 0f, -): IPCPacket = packetInts(type, x, y, button, scrollX.reinterpretAsInt(), scrollY.reinterpretAsInt(), scrollZ.reinterpretAsInt()) -fun IPCPacket.Companion.resizePacket(type: Int, width: Int, height: Int, scale: Float = 1f): IPCPacket = packetInts(type, width, height, scale.reinterpretAsInt()) +): IPCPacket = packetInts(type, x, y, button, scrollX.toRawBits(), scrollY.toRawBits(), scrollZ.toRawBits()) +fun IPCPacket.Companion.resizePacket(type: Int, width: Int, height: Int, scale: Float = 1f): IPCPacket = packetInts(type, width, height, scale.toRawBits()) fun IPCPacket.Companion.nodePacket(type: Int, nodeId: Int): IPCPacket = packetInts(type, nodeId) fun IPCPacket.Companion.requestNodeChildrenPacket(nodeId: Int): IPCPacket = nodePacket(IPCPacket.REQUEST_NODE_CHILDREN, nodeId)