Unverified Commit 94ef0533 authored by Nikolay Igotti's avatar Nikolay Igotti Committed by GitHub

JVM SkiaLayer API (#281)

parent 756df5ab
...@@ -31,13 +31,40 @@ i.e. something like this ...@@ -31,13 +31,40 @@ i.e. something like this
else -> error("Unsupported arch: $osArch") else -> error("Unsupported arch: $osArch")
} }
val version = "0.4.12" val version = "0.5.2"
val target = "${targetOs}-${targetArch}" val target = "${targetOs}-${targetArch}"
dependencies { dependencies {
implementation("org.jetbrains.skiko:skiko-jvm-runtime-$target:$version") implementation("org.jetbrains.skiko:skiko-jvm-runtime-$target:$version")
} }
``` ```
Simple example for Kotlin/JVM
```kotlin
fun main() {
val skiaLayer = SkiaLayer()
skiaLayer.skikoView = GenericSkikoView(skiaLayer, object : SkikoView {
val paint = Paint().apply {
color = Color.RED
}
override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
canvas.clear(Color.CYAN)
val ts = nanoTime / 5_000_000
canvas.drawCircle( (ts % width).toFloat(), (ts % height).toFloat(), 20f, paint )
}
})
SwingUtilities.invokeLater {
val window = JFrame("Skiko example").apply {
defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE
preferredSize = Dimension(800, 600)
}
skiaLayer.attachTo(window.contentPane)
skiaLayer.needRedraw()
window.pack()
window.isVisible = true
}
}
```
To use latest development snapshot use version `0.0.0-SNAPSHOT`. To use latest development snapshot use version `0.0.0-SNAPSHOT`.
## Building JVM bindings ## Building JVM bindings
......
plugins { plugins {
kotlin("multiplatform") version "1.5.31" kotlin("multiplatform") version "1.6.0-RC"
} }
val coroutinesVersion = "1.5.2" val coroutinesVersion = "1.5.2"
...@@ -60,6 +60,12 @@ kotlin { ...@@ -60,6 +60,12 @@ kotlin {
targets.add(iosArm64()) targets.add(iosArm64())
} }
jvm {
compilations.all {
kotlinOptions.jvmTarget = "11"
}
}
js(IR) { js(IR) {
browser() browser()
binaries.executable() binaries.executable()
...@@ -92,6 +98,13 @@ kotlin { ...@@ -92,6 +98,13 @@ kotlin {
dependsOn(commonMain) dependsOn(commonMain)
} }
val jvmMain by getting {
dependsOn(commonMain)
dependencies {
implementation("org.jetbrains.skiko:skiko-jvm-runtime-$hostOs-$hostArch:$version")
}
}
val jsMain by getting { val jsMain by getting {
dependsOn(commonMain) dependsOn(commonMain)
resources.setSrcDirs(resources.srcDirs) resources.setSrcDirs(resources.srcDirs)
...@@ -150,7 +163,7 @@ project.tasks.register<Exec>("runIosSim") { ...@@ -150,7 +163,7 @@ project.tasks.register<Exec>("runIosSim") {
} }
} }
project.tasks.register<Exec>("run") { project.tasks.register<Exec>("runNative") {
workingDir = project.buildDir workingDir = project.buildDir
val binTask = project.tasks.named("linkDebugExecutable${hostOs.capitalize()}${hostArch.capitalize()}") val binTask = project.tasks.named("linkDebugExecutable${hostOs.capitalize()}${hostArch.capitalize()}")
dependsOn(binTask) dependsOn(binTask)
...@@ -163,6 +176,26 @@ project.tasks.register<Exec>("run") { ...@@ -163,6 +176,26 @@ project.tasks.register<Exec>("run") {
} }
} }
project.tasks.register<JavaExec>("runJvm") {
val kotlinTask = project.tasks.named("compileKotlinJvm")
dependsOn(kotlinTask)
systemProperty("skiko.fps.enabled", "true")
systemProperty("skiko.linux.autodpi", "true")
systemProperty("skiko.hardwareInfo.enabled", "true")
systemProperty("skiko.win.exception.logger.enabled", "true")
systemProperty("skiko.win.exception.handler.enabled", "true")
jvmArgs?.add("-ea")
System.getProperties().entries
.associate {
(it.key as String) to (it.value as String)
}
.filterKeys { it.startsWith("skiko.") }
.forEach { systemProperty(it.key, it.value) }
mainClass.set("org.jetbrains.skiko.sample.App_jvmKt")
classpath(kotlinTask.get().outputs)
classpath(kotlin.jvm().compilations["main"].runtimeDependencyFiles)
}
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile>().configureEach { tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile>().configureEach {
dependsOn(unzipTask) dependsOn(unzipTask)
} }
......
...@@ -84,9 +84,10 @@ class BouncingBalls: SkikoView { ...@@ -84,9 +84,10 @@ class BouncingBalls: SkikoView {
Color4f(0f, 1f, 1f, 0.8f).asPaint() Color4f(0f, 1f, 1f, 0.8f).asPaint()
)).toMutableList() )).toMutableList()
override fun onRender(canvas: Canvas, width: Int, height: Int, currentTimestamp: Long) { override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
val dtime = (currentTimestamp - prevTimestamp) canvas.clear(-1)
prevTimestamp = currentTimestamp val dtime = (nanoTime - prevTimestamp)
prevTimestamp = nanoTime
data.forEach { (ball, paint) -> data.forEach { (ball, paint) ->
ball.recalculate(width, height, dtime.toFloat()) ball.recalculate(width, height, dtime.toFloat())
...@@ -112,7 +113,7 @@ class BouncingBalls: SkikoView { ...@@ -112,7 +113,7 @@ class BouncingBalls: SkikoView {
data.removeLast() data.removeLast()
} }
// To avoid log spamming // To avoid log spamming
//if (event.kind != SkikoMouseEventKind.MOVE) if (event.kind != SkikoPointerEventKind.MOVE)
println("onMouse: $event") println("onMouse: $event")
} }
} }
\ No newline at end of file
...@@ -13,7 +13,7 @@ fun main() { ...@@ -13,7 +13,7 @@ fun main() {
skiaLayer.skikoView = GenericSkikoView(skiaLayer, game) skiaLayer.skikoView = GenericSkikoView(skiaLayer, game)
val canvas = document.getElementById("SkikoTarget") as HTMLCanvasElement val canvas = document.getElementById("SkikoTarget") as HTMLCanvasElement
canvas.setAttribute("tabindex", "0") canvas.setAttribute("tabindex", "0")
skiaLayer.setCanvas(canvas) skiaLayer.attachTo(canvas)
skiaLayer.needRedraw() skiaLayer.needRedraw()
} }
} }
package org.jetbrains.skiko.sample
import org.jetbrains.skiko.*
import java.awt.Dimension
import javax.swing.*
fun main() {
val skiaLayer = SkiaLayer()
skiaLayer.skikoView = GenericSkikoView(skiaLayer, BouncingBalls())
SwingUtilities.invokeLater {
val window = JFrame("Skiko example").apply {
defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE
preferredSize = Dimension(800, 600)
}
skiaLayer.attachTo(window.contentPane)
skiaLayer.needRedraw()
window.pack()
window.isVisible = true
}
}
...@@ -4,7 +4,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile ...@@ -4,7 +4,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile
plugins { plugins {
kotlin("multiplatform") version "1.5.31" // "1.6.0-M1" kotlin("multiplatform") version "1.5.31"
`maven-publish` `maven-publish`
id("org.gradle.crypto.checksum") version "1.1.0" id("org.gradle.crypto.checksum") version "1.1.0"
id("de.undercouch.download") version "4.1.1" id("de.undercouch.download") version "4.1.1"
...@@ -202,8 +202,13 @@ val linkWasm = tasks.register<LinkSkikoWasmTask>("linkWasm") { ...@@ -202,8 +202,13 @@ val linkWasm = tasks.register<LinkSkikoWasmTask>("linkWasm") {
val skiaBinSubdir = "out/${buildType.id}-${targetOs.id}-${targetArch.id}" val skiaBinSubdir = "out/${buildType.id}-${targetOs.id}-${targetArch.id}"
internal val Project.isInIdea: Boolean
get() {
return System.getProperty("idea.active")?.toBoolean() == true
}
val Project.supportNative: Boolean val Project.supportNative: Boolean
get() = properties.get("skiko.native.enabled") == "true" get() = (properties.get("skiko.native.enabled") == "true") || isInIdea
val Project.supportWasm: Boolean val Project.supportWasm: Boolean
get() = properties.get("skiko.wasm.enabled") == "true" get() = properties.get("skiko.wasm.enabled") == "true"
......
...@@ -470,7 +470,6 @@ SKIKO_EXPORT KInteropPointer org_jetbrains_skia_Font__1nGetMetrics ...@@ -470,7 +470,6 @@ SKIKO_EXPORT KInteropPointer org_jetbrains_skia_Font__1nGetMetrics
#endif #endif
SKIKO_EXPORT KFloat org_jetbrains_skia_Font__1nGetSpacing SKIKO_EXPORT KFloat org_jetbrains_skia_Font__1nGetSpacing
(KNativePointer ptr, KShort* glyphsArr) { (KNativePointer ptr, KShort* glyphsArr) {
TODO("implement org_jetbrains_skia_Font__1nGetSpacing"); TODO("implement org_jetbrains_skia_Font__1nGetSpacing");
......
...@@ -10,7 +10,10 @@ expect open class SkiaLayer { ...@@ -10,7 +10,10 @@ expect open class SkiaLayer {
var skikoView: SkikoView? var skikoView: SkikoView?
// Actual type of attach container is platform-specific.
fun attachTo(container: Any)
fun needRedraw() fun needRedraw()
} }
internal class PictureHolder(val instance: Picture, val width: Int, val height: Int) internal class PictureHolder(val instance: Picture, val width: Int, val height: Int)
...@@ -49,9 +49,11 @@ actual open class SkiaLayer( ...@@ -49,9 +49,11 @@ actual open class SkiaLayer(
lateinit var view: UIView lateinit var view: UIView
// We need to keep reference to controller as Objective-C will only keep weak reference here. // We need to keep reference to controller as Objective-C will only keep weak reference here.
lateinit private var controller: NSObject lateinit private var controller: NSObject
actual fun attachTo(container: Any) {
fun initLayer(viewController: SkikoViewController) { attachTo(container as UIView)
this.view = viewController.view }
fun attachTo(view: UIView) {
this.view = view
// See https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/using_responders_and_the_responder_chain_to_handle_events?language=objc // See https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/using_responders_and_the_responder_chain_to_handle_events?language=objc
controller = object : NSObject() { controller = object : NSObject() {
......
...@@ -42,6 +42,6 @@ class SkikoViewController : UIViewController { ...@@ -42,6 +42,6 @@ class SkikoViewController : UIViewController {
skikoView = appFactory(this) skikoView = appFactory(this)
} }
view.setFrame(CGRectMake(0.0, 0.0, width, height)) view.setFrame(CGRectMake(0.0, 0.0, width, height))
layer.initLayer(this) layer.attachTo(this.view)
} }
} }
...@@ -47,7 +47,11 @@ actual open class SkiaLayer(properties: SkiaLayerProperties = makeDefaultSkiaLay ...@@ -47,7 +47,11 @@ actual open class SkiaLayer(properties: SkiaLayerProperties = makeDefaultSkiaLay
) )
} }
fun setCanvas(htmlCanvas: HTMLCanvasElement) { actual fun attachTo(container: Any) {
attachTo(container as HTMLCanvasElement)
}
fun attachTo(htmlCanvas: HTMLCanvasElement) {
state = object: CanvasRenderer(htmlCanvas) { state = object: CanvasRenderer(htmlCanvas) {
override fun drawFrame(currentTimestamp: Double) { override fun drawFrame(currentTimestamp: Double) {
// currentTimestamp is in milliseconds. // currentTimestamp is in milliseconds.
......
...@@ -19,6 +19,7 @@ import java.awt.event.* ...@@ -19,6 +19,7 @@ import java.awt.event.*
import java.awt.im.InputMethodRequests import java.awt.im.InputMethodRequests
import java.util.concurrent.CancellationException import java.util.concurrent.CancellationException
import javax.accessibility.Accessible import javax.accessibility.Accessible
import javax.swing.JComponent
import javax.swing.JPanel import javax.swing.JPanel
import javax.swing.SwingUtilities.isEventDispatchThread import javax.swing.SwingUtilities.isEventDispatchThread
...@@ -124,6 +125,69 @@ actual open class SkiaLayer internal constructor( ...@@ -124,6 +125,69 @@ actual open class SkiaLayer internal constructor(
actual var skikoView: SkikoView? = null actual var skikoView: SkikoView? = null
actual fun attachTo(container: Any) {
attachTo(container as JComponent)
}
fun attachTo(jComponent: JComponent) {
jComponent.add(this)
backedLayer.addMouseListener(object : MouseAdapter() {
override fun mousePressed(e: MouseEvent?) {
e!!
skikoView?.onPointerEvent(
SkikoPointerEvent(e.x.toDouble(), e.y.toDouble(),
if (e.button == 1) SkikoMouseButtons.LEFT else SkikoMouseButtons.RIGHT,
SkikoPointerEventKind.DOWN,
e
)
)
}
override fun mouseReleased(e: MouseEvent?) {
e!!
skikoView?.onPointerEvent(
SkikoPointerEvent(e.x.toDouble(), e.y.toDouble(),
if (e.button == 1) SkikoMouseButtons.LEFT else SkikoMouseButtons.RIGHT,
SkikoPointerEventKind.UP,
e
)
)
}
})
backedLayer.addMouseMotionListener(object : MouseMotionAdapter() {
override fun mouseMoved(e: MouseEvent?) {
e!!
skikoView?.onPointerEvent(
SkikoPointerEvent(e.x.toDouble(), e.y.toDouble(),
SkikoMouseButtons.NONE,
SkikoPointerEventKind.MOVE,
e
)
)
}
})
backedLayer.addKeyListener(object : KeyAdapter() {
override fun keyPressed(e: KeyEvent?) {
e!!
skikoView?.onKeyboardEvent(
SkikoKeyboardEvent(e.keyCode,
SkikoKeyboardEventKind.DOWN,
e
)
)
}
override fun keyReleased(e: KeyEvent?) {
e!!
skikoView?.onKeyboardEvent(
SkikoKeyboardEvent(
e.keyCode,
SkikoKeyboardEventKind.UP,
e
)
)
}
})
}
val clipComponents = mutableListOf<ClipRectangle>() val clipComponents = mutableListOf<ClipRectangle>()
@Volatile @Volatile
......
...@@ -2,6 +2,7 @@ package org.jetbrains.skiko ...@@ -2,6 +2,7 @@ package org.jetbrains.skiko
import javax.swing.JFrame import javax.swing.JFrame
@Deprecated("Will be removed soon")
open class SkiaWindow( open class SkiaWindow(
properties: SkiaLayerProperties = makeDefaultSkiaLayerProperties(), properties: SkiaLayerProperties = makeDefaultSkiaLayerProperties(),
layerFactory: () -> SkiaLayer = { SkiaLayer(properties) } layerFactory: () -> SkiaLayer = { SkiaLayer(properties) }
...@@ -24,6 +25,12 @@ open class SkiaWindow( ...@@ -24,6 +25,12 @@ open class SkiaWindow(
} }
} }
fun SkiaLayer.disableTitleBar() {
backedLayer.useDrawingSurfacePlatformInfo {
platformOperations.disableTitleBar(it)
}
}
fun orderEmojiAndSymbolsPopup() { fun orderEmojiAndSymbolsPopup() {
platformOperations.orderEmojiAndSymbolsPopup() platformOperations.orderEmojiAndSymbolsPopup()
} }
\ No newline at end of file
...@@ -15,6 +15,9 @@ actual open class SkiaLayer(properties: SkiaLayerProperties) { ...@@ -15,6 +15,9 @@ actual open class SkiaLayer(properties: SkiaLayerProperties) {
actual fun needRedraw() { actual fun needRedraw() {
TODO("unimplemented") TODO("unimplemented")
} }
actual fun attachTo(container: Any) {
TODO("unimplemented")
}
actual var skikoView: SkikoView? = null actual var skikoView: SkikoView? = null
} }
......
...@@ -60,8 +60,10 @@ actual open class SkiaLayer( ...@@ -60,8 +60,10 @@ actual open class SkiaLayer(
private fun eventToKeyboard(event: NSEvent, kind: SkikoKeyboardEventKind): SkikoKeyboardEvent { private fun eventToKeyboard(event: NSEvent, kind: SkikoKeyboardEventKind): SkikoKeyboardEvent {
return SkikoKeyboardEvent(event.keyCode.toInt(), kind, event) return SkikoKeyboardEvent(event.keyCode.toInt(), kind, event)
} }
actual fun attachTo(container: Any) {
fun initLayer(window: NSWindow) { attachTo(container as NSWindow)
}
fun attachTo(window: NSWindow) {
val (width, height) = window.contentLayoutRect.useContents { val (width, height) = window.contentLayoutRect.useContents {
this.size.width to this.size.height this.size.width to this.size.height
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment