Unverified Commit 9383b5d9 authored by Nikolay Igotti's avatar Nikolay Igotti Committed by GitHub

Input handling (#276)

parent 7d15187d
...@@ -2,10 +2,9 @@ package org.jetbrains.skiko.sample.js ...@@ -2,10 +2,9 @@ package org.jetbrains.skiko.sample.js
import kotlinx.browser.document import kotlinx.browser.document
import org.jetbrains.skia.Canvas import org.jetbrains.skia.Canvas
import org.jetbrains.skia.Color4f
import org.jetbrains.skia.Paint import org.jetbrains.skia.Paint
import org.jetbrains.skia.Rect import org.jetbrains.skia.Rect
import org.jetbrains.skiko.GenericRenderer import org.jetbrains.skiko.GenericSkikoApp
import org.jetbrains.skiko.SkiaLayer import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.SkiaRenderer import org.jetbrains.skiko.SkiaRenderer
import org.jetbrains.skiko.wasm.onWasmReady import org.jetbrains.skiko.wasm.onWasmReady
...@@ -34,7 +33,7 @@ fun main() { ...@@ -34,7 +33,7 @@ fun main() {
} else { } else {
BouncingBalls() BouncingBalls()
} }
skiaLayer.renderer = GenericRenderer(skiaLayer, app) skiaLayer.renderer = GenericSkikoApp(skiaLayer, app)
skiaLayer.setCanvas(canvas) skiaLayer.setCanvas(canvas)
skiaLayer.draw() skiaLayer.draw()
} }
......
package org.jetbrains.skiko.sample package org.jetbrains.skiko.sample
import org.jetbrains.skia.* import org.jetbrains.skia.*
import org.jetbrains.skiko.SkiaRenderer import org.jetbrains.skiko.*
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.cos import kotlin.math.cos
import kotlin.math.sin import kotlin.math.sin
class BouncingBalls: SkiaRenderer { class BouncingBalls: SkikoView {
private data class Circle(var x: Float, var y: Float, var r: Float) private data class Circle(var x: Float, var y: Float, var r: Float)
companion object { companion object {
...@@ -82,7 +82,7 @@ class BouncingBalls: SkiaRenderer { ...@@ -82,7 +82,7 @@ class BouncingBalls: SkiaRenderer {
Color4f(1f, 0f, 0f, 0.8f).asPaint(), Color4f(1f, 0f, 0f, 0.8f).asPaint(),
Color4f(1f, 0f, 1f, 0.8f).asPaint(), Color4f(1f, 0f, 1f, 0.8f).asPaint(),
Color4f(0f, 1f, 1f, 0.8f).asPaint() Color4f(0f, 1f, 1f, 0.8f).asPaint()
)) )).toMutableList()
override fun onRender(canvas: Canvas, width: Int, height: Int, currentTimestamp: Long) { override fun onRender(canvas: Canvas, width: Int, height: Int, currentTimestamp: Long) {
val dtime = (currentTimestamp - prevTimestamp) val dtime = (currentTimestamp - prevTimestamp)
...@@ -93,4 +93,26 @@ class BouncingBalls: SkiaRenderer { ...@@ -93,4 +93,26 @@ class BouncingBalls: SkiaRenderer {
canvas.drawCircle(ball.circle.x, ball.circle.y, ball.circle.r, paint) canvas.drawCircle(ball.circle.x, ball.circle.y, ball.circle.r, paint)
} }
} }
override fun onInputEvent(event: SkikoInputEvent) {
println("onInput: $event")
}
override fun onKeyboardEvent(event: SkikoKeyboardEvent) {
println("onKeyboard: $event")
}
override fun onPointerEvent(event: SkikoPointerEvent) {
if (event.isLeftClick) {
val paint = Color4f(1f, 0f, 0f, 0.8f).asPaint()
data += BouncingBall(
Circle(event.x.toFloat(), event.y.toFloat(), 25f), 172f, PI / 4) to paint
}
if (event.isRightClick && data.size > 0) {
data.removeLast()
}
// To avoid log spamming
//if (event.kind != SkikoMouseEventKind.MOVE)
println("onMouse: $event")
}
} }
\ No newline at end of file
package org.jetbrains.skiko.sample package org.jetbrains.skiko.sample
import org.jetbrains.skia.* import org.jetbrains.skia.*
import org.jetbrains.skiko.SkiaRenderer import org.jetbrains.skiko.*
class RotatingSquare : SkiaRenderer { class RotatingSquare : SkikoView {
override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) { override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
val angleDeg = (nanoTime / 5_000_000) % 360 val angleDeg = (nanoTime / 5_000_000) % 360
val paint = Paint().apply { color = Color.GREEN } val paint = Paint().apply { color = Color.GREEN }
...@@ -15,4 +15,16 @@ class RotatingSquare : SkiaRenderer { ...@@ -15,4 +15,16 @@ class RotatingSquare : SkiaRenderer {
canvas.drawRect(rect, paint) canvas.drawRect(rect, paint)
canvas.restore() canvas.restore()
} }
}
override fun onInputEvent(event: SkikoInputEvent) {
println("onInput: $event")
}
override fun onKeyboardEvent(event: SkikoKeyboardEvent) {
println("onKeyboard: $event")
}
override fun onPointerEvent(event: SkikoPointerEvent) {
println("onMouse: $event")
}
}
\ No newline at end of file
...@@ -2,11 +2,13 @@ ...@@ -2,11 +2,13 @@
package org.jetbrains.skiko.sample package org.jetbrains.skiko.sample
import kotlinx.cinterop.* import kotlinx.cinterop.*
import org.jetbrains.skiko.SkiaRenderer import org.jetbrains.skiko.GenericSkikoView
import org.jetbrains.skiko.SkikoView
import org.jetbrains.skiko.SkikoViewController
import platform.UIKit.* import platform.UIKit.*
import platform.Foundation.* import platform.Foundation.*
fun makeApp(): SkiaRenderer = BouncingBalls() fun makeApp(): SkikoView = BouncingBalls()
fun main() { fun main() {
val args = emptyArray<String>() val args = emptyArray<String>()
...@@ -19,3 +21,28 @@ fun main() { ...@@ -19,3 +21,28 @@ fun main() {
} }
} }
class SkikoAppDelegate : UIResponder, UIApplicationDelegateProtocol {
companion object : UIResponderMeta(), UIApplicationDelegateProtocolMeta
@ObjCObjectBase.OverrideInit
constructor() : super()
private var _window: UIWindow? = null
override fun window() = _window
override fun setWindow(window: UIWindow?) {
_window = window
}
override fun application(application: UIApplication, didFinishLaunchingWithOptions: Map<Any?, *>?): Boolean {
window = UIWindow(frame = UIScreen.mainScreen.bounds)
window!!.rootViewController = SkikoViewController().apply {
setAppFactory { layer ->
GenericSkikoView(layer, makeApp()).also {
layer.skikoView = it
}
}
}
window!!.makeKeyAndVisible()
return true
}
}
package org.jetbrains.skiko.sample
import kotlinx.cinterop.*
import platform.UIKit.*
class SkikoAppDelegate : UIResponder, UIApplicationDelegateProtocol {
companion object : UIResponderMeta(), UIApplicationDelegateProtocolMeta
@ObjCObjectBase.OverrideInit
constructor() : super()
private var _window: UIWindow? = null
override fun window() = _window
override fun setWindow(window: UIWindow?) {
_window = window
}
override fun application(application: UIApplication, didFinishLaunchingWithOptions: Map<Any?, *>?): Boolean {
window = UIWindow(frame = UIScreen.mainScreen.bounds)
window!!.rootViewController = SkikoViewController()
window!!.makeKeyAndVisible()
return true
}
}
\ No newline at end of file
package org.jetbrains.skiko.sample package org.jetbrains.skiko.sample
import kotlinx.browser.document import kotlinx.browser.document
import org.jetbrains.skiko.GenericRenderer import org.jetbrains.skiko.GenericSkikoView
import org.jetbrains.skiko.SkiaLayer import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.wasm.onWasmReady import org.jetbrains.skiko.wasm.onWasmReady
import org.w3c.dom.HTMLCanvasElement import org.w3c.dom.HTMLCanvasElement
...@@ -9,9 +9,11 @@ import org.w3c.dom.HTMLCanvasElement ...@@ -9,9 +9,11 @@ import org.w3c.dom.HTMLCanvasElement
fun main() { fun main() {
onWasmReady { onWasmReady {
val skiaLayer = SkiaLayer() val skiaLayer = SkiaLayer()
skiaLayer.renderer = GenericRenderer(skiaLayer, RotatingSquare()) val game = BouncingBalls()
skiaLayer.skikoView = GenericSkikoView(skiaLayer, game)
val canvas = document.getElementById("SkikoTarget") as HTMLCanvasElement val canvas = document.getElementById("SkikoTarget") as HTMLCanvasElement
canvas.setAttribute("tabindex", "0")
skiaLayer.setCanvas(canvas) skiaLayer.setCanvas(canvas)
skiaLayer.draw() skiaLayer.needRedraw()
} }
} }
...@@ -5,14 +5,24 @@ import platform.AppKit.* ...@@ -5,14 +5,24 @@ import platform.AppKit.*
import org.jetbrains.skia.* import org.jetbrains.skia.*
import org.jetbrains.skiko.* import org.jetbrains.skiko.*
import kotlinx.cinterop.* import kotlinx.cinterop.*
import platform.Foundation.NSMakeRect
fun makeApp() = BouncingBalls() fun makeApp() = BouncingBalls()
fun main() { fun main() {
NSApplication.sharedApplication() NSApplication.sharedApplication()
val layer = SkiaLayer() val windowStyle = NSWindowStyleMaskTitled or
val window = SkiaWindow(layer) NSWindowStyleMaskMiniaturizable or
layer.renderer = GenericRenderer(layer, makeApp()) NSWindowStyleMaskClosable or
window.nsWindow.orderFrontRegardless() NSWindowStyleMaskResizable
val window = NSWindow(
contentRect = NSMakeRect(0.0, 0.0, 640.0, 480.0),
styleMask = windowStyle,
backing = NSBackingStoreBuffered,
defer = true)
val skiaLayer = SkiaLayer()
skiaLayer.skikoView = GenericSkikoView(skiaLayer, makeApp())
skiaLayer.initLayer(window)
window.orderFrontRegardless()
NSApp?.run() NSApp?.run()
} }
...@@ -1071,4 +1071,4 @@ fun registerOrGetSkiaDirProvider(os: OS, arch: Arch): Provider<File> { ...@@ -1071,4 +1071,4 @@ fun registerOrGetSkiaDirProvider(os: OS, arch: Arch): Provider<File> {
into(skiko.dependenciesDir.resolve("skia/$skiaRelease")) into(skiko.dependenciesDir.resolve("skia/$skiaRelease"))
}.map { it.destinationDir.absoluteFile } }.map { it.destinationDir.absoluteFile }
} }
} }
\ No newline at end of file
package org.jetbrains.skiko
// Clicked mouse buttons bitmask.
object SkikoMouseButtons {
const val NONE = 0
const val LEFT = 1 shl 0
const val RIGHT = 1 shl 1
const val MIDDLE = 1 shl 2
}
expect class SkikoPlatformInputEvent
data class SkikoInputEvent(
val input: String,
val platform: SkikoPlatformInputEvent?
)
enum class SkikoKeyboardEventKind {
UP, DOWN
}
expect class SkikoPlatformKeyboardEvent
data class SkikoKeyboardEvent(
val code: Int,
val kind: SkikoKeyboardEventKind,
val platform: SkikoPlatformKeyboardEvent?
)
enum class SkikoPointerEventKind {
UP, DOWN, MOVE
}
expect class SkikoPlatformPointerEvent
data class SkikoPointerEvent(
val x: Double, val y: Double,
val buttonMask: Int,
val kind: SkikoPointerEventKind,
val platform: SkikoPlatformPointerEvent?
)
val SkikoPointerEvent.isLeftClick: Boolean
get() = (buttonMask and SkikoMouseButtons.LEFT) != 0 && (kind == SkikoPointerEventKind.UP)
val SkikoPointerEvent.isRightClick: Boolean
get() = (buttonMask and SkikoMouseButtons.RIGHT) != 0 && (kind == SkikoPointerEventKind.UP)
package org.jetbrains.skiko package org.jetbrains.skiko
import org.jetbrains.skia.Canvas
import org.jetbrains.skia.Picture import org.jetbrains.skia.Picture
expect open class SkiaLayer { expect open class SkiaLayer {
...@@ -9,25 +8,9 @@ expect open class SkiaLayer { ...@@ -9,25 +8,9 @@ expect open class SkiaLayer {
var fullscreen: Boolean var fullscreen: Boolean
var transparency: Boolean var transparency: Boolean
var renderer: SkiaRenderer? var skikoView: SkikoView?
fun needRedraw() fun needRedraw()
} }
interface SkiaRenderer {
fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long)
}
open class GenericRenderer(
val layer: SkiaLayer,
val app: SkiaRenderer
): SkiaRenderer {
override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
val contentScale = layer.contentScale
canvas.scale(contentScale, contentScale)
app.onRender(canvas, (width / contentScale).toInt(), (height / contentScale).toInt(), nanoTime)
layer.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)
package org.jetbrains.skiko
import org.jetbrains.skia.Canvas
interface SkikoView {
// Input
fun onKeyboardEvent(event: SkikoKeyboardEvent)
fun onPointerEvent(event: SkikoPointerEvent)
fun onInputEvent(event: SkikoInputEvent)
// Rendering
fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long)
}
open class GenericSkikoView(
val layer: SkiaLayer,
val app: SkikoView
): SkikoView {
override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
val contentScale = layer.contentScale
canvas.scale(contentScale, contentScale)
app.onRender(canvas, (width / contentScale).toInt(), (height / contentScale).toInt(), nanoTime)
// Request next frame immediately.
layer.needRedraw()
}
override fun onInputEvent(event: SkikoInputEvent) {
app.onInputEvent(event)
}
override fun onKeyboardEvent(event: SkikoKeyboardEvent) {
app.onKeyboardEvent(event)
}
override fun onPointerEvent(event: SkikoPointerEvent) {
app.onPointerEvent(event)
}
}
abstract class NoInputSkikoView: SkikoView {
override fun onInputEvent(event: SkikoInputEvent) {
}
override fun onKeyboardEvent(event: SkikoKeyboardEvent) {
}
override fun onPointerEvent(event: SkikoPointerEvent) {
}
}
package org.jetbrains.skiko.sample package org.jetbrains.skiko
import kotlinx.cinterop.* import kotlinx.cinterop.ExportObjCClass
import kotlinx.coroutines.runBlocking import kotlinx.cinterop.useContents
import org.jetbrains.skiko.GenericRenderer
import org.jetbrains.skiko.SkiaLayer
import platform.CoreGraphics.CGRect
import platform.CoreGraphics.CGRectMake import platform.CoreGraphics.CGRectMake
import platform.Foundation.*
import platform.UIKit.*
import platform.Foundation.NSCoder import platform.Foundation.NSCoder
import platform.UIKit.UIEvent
import platform.UIKit.UIScreen
import platform.UIKit.UIViewController
import platform.UIKit.setFrame
@ExportObjCClass @ExportObjCClass
class SkikoViewController : UIViewController { class SkikoViewController : UIViewController {
@OverrideInit @OverrideInit
constructor() : super(nibName = null, bundle = null) constructor() : super(nibName = null, bundle = null)
@OverrideInit @OverrideInit
constructor(coder: NSCoder) : super(coder) constructor(coder: NSCoder) : super(coder)
override fun touchesBegan(touches: Set<*>, withEvent: UIEvent?) {
println("touchesBegan: $withEvent")
super.touchesBegan(touches, withEvent)
}
override fun touchesEnded(touches: Set<*>, withEvent: UIEvent?) {
println("touchesEnded $withEvent")
super.touchesEnded(touches, withEvent)
}
internal lateinit var appFactory: (SkiaLayer) -> SkikoView
fun setAppFactory(appFactory: (SkiaLayer) -> SkikoView) {
this.appFactory = appFactory
}
override fun viewDidLoad() { override fun viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
val (width, height) = UIScreen.mainScreen.bounds.useContents { val (width, height) = UIScreen.mainScreen.bounds.useContents {
this.size.width to this.size.height this.size.width to this.size.height
} }
val layer = SkiaLayer().apply {
skikoView = appFactory(this)
}
view.setFrame(CGRectMake(0.0, 0.0, width, height)) view.setFrame(CGRectMake(0.0, 0.0, width, height))
val layer = SkiaLayer(width.toFloat(), height.toFloat()) layer.initLayer(this)
layer.renderer = GenericRenderer(layer, makeApp())
layer.initLayer(view)
} }
} }
package org.jetbrains.skiko package org.jetbrains.skiko
import kotlinx.cinterop.ObjCAction
import kotlinx.cinterop.useContents
import org.jetbrains.skia.PictureRecorder import org.jetbrains.skia.PictureRecorder
import org.jetbrains.skia.Rect import org.jetbrains.skia.Rect
import org.jetbrains.skiko.context.MetalContextHandler import org.jetbrains.skiko.context.MetalContextHandler
import org.jetbrains.skiko.redrawer.MetalRedrawer import org.jetbrains.skiko.redrawer.MetalRedrawer
import platform.Foundation.NSSelectorFromString
import platform.UIKit.* import platform.UIKit.*
import platform.darwin.NSObject
actual open class SkiaLayer( actual open class SkiaLayer(
var width: Float, var height: Float,
val properties: SkiaLayerProperties = makeDefaultSkiaLayerProperties() val properties: SkiaLayerProperties = makeDefaultSkiaLayerProperties()
) ) {
{
fun isShowing(): Boolean { fun isShowing(): Boolean {
return true return true
} }
...@@ -34,16 +36,50 @@ actual open class SkiaLayer( ...@@ -34,16 +36,50 @@ actual open class SkiaLayer(
redrawer?.needRedraw() redrawer?.needRedraw()
} }
val width: Float
get() = view.frame.useContents {
return@useContents size.width.toFloat()
}
val height: Float
get() = view.frame.useContents {
return@useContents size.height.toFloat()
}
lateinit var view: UIView lateinit var view: UIView
// We need to keep reference to controller as Objective-C will only keep weak reference here.
lateinit private var controller: NSObject
fun initLayer(view: UIView) { fun initLayer(viewController: SkikoViewController) {
println("SkiaLayer.initLayer") this.view = viewController.view
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
controller = object : NSObject() {
@ObjCAction
fun onTap(sender: UITapGestureRecognizer) {
val (x, y) = sender.locationInView(view).useContents {
x to y
}
// TODO: rework events using https://developer.apple.com/documentation/uikit/uiresponder/1621142-touchesbegan?language=objc
skikoView?.onPointerEvent(
SkikoPointerEvent(x, y,
SkikoMouseButtons.LEFT, SkikoPointerEventKind.DOWN,
null))
skikoView?.onPointerEvent(
SkikoPointerEvent(x, y,
SkikoMouseButtons.LEFT, SkikoPointerEventKind.UP,
null))
}
}
// We have ':' in selector to take care of function argument.
view.addGestureRecognizer(UITapGestureRecognizer(controller, NSSelectorFromString("onTap:")))
redrawer = MetalRedrawer(this, properties) redrawer = MetalRedrawer(this, properties)
redrawer?.redrawImmediately() redrawer?.redrawImmediately()
} }
actual var renderer: SkiaRenderer? = null actual var skikoView: SkikoView? = null
internal var redrawer: MetalRedrawer? = null internal var redrawer: MetalRedrawer? = null
private var picture: PictureHolder? = null private var picture: PictureHolder? = null
private val pictureRecorder = PictureRecorder() private val pictureRecorder = PictureRecorder()
...@@ -55,7 +91,7 @@ actual open class SkiaLayer( ...@@ -55,7 +91,7 @@ actual open class SkiaLayer(
val bounds = Rect.makeWH(pictureWidth, pictureHeight) val bounds = Rect.makeWH(pictureWidth, pictureHeight)
val canvas = pictureRecorder.beginRecording(bounds) val canvas = pictureRecorder.beginRecording(bounds)
renderer?.onRender(canvas, pictureWidth.toInt(), pictureHeight.toInt(), nanoTime) skikoView?.onRender(canvas, pictureWidth.toInt(), pictureHeight.toInt(), nanoTime)
val picture = pictureRecorder.finishRecordingAsPicture() val picture = pictureRecorder.finishRecordingAsPicture()
this.picture = PictureHolder(picture, pictureWidth.toInt(), pictureHeight.toInt()) this.picture = PictureHolder(picture, pictureWidth.toInt(), pictureHeight.toInt())
} }
...@@ -75,3 +111,8 @@ actual open class SkiaLayer( ...@@ -75,3 +111,8 @@ actual open class SkiaLayer(
} }
} }
} }
// TODO: do properly
actual typealias SkikoPlatformInputEvent = UIEvent
actual typealias SkikoPlatformKeyboardEvent = UIEvent
actual typealias SkikoPlatformPointerEvent = UIEvent
\ No newline at end of file
...@@ -2,6 +2,9 @@ package org.jetbrains.skiko ...@@ -2,6 +2,9 @@ package org.jetbrains.skiko
import org.jetbrains.skiko.wasm.api.CanvasRenderer import org.jetbrains.skiko.wasm.api.CanvasRenderer
import org.w3c.dom.HTMLCanvasElement import org.w3c.dom.HTMLCanvasElement
import org.w3c.dom.events.InputEvent
import org.w3c.dom.events.KeyboardEvent
import org.w3c.dom.events.MouseEvent
actual open class SkiaLayer(properties: SkiaLayerProperties = makeDefaultSkiaLayerProperties() actual open class SkiaLayer(properties: SkiaLayerProperties = makeDefaultSkiaLayerProperties()
) { ) {
...@@ -12,28 +15,89 @@ actual open class SkiaLayer(properties: SkiaLayerProperties = makeDefaultSkiaLay ...@@ -12,28 +15,89 @@ actual open class SkiaLayer(properties: SkiaLayerProperties = makeDefaultSkiaLay
get() = 1.0f get() = 1.0f
actual var fullscreen: Boolean actual var fullscreen: Boolean
get() = false get() = false
set(value) = throw Exception("Fullscreen is not supported!") set(value) {
if (value) throw Exception("Fullscreen is not supported!")
}
actual var transparency: Boolean actual var transparency: Boolean
get() = false get() = false
set(value) = throw Exception("Transparency is not supported!") set(value) {
if (value) throw Exception("Transparency is not supported!")
}
actual fun needRedraw() { actual fun needRedraw() {
draw() draw()
} }
actual var renderer: SkiaRenderer? = null actual var skikoView: SkikoView? = null
fun translatePointerEvent(
event: MouseEvent,
buttons: Boolean,
kind: SkikoPointerEventKind
): SkikoPointerEvent {
var mask = SkikoMouseButtons.NONE
if (buttons && event.button.toInt() == 0)
mask = mask or SkikoMouseButtons.LEFT
// https://www.w3schools.com/jsref/event_button.asp
if (buttons && event.button.toInt() == 2)
mask = mask or SkikoMouseButtons.RIGHT
return SkikoPointerEvent(
event.offsetX, event.offsetY,
mask, kind, event
)
}
fun setCanvas(htmlCanvas: HTMLCanvasElement) { fun setCanvas(htmlCanvas: HTMLCanvasElement) {
state = object: CanvasRenderer(htmlCanvas) { state = object: CanvasRenderer(htmlCanvas) {
override fun drawFrame(currentTimestamp: Double) { override fun drawFrame(currentTimestamp: Double) {
// currentTimestamp is milliseconds. // currentTimestamp is in milliseconds.
val currentNanos = currentTimestamp * 1000000 val currentNanos = currentTimestamp * 1_000_000
renderer?.onRender(canvas, width, height, currentNanos.toLong()) skikoView?.onRender(canvas, width, height, currentNanos.toLong())
} }
} }
// See https://www.w3schools.com/jsref/dom_obj_event.asp
// https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events
htmlCanvas.addEventListener("pointerdown", { event ->
event as MouseEvent
skikoView?.onPointerEvent(translatePointerEvent(
event, true, SkikoPointerEventKind.DOWN))
})
htmlCanvas.addEventListener("pointerup", { event ->
event as MouseEvent
var mask = SkikoMouseButtons.NONE
skikoView?.onPointerEvent(translatePointerEvent(
event, true, SkikoPointerEventKind.UP))
})
htmlCanvas.addEventListener("pointermove", { event ->
event as MouseEvent
skikoView?.onPointerEvent(translatePointerEvent(
event, false, SkikoPointerEventKind.MOVE))
})
htmlCanvas.addEventListener("contextmenu", { event ->
event.preventDefault()
})
htmlCanvas.addEventListener("keydown", { event ->
event as KeyboardEvent
skikoView?.onKeyboardEvent(
SkikoKeyboardEvent(
event.keyCode, SkikoKeyboardEventKind.DOWN, event
)
)
})
htmlCanvas.addEventListener("keyup", { event ->
event as KeyboardEvent
skikoView?.onKeyboardEvent(SkikoKeyboardEvent(
event.keyCode, SkikoKeyboardEventKind.UP, event
))
})
} }
fun draw() { fun draw() {
state?.draw() state?.draw()
} }
} }
actual typealias SkikoPlatformInputEvent = InputEvent
actual typealias SkikoPlatformKeyboardEvent = KeyboardEvent
// MouseEvent is base class of PointerEvent
actual typealias SkikoPlatformPointerEvent = MouseEvent
...@@ -12,7 +12,6 @@ import org.jetbrains.skiko.wasm.CreateWebGLContext ...@@ -12,7 +12,6 @@ import org.jetbrains.skiko.wasm.CreateWebGLContext
import org.jetbrains.skiko.wasm.GL import org.jetbrains.skiko.wasm.GL
import org.w3c.dom.HTMLCanvasElement import org.w3c.dom.HTMLCanvasElement
fun Surface.Companion.createFromGL(width: Int, height: Int) = makeFromBackendRenderTarget( fun Surface.Companion.createFromGL(width: Int, height: Int) = makeFromBackendRenderTarget(
DirectContext.makeGL(), DirectContext.makeGL(),
BackendRenderTarget.makeGL( BackendRenderTarget.makeGL(
...@@ -31,7 +30,7 @@ abstract class CanvasRenderer constructor(htmlCanvas: HTMLCanvasElement, val wid ...@@ -31,7 +30,7 @@ abstract class CanvasRenderer constructor(htmlCanvas: HTMLCanvasElement, val wid
GL.makeContextCurrent(contextPointer) GL.makeContextCurrent(contextPointer)
} }
val surface = Surface.createFromGL(width, height) private val surface = Surface.createFromGL(width, height)
val canvas: Canvas val canvas: Canvas
get() = surface.canvas get() = surface.canvas
......
...@@ -8,7 +8,6 @@ import org.jetbrains.skia.ColorType ...@@ -8,7 +8,6 @@ import org.jetbrains.skia.ColorType
import org.jetbrains.skia.ColorSpace import org.jetbrains.skia.ColorSpace
import org.jetbrains.skia.ClipMode import org.jetbrains.skia.ClipMode
import org.jetbrains.skia.ImageInfo import org.jetbrains.skia.ImageInfo
import org.jetbrains.skia.Picture
import org.jetbrains.skia.PictureRecorder import org.jetbrains.skia.PictureRecorder
import org.jetbrains.skia.Rect import org.jetbrains.skia.Rect
import org.jetbrains.skiko.context.ContextHandler import org.jetbrains.skiko.context.ContextHandler
...@@ -123,7 +122,8 @@ actual open class SkiaLayer internal constructor( ...@@ -123,7 +122,8 @@ actual open class SkiaLayer internal constructor(
backedLayer.fullscreen = value backedLayer.fullscreen = value
} }
actual var renderer: SkiaRenderer? = null actual var skikoView: SkikoView? = null
val clipComponents = mutableListOf<ClipRectangle>() val clipComponents = mutableListOf<ClipRectangle>()
@Volatile @Volatile
...@@ -340,7 +340,7 @@ actual open class SkiaLayer internal constructor( ...@@ -340,7 +340,7 @@ actual open class SkiaLayer internal constructor(
try { try {
isRendering = true isRendering = true
renderer?.onRender(canvas, pictureWidth, pictureHeight, nanoTime) skikoView?.onRender(canvas, pictureWidth, pictureHeight, nanoTime)
} finally { } finally {
isRendering = false isRendering = false
} }
...@@ -443,3 +443,8 @@ actual open class SkiaLayer internal constructor( ...@@ -443,3 +443,8 @@ actual open class SkiaLayer internal constructor(
return rounded.toInt() return rounded.toInt()
} }
} }
// InputEvent is abstract, so we wrap to match modality.
actual class SkikoPlatformInputEvent(val wrapped: InputEvent)
actual typealias SkikoPlatformKeyboardEvent = KeyEvent
actual typealias SkikoPlatformPointerEvent = MouseEvent
\ No newline at end of file
...@@ -71,7 +71,7 @@ native crash in SkiaWindowTest "render single window" ...@@ -71,7 +71,7 @@ native crash in SkiaWindowTest "render single window"
init { init {
setSize(width, height) setSize(width, height)
defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE
layer.renderer = object : SkiaRenderer { layer.skikoView = object : NoInputSkikoView() {
override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) { override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
if (canCollect && frameTimes.size < frameCount) { if (canCollect && frameTimes.size < frameCount) {
frameTimes.add(System.nanoTime()) // we check the real time, not the time provided by the argument frameTimes.add(System.nanoTime()) // we check the real time, not the time provided by the argument
...@@ -181,7 +181,7 @@ j org.jetbrains.skiko.redrawer.MacOsRedrawer$drawLayer$1.draw()V+7 ...@@ -181,7 +181,7 @@ j org.jetbrains.skiko.redrawer.MacOsRedrawer$drawLayer$1.draw()V+7
val smoothFPSCounter = FPSCounter(count = frameCheckCount) val smoothFPSCounter = FPSCounter(count = frameCheckCount)
val onComplete = CompletableDeferred<Unit>() val onComplete = CompletableDeferred<Unit>()
fun renderer(window: SkiaWindow) = object : SkiaRenderer { fun renderer(window: SkiaWindow) = object : NoInputSkikoView() {
var t1 = Long.MAX_VALUE var t1 = Long.MAX_VALUE
val refreshRate = window.graphicsConfiguration.device.displayMode.refreshRate val refreshRate = window.graphicsConfiguration.device.displayMode.refreshRate
...@@ -223,7 +223,7 @@ j org.jetbrains.skiko.redrawer.MacOsRedrawer$drawLayer$1.draw()V+7 ...@@ -223,7 +223,7 @@ j org.jetbrains.skiko.redrawer.MacOsRedrawer$drawLayer$1.draw()V+7
window.isVisible = true window.isVisible = true
delay(1000) delay(1000)
window.layer.renderer = renderer(window) window.layer.skikoView = renderer(window)
window.layer.needRedraw() window.layer.needRedraw()
onComplete.await() onComplete.await()
......
...@@ -54,15 +54,15 @@ class SkiaWindowTest { ...@@ -54,15 +54,15 @@ class SkiaWindowTest {
window.setLocation(200, 200) window.setLocation(200, 200)
window.setSize(400, 200) window.setSize(400, 200)
window.defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE window.defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE
val renderer = RectRenderer(window.layer, 200, 100, Color.RED) val app = RectRenderer(window.layer, 200, 100, Color.RED)
window.layer.renderer = renderer window.layer.skikoView = app
window.isUndecorated = true window.isUndecorated = true
window.isVisible = true window.isVisible = true
delay(1000) delay(1000)
screenshots.assert(window.bounds, "frame1") screenshots.assert(window.bounds, "frame1")
renderer.rectWidth = 100 app.rectWidth = 100
window.layer.needRedraw() window.layer.needRedraw()
delay(1000) delay(1000)
screenshots.assert(window.bounds, "frame2") screenshots.assert(window.bounds, "frame2")
...@@ -78,8 +78,8 @@ class SkiaWindowTest { ...@@ -78,8 +78,8 @@ class SkiaWindowTest {
window.setLocation(200, 200) window.setLocation(200, 200)
window.preferredSize = Dimension(400, 200) window.preferredSize = Dimension(400, 200)
window.defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE window.defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE
val renderer = RectRenderer(window.layer, 200, 100, Color.RED) val app = RectRenderer(window.layer, 200, 100, Color.RED)
window.layer.renderer = renderer window.layer.skikoView = app
window.isUndecorated = true window.isUndecorated = true
window.pack() window.pack()
window.paint(window.graphics) window.paint(window.graphics)
...@@ -88,7 +88,7 @@ class SkiaWindowTest { ...@@ -88,7 +88,7 @@ class SkiaWindowTest {
delay(1000) delay(1000)
screenshots.assert(window.bounds, "frame1") screenshots.assert(window.bounds, "frame1")
renderer.rectWidth = 100 app.rectWidth = 100
window.layer.needRedraw() window.layer.needRedraw()
delay(1000) delay(1000)
screenshots.assert(window.bounds, "frame2") screenshots.assert(window.bounds, "frame2")
...@@ -104,7 +104,7 @@ class SkiaWindowTest { ...@@ -104,7 +104,7 @@ class SkiaWindowTest {
window.setLocation(200, 200) window.setLocation(200, 200)
window.setSize(40, 20) window.setSize(40, 20)
window.defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE window.defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE
window.layer.renderer = RectRenderer(window.layer, 20, 10, Color.RED) window.layer.skikoView = RectRenderer(window.layer, 20, 10, Color.RED)
window.isUndecorated = true window.isUndecorated = true
window.isVisible = true window.isVisible = true
delay(1000) delay(1000)
...@@ -124,7 +124,7 @@ class SkiaWindowTest { ...@@ -124,7 +124,7 @@ class SkiaWindowTest {
setLocation(200,200) setLocation(200,200)
setSize(400, 200) setSize(400, 200)
defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE
layer.renderer = RectRenderer(layer, 200, 100, color) layer.skikoView = RectRenderer(layer, 200, 100, color)
isUndecorated = true isUndecorated = true
isVisible = true isVisible = true
} }
...@@ -163,7 +163,7 @@ class SkiaWindowTest { ...@@ -163,7 +163,7 @@ class SkiaWindowTest {
window.setLocation(200, 200) window.setLocation(200, 200)
window.setSize(40, 20) window.setSize(40, 20)
window.defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE window.defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE
window.layer.renderer = object : SkiaRenderer { window.layer.skikoView = object : NoInputSkikoView() {
override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) { override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
renderCount++ renderCount++
} }
...@@ -194,7 +194,7 @@ class SkiaWindowTest { ...@@ -194,7 +194,7 @@ class SkiaWindowTest {
setLocation(200,200) setLocation(200,200)
setSize(40, 20) setSize(40, 20)
defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE
layer.renderer = if (isAnimated) { layer.skikoView = if (isAnimated) {
AnimatedBoxRenderer(layer, pixelsPerSecond = 20.0, size = 20.0) AnimatedBoxRenderer(layer, pixelsPerSecond = 20.0, size = 20.0)
} else { } else {
RectRenderer(layer, 20, 10, Color.RED) RectRenderer(layer, 20, 10, Color.RED)
...@@ -238,7 +238,7 @@ class SkiaWindowTest { ...@@ -238,7 +238,7 @@ class SkiaWindowTest {
setLocation(200,200) setLocation(200,200)
setSize(400, 200) setSize(400, 200)
defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE
layer.renderer = AnimatedBoxRenderer(layer, pixelsPerSecond = 20.0, size = 20.0) layer.skikoView = AnimatedBoxRenderer(layer, pixelsPerSecond = 20.0, size = 20.0)
isVisible = true isVisible = true
} }
...@@ -262,7 +262,7 @@ class SkiaWindowTest { ...@@ -262,7 +262,7 @@ class SkiaWindowTest {
setSize(400, 200) setSize(400, 200)
preferredSize = Dimension(400, 200) preferredSize = Dimension(400, 200)
defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE
layer.renderer = object : SkiaRenderer { layer.skikoView = object : NoInputSkikoView() {
override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) { override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
} }
} }
...@@ -364,15 +364,15 @@ class SkiaWindowTest { ...@@ -364,15 +364,15 @@ class SkiaWindowTest {
window.setLocation(200, 200) window.setLocation(200, 200)
window.setSize(400, 200) window.setSize(400, 200)
window.defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE window.defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE
val renderer = RectRenderer(window.layer, 200, 100, Color.RED) val app = RectRenderer(window.layer, 200, 100, Color.RED)
window.layer.renderer = renderer window.layer.skikoView = app
window.isUndecorated = true window.isUndecorated = true
window.isVisible = true window.isVisible = true
delay(1000) delay(1000)
screenshots.assert(window.bounds, "frame1", "testFallbackToSoftware") screenshots.assert(window.bounds, "frame1", "testFallbackToSoftware")
renderer.rectWidth = 100 app.rectWidth = 100
window.layer.needRedraw() window.layer.needRedraw()
delay(1000) delay(1000)
screenshots.assert(window.bounds, "frame2", "testFallbackToSoftware") screenshots.assert(window.bounds, "frame2", "testFallbackToSoftware")
...@@ -424,7 +424,7 @@ class SkiaWindowTest { ...@@ -424,7 +424,7 @@ class SkiaWindowTest {
window.setLocation(200, 200) window.setLocation(200, 200)
window.setSize(400, 200) window.setSize(400, 200)
window.defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE window.defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE
window.layer.renderer = object : SkiaRenderer { window.layer.skikoView = object : NoInputSkikoView() {
override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) { override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
drawCount++ drawCount++
...@@ -470,7 +470,7 @@ class SkiaWindowTest { ...@@ -470,7 +470,7 @@ class SkiaWindowTest {
val paragraph by lazy { paragraph(window.layer.contentScale * 40, "=-+Нп") } val paragraph by lazy { paragraph(window.layer.contentScale * 40, "=-+Нп") }
window.layer.renderer = object : SkiaRenderer { window.layer.skikoView = object : NoInputSkikoView() {
override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) { override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
paragraph.layout(Float.POSITIVE_INFINITY) paragraph.layout(Float.POSITIVE_INFINITY)
paragraph.paint(canvas, 0f, 0f) paragraph.paint(canvas, 0f, 0f)
...@@ -502,7 +502,7 @@ class SkiaWindowTest { ...@@ -502,7 +502,7 @@ class SkiaWindowTest {
var rectWidth: Int, var rectWidth: Int,
var rectHeight: Int, var rectHeight: Int,
private val rectColor: Color private val rectColor: Color
) : SkiaRenderer { ) : NoInputSkikoView() {
override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) { override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
val dpi = layer.contentScale val dpi = layer.contentScale
canvas.drawRect(Rect(0f, 0f, width.toFloat(), height.toFloat()), Paint().apply { canvas.drawRect(Rect(0f, 0f, width.toFloat(), height.toFloat()), Paint().apply {
...@@ -518,7 +518,7 @@ class SkiaWindowTest { ...@@ -518,7 +518,7 @@ class SkiaWindowTest {
private val layer: SkiaLayer, private val layer: SkiaLayer,
private val pixelsPerSecond: Double, private val pixelsPerSecond: Double,
private val size: Double private val size: Double
) : SkiaRenderer { ) : NoInputSkikoView() {
private var oldNanoTime = Long.MAX_VALUE private var oldNanoTime = Long.MAX_VALUE
private var x = 0.0 private var x = 0.0
......
...@@ -7,7 +7,6 @@ internal actual fun createNativeContextHandler(layer: SkiaLayer, renderApi: Grap ...@@ -7,7 +7,6 @@ internal actual fun createNativeContextHandler(layer: SkiaLayer, renderApi: Grap
TODO() TODO()
} }
internal actual fun createNativeRedrawer( internal actual fun createNativeRedrawer(
layer: SkiaLayer, layer: SkiaLayer,
renderApi: GraphicsApi, renderApi: GraphicsApi,
......
...@@ -15,5 +15,10 @@ actual open class SkiaLayer(properties: SkiaLayerProperties) { ...@@ -15,5 +15,10 @@ actual open class SkiaLayer(properties: SkiaLayerProperties) {
actual fun needRedraw() { actual fun needRedraw() {
TODO("unimplemented") TODO("unimplemented")
} }
actual var renderer: SkiaRenderer? = null actual var skikoView: SkikoView? = null
} }
\ No newline at end of file
// TODO: do properly
actual typealias SkikoPlatformInputEvent = Any
actual typealias SkikoPlatformKeyboardEvent = Any
actual typealias SkikoPlatformPointerEvent = Any
\ No newline at end of file
...@@ -4,7 +4,7 @@ import kotlinx.cinterop.useContents ...@@ -4,7 +4,7 @@ import kotlinx.cinterop.useContents
import org.jetbrains.skiko.context.* import org.jetbrains.skiko.context.*
import org.jetbrains.skia.* import org.jetbrains.skia.*
import org.jetbrains.skiko.redrawer.Redrawer import org.jetbrains.skiko.redrawer.Redrawer
import platform.AppKit.NSView import platform.AppKit.*
import platform.Foundation.NSMakeRect import platform.Foundation.NSMakeRect
actual open class SkiaLayer( actual open class SkiaLayer(
...@@ -26,10 +26,10 @@ actual open class SkiaLayer( ...@@ -26,10 +26,10 @@ actual open class SkiaLayer(
if (value) throw IllegalArgumentException("transparency unsupported") if (value) throw IllegalArgumentException("transparency unsupported")
} }
val nsView = NSView(NSMakeRect(0.0, 0.0, 640.0, 480.0)) lateinit var nsView: NSView
var _contentScale: Float = 1.0f var _contentScale: Float = 1.0f
actual var renderer: SkiaRenderer? = null actual var skikoView: SkikoView? = null
private var contextHandler = MacOSOpenGLContextHandler(this) private var contextHandler = MacOSOpenGLContextHandler(this)
...@@ -38,8 +38,75 @@ actual open class SkiaLayer( ...@@ -38,8 +38,75 @@ actual open class SkiaLayer(
private var picture: PictureHolder? = null private var picture: PictureHolder? = null
private val pictureRecorder = PictureRecorder() private val pictureRecorder = PictureRecorder()
fun initLayer() { private fun eventToMouse(event: NSEvent, kind: SkikoPointerEventKind): SkikoPointerEvent {
println("SkiaLayer.initLayer") var buttons = SkikoMouseButtons.NONE
val mask = event.buttonMask.toInt()
if ((mask and 1) != 0 || event.buttonNumber == 0L) {
buttons = buttons or SkikoMouseButtons.LEFT
}
if ((mask and 2) != 0 || event.buttonNumber == 1L) {
buttons = buttons or SkikoMouseButtons.RIGHT
}
var (x, y) = event.locationInWindow.useContents {
this.x to this.y
}
// Translate.
nsView.frame.useContents {
y = size.height - y
}
return SkikoPointerEvent(x, y, buttons, kind, event)
}
private fun eventToKeyboard(event: NSEvent, kind: SkikoKeyboardEventKind): SkikoKeyboardEvent {
return SkikoKeyboardEvent(event.keyCode.toInt(), kind, event)
}
fun initLayer(window: NSWindow) {
val (width, height) = window.contentLayoutRect.useContents {
this.size.width to this.size.height
}
nsView = object : NSView(NSMakeRect(0.0, 0.0, width, height)) {
private var trackingArea : NSTrackingArea? = null
override fun acceptsFirstResponder(): Boolean {
return true
}
override fun viewWillMoveToWindow(newWindow: NSWindow?) {
updateTrackingAreas()
}
override fun updateTrackingAreas() {
bounds.useContents {
println("update tracking areas to ${this.size.width} ${this.size.height}")
}
trackingArea?.let { removeTrackingArea(it) }
trackingArea = NSTrackingArea(rect = bounds,
options = NSMouseMoved or NSTrackingActiveAlways, // NSTrackingActiveInActiveApp,
owner = nsView, userInfo = null)
nsView.addTrackingArea(trackingArea!!)
}
override fun rightMouseDown(event: NSEvent) {
skikoView?.onPointerEvent(eventToMouse(event, SkikoPointerEventKind.DOWN))
}
override fun rightMouseUp(event: NSEvent) {
skikoView?.onPointerEvent(eventToMouse(event, SkikoPointerEventKind.UP))
}
override fun mouseDown(event: NSEvent) {
skikoView?.onPointerEvent(eventToMouse(event, SkikoPointerEventKind.DOWN))
}
override fun mouseUp(event: NSEvent) {
skikoView?.onPointerEvent(eventToMouse(event, SkikoPointerEventKind.UP))
}
override fun mouseMoved(event: NSEvent) {
skikoView?.onPointerEvent(eventToMouse(event, SkikoPointerEventKind.MOVE))
}
override fun keyDown(event: NSEvent) {
skikoView?.onKeyboardEvent(eventToKeyboard(event, SkikoKeyboardEventKind.DOWN))
}
override fun keyUp(event: NSEvent) {
skikoView?.onKeyboardEvent(eventToKeyboard(event, SkikoKeyboardEventKind.UP))
}
}
window.contentView!!.addSubview(nsView)
redrawer = createNativeRedrawer(this, GraphicsApi.OPENGL, properties) redrawer = createNativeRedrawer(this, GraphicsApi.OPENGL, properties)
redrawer?.redrawImmediately() redrawer?.redrawImmediately()
} }
...@@ -55,8 +122,6 @@ actual open class SkiaLayer( ...@@ -55,8 +122,6 @@ actual open class SkiaLayer(
} }
fun update(nanoTime: Long) { fun update(nanoTime: Long) {
println("SkiaLayer.update")
val width = nsView.frame.useContents { size.width } val width = nsView.frame.useContents { size.width }
val height = nsView.frame.useContents { size.height } val height = nsView.frame.useContents { size.height }
...@@ -65,7 +130,7 @@ actual open class SkiaLayer( ...@@ -65,7 +130,7 @@ actual open class SkiaLayer(
val bounds = Rect.makeWH(pictureWidth.toFloat(), pictureHeight.toFloat()) val bounds = Rect.makeWH(pictureWidth.toFloat(), pictureHeight.toFloat())
val canvas = pictureRecorder.beginRecording(bounds) val canvas = pictureRecorder.beginRecording(bounds)
renderer?.onRender(canvas, pictureWidth.toInt(), pictureHeight.toInt(), nanoTime) skikoView?.onRender(canvas, pictureWidth.toInt(), pictureHeight.toInt(), nanoTime)
val picture = pictureRecorder.finishRecordingAsPicture() val picture = pictureRecorder.finishRecordingAsPicture()
this.picture = PictureHolder(picture, pictureWidth.toInt(), pictureHeight.toInt()) this.picture = PictureHolder(picture, pictureWidth.toInt(), pictureHeight.toInt())
...@@ -74,7 +139,6 @@ actual open class SkiaLayer( ...@@ -74,7 +139,6 @@ actual open class SkiaLayer(
private var initedCanvas = false private var initedCanvas = false
fun draw() { fun draw() {
println("SkiaLayer.draw")
contextHandler.apply { contextHandler.apply {
if (!initedCanvas) { if (!initedCanvas) {
if (!initContext()) { if (!initContext()) {
...@@ -86,7 +150,6 @@ actual open class SkiaLayer( ...@@ -86,7 +150,6 @@ actual open class SkiaLayer(
} }
clearCanvas() clearCanvas()
val picture = picture val picture = picture
println("SkiaLayer.draw: picture=$picture")
if (picture != null) { if (picture != null) {
drawOnCanvas(picture.instance) drawOnCanvas(picture.instance)
} }
...@@ -95,3 +158,7 @@ actual open class SkiaLayer( ...@@ -95,3 +158,7 @@ actual open class SkiaLayer(
} }
} }
// TODO: do properly
actual typealias SkikoPlatformInputEvent = NSEvent
actual typealias SkikoPlatformKeyboardEvent = NSEvent
actual typealias SkikoPlatformPointerEvent = NSEvent
package org.jetbrains.skiko
import platform.AppKit.*
import platform.Foundation.*
class SkiaWindow(layer: SkiaLayer) {
private val windowStyle =
NSWindowStyleMaskTitled or
NSWindowStyleMaskMiniaturizable or
NSWindowStyleMaskClosable or
NSWindowStyleMaskResizable
// TODO: what's the proper way to create window here?
// Should we subclass NSWindow?
// Provide multiple constructors for SkiaWindow?
// Behave similar to Skiko/JVM?
val nsWindow = NSWindow(
contentRect = NSMakeRect(0.0, 0.0, 640.0, 480.0),
styleMask = windowStyle,
backing = NSBackingStoreBuffered,
defer = true)
init {
nsWindow.contentView!!.addSubview(layer.nsView)
layer.initLayer()
}
}
...@@ -9,7 +9,6 @@ import platform.OpenGLCommon.GLenum ...@@ -9,7 +9,6 @@ import platform.OpenGLCommon.GLenum
internal class MacOSOpenGLContextHandler(layer: SkiaLayer) : ContextHandler(layer) { internal class MacOSOpenGLContextHandler(layer: SkiaLayer) : ContextHandler(layer) {
override fun initContext(): Boolean { override fun initContext(): Boolean {
println("MacOSOpenGLContextHandler.initContext")
try { try {
if (context == null) { if (context == null) {
context = DirectContext.makeGL() context = DirectContext.makeGL()
...@@ -33,7 +32,6 @@ internal class MacOSOpenGLContextHandler(layer: SkiaLayer) : ContextHandler(laye ...@@ -33,7 +32,6 @@ internal class MacOSOpenGLContextHandler(layer: SkiaLayer) : ContextHandler(laye
} }
override fun initCanvas() { override fun initCanvas() {
println("MacOSOpenGLContextHandler.initCanvas")
val scale = layer.contentScale val scale = layer.contentScale
val w = (layer.nsView.frame.useContents { size.width } * scale).toInt().coerceAtLeast(0) val w = (layer.nsView.frame.useContents { size.width } * scale).toInt().coerceAtLeast(0)
val h = (layer.nsView.frame.useContents { size.height } * scale).toInt().coerceAtLeast(0) val h = (layer.nsView.frame.useContents { size.height } * scale).toInt().coerceAtLeast(0)
......
...@@ -103,8 +103,6 @@ class MacosGLLayer(val layer: SkiaLayer, setNeedsDisplayOnBoundsChange: Boolean) ...@@ -103,8 +103,6 @@ class MacosGLLayer(val layer: SkiaLayer, setNeedsDisplayOnBoundsChange: Boolean)
displayTime: CPointer<CVTimeStamp>? displayTime: CPointer<CVTimeStamp>?
) { ) {
CGLSetCurrentContext(ctx); CGLSetCurrentContext(ctx);
println("MacosGLLayer::drawInCGLContext")
performDraw() performDraw()
//context.flush() // TODO: I thought the below should call context.flush(). //context.flush() // TODO: I thought the below should call context.flush().
......
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