@@ -2,13 +2,20 @@ package dev.silenium.compose.gl
22
33import androidx.compose.runtime.CompositionLocal
44import org.jetbrains.skia.DirectContext
5- import org.jetbrains.skia.Surface
65import org.jetbrains.skia.impl.NativePointer
76import org.jetbrains.skiko.GraphicsApi
87import org.jetbrains.skiko.SkiaLayer
9- import org.jetbrains.skiko.graphicapi.DirectXOffscreenContext
8+ import java.awt.Container
109import java.awt.Window
10+ import java.util.*
11+ import javax.swing.JComponent
12+ import kotlin.reflect.KClass
13+ import kotlin.reflect.KProperty1
14+ import kotlin.reflect.KType
15+ import kotlin.reflect.full.createType
16+ import kotlin.reflect.full.isSubtypeOf
1117import kotlin.reflect.full.memberProperties
18+ import kotlin.reflect.full.superclasses
1219import kotlin.reflect.jvm.isAccessible
1320import kotlin.reflect.typeOf
1421
@@ -19,59 +26,71 @@ val LocalWindow: CompositionLocal<Window?> by lazy {
1926 method.invoke(null ) as CompositionLocal <Window ?>
2027}
2128
22- fun Window.directXRedrawer (): Any? {
23- return contextHandler().let {
24- val getter = it::class .memberProperties.first { it.name == " directXRedrawer" }
25- getter.isAccessible = true
26- getter.call(it)
29+ fun Window.directX12Device (): NativePointer ? {
30+ return findSkiaLayer()?.let { layer ->
31+ if (layer.graphicsApi() != GraphicsApi .DIRECT3D ) return null
32+ layer.redrawer().let { redrawer ->
33+ val getter = redrawer::class .memberProperties.first {
34+ it.name == " device" && it.returnType == typeOf<Long >()
35+ }
36+ getter.isAccessible = true
37+ getter.call(redrawer) as Long?
38+ }
2739 }
2840}
2941
30- fun Window.directX12Device (): NativePointer ? {
31- return directXRedrawer()?.let {
32- val getter = it::class .memberProperties.first { it.name == " device" && it.returnType == typeOf<Long >() }
33- getter.isAccessible = true
34- getter.call(it) as Long?
35- }
42+ fun SkiaLayer.directContext (): DirectContext ? {
43+ return contextHandler()?.findProperty<DirectContext ?>()
3644}
3745
38- fun Window.directContext (): DirectContext ? {
39- val surface = contextHandler().let {
40- val getter = it::class .java.superclass.superclass.getDeclaredMethod(" getSurface" )
41- getter.isAccessible = true
42- getter.invoke(it) as ? Surface
43- }
44- return surface?.recordingContext
46+ inline fun <reified T > Any.findProperty (): T ? {
47+ return findProperty(typeOf<T >()) as T ?
4548}
4649
47- fun Window.graphicsApi (): GraphicsApi {
48- return mediator().let {
49- val getter = it::class .memberProperties.first { it.name == " renderApi" && it.returnType == typeOf<GraphicsApi >() }
50- getter.isAccessible = true
51- getter.call(it) as GraphicsApi
50+ fun Any.findProperty (type : KType ): Any? {
51+ val supertypes = LinkedList <KClass <* >>()
52+ supertypes.add(this ::class )
53+ var getter: KProperty1 <* , * >? = null
54+ while (getter == null && supertypes.isNotEmpty()) {
55+ val klass = supertypes.pop()
56+ for (prop in klass.memberProperties) {
57+ if (prop.returnType.isSubtypeOf(type)) {
58+ getter = prop
59+ break
60+ }
61+ }
62+ klass.superclasses.let (supertypes::addAll)
5263 }
64+ getter?.isAccessible = true
65+ return getter?.call(this )
5366}
5467
55- fun Window.mediator (): Any {
56- val composePanel = this .getFieldValue(" composePanel" )!!
57- val composeContainer = composePanel.getFieldValue(" _composeContainer" )!!
58- return composeContainer.getFieldValue(" mediator" )!!
68+ fun SkiaLayer.graphicsApi (): GraphicsApi {
69+ return findProperty<GraphicsApi >() ? : GraphicsApi .UNKNOWN
5970}
6071
61- fun Window.contextHandler (): Any {
62- val contentComponent = mediator().let {
63- val getter = it::class .java.getMethod(" getContentComponent" )
64- getter.invoke(it) as SkiaLayer
65- }
66- val redrawer = contentComponent.let {
67- val getter = it::class .java.getMethod(" getRedrawer${' $' } skiko" )
68- getter.invoke(it)
69- }
70- return redrawer?.getFieldValue(" contextHandler" )!!
72+ fun SkiaLayer.contextHandler (): Any? {
73+ val propType = Class .forName(" org.jetbrains.skiko.context.ContextHandler" )
74+ return redrawer().findProperty(propType.kotlin.createType())
7175}
7276
73- private fun Any.getFieldValue (fieldName : String ): Any? {
74- val field = this ::class .java.getDeclaredField(fieldName)
75- field.isAccessible = true
76- return field.get(this )
77+ fun SkiaLayer.redrawer (): Any = SkikoCompat .getRedrawer(this )
78+
79+ fun Window.findSkiaLayer () = findComponent<SkiaLayer >()
80+
81+ private fun <T : JComponent > findComponent (
82+ container : Container ,
83+ klass : Class <T >,
84+ ): T ? {
85+ val componentSequence = container.components.asSequence()
86+ return componentSequence
87+ .filter { klass.isInstance(it) }
88+ .ifEmpty {
89+ componentSequence
90+ .filterIsInstance<Container >()
91+ .mapNotNull { findComponent(it, klass) }
92+ }.map { klass.cast(it) }
93+ .firstOrNull()
7794}
95+
96+ private inline fun <reified T : JComponent > Container.findComponent () = findComponent(this , T ::class .java)
0 commit comments