Greetings! I'd like to contribute a native Linux s...
# compose
n
Greetings! I'd like to contribute a native Linux support for Compose Multiplatform. 1. Where should I start? I've heard that window management would be a good place to start. Could you point me in the right direction? Where do I get started with familiarizing myself with the codebase? 2. Would a compose-multiplatform-core repository on GitHub be enough for what I'm trying to achieve? They say there is only a limited type of folders that can be contributed to via GitHub's Pull Request method. What issues may arise with this limitation? Why is there a limit on what can be contributed to? 3. Why are compose repositories separate (core and non-core)? How are they different? 4. Is this channel the right place to ask questions about the development process? Are there any JB developers here who would be able to answer then or is this a strictly community-to-commumity place?
i
1. I guess you can get some context from Jake's port to linux based light switch (

https://youtu.be/D0P5Lb-2uCY

) 2. Yes. this limitations docs are from Google's AOSP, not from multiplatform fork follow MULTIPLATFORM.md instead. 3. Because we need a fork. *-core is a fork of Google's androidx/AOSP, and the main one is for our plugin, examples and some more components. 4. It will work, most of the teammates are here
❤️ 1
j
Nothing of mine was merged, sadly. Aside from a few general bug fixes that are not Linux ARM-specific.
Skiko requires fundamental build changes to work, since it requires its C++ be built on a system with that OS and architecture, but it's build system is Gradle and KGP doesn't build on Linux ARM. No real movement on that support.
Compose UI is even harder. What UI toolkit would they even target? I was targeting a full screen device with no window manager, but most people will expect Wayland or X11 and then GTK or Qt.
There is no "native" window system like there is for Mac or the JVM. If you target one, you kinda break the ability to use it with others unless your actuals on Linux are themselves an abstraction that can be swapped out.
a
Considering almost every DE is deprecating X11 for Wayland, I don't really see much point in supporting X11. With that out of the way, window management becomes much simpler, no GTK or QT required: https://wayland.app/protocols/xdg-shell
👍 1
n
I don't really see much point in supporting X11
Yea, exactly. I was thinking about specifically targeting Wayland. Preferably making everything modular enough so that someone else can implement X11 themselves if they really need it.
Compose UI is even harder.
@jw, could you elaborate on that? I had an impression it is Skiko that implements all the native drawing and event handling logic. After that CMP uses it through a common interface and there is not a lot of extra integration going on. I've seen your talk (great work btw), but I currently need more practical experience with CMP internals to fully understand it.
Skiko requires fundamental build changes to work, since it requires its C++ be built on a system with that OS and architecture, but it's build system is Gradle and KGP doesn't build on Linux ARM. No real movement on that support.
Are you talking about the fact that Skiko, when compiled and run on the device, links to certain system libraries? I can certainly see how this would become a problem is we compile for AMD64/x86-64 and then run on arm, sure. But Skiko already has a support for native iOS, which is arm (cross-compilation, I assume?). We should be able to do the same for Linux, shouldn't we?
j
I'm not saying cross-compilation is impossible. I'm just saying that the current build setup does not do cross-compilation for Linux ARM if you simply add it as a target.
n
Yea, but shouldn't some sort of cross-comp happen for iOS [already]?
j
Yes
As for the problems with Compose UI, you can look at its expect/actuals to see the problems you'll face, or my commit which adds Linux ARM in my fork. The use of expect/actual instead of polymorphism means that it assumes there is only a single, canonical UI toolkit for each build target and that's simply not true for Linux. If you actualize to GTK then it would be impossible to use for Qt and it would have been impossible for me to use on the light switch or similar embedded-style devices.
i
The use of expect/actual instead of polymorphism means that it assumes there is only a single, canonical UI toolkit for each build target and that's simply not true for Linux.
💯 Trying to change this internally (not only because of linux), but it's not so easy
n
Whats the current status of
linuxMain
then? I assume it is only a stub now?
j
In what module? The runtime modules have full linux support, but foundation and up (ui, material, etc.) should not.
n
I am talking about this one. I assume all
xxxMain
source sets are responsible for UI? Where can I see the runtime?
i
j
All/almost all of those actuals in
linuxMain
are legacy stuff from when Skiko abstracted over window toolkits. Thankfully most of that has moved up into Compose UI, and Skiko has slimmed down mostly into a Skia MPP abstraction. For Linux, all the interesting bits are ready to go in the
nativeJsMain
source set which serves all native targets.
n
I'm so very confused :( From what I understand: Skiko is just bindings over C++ Skia and is only responsible for drawing primitives on the screen. The actual window management and event management happens in Compose UI (aka the compose-multiplatform-core). Window management and event system does not exist yet. And Skiko works on Linux, but does not yet have a "proper" cross-compilation support from the build system (but the inspiration can be taken from iOS). Is that correct?
i
Skiko is just bindings over C++ Skia and is only responsible for drawing primitives on the screen.
*to some surface. skiko also adds some logic for creating GPU context for skia.
The actual window management and event management happens in Compose UI
well, for now it just delegates it to AWT/Swing
aka the compose-multiplatform-core
It's fork of monorepo, it contains MUCH more
Window management and event system does not exist yet
*the binding between platform events and compose ones for linux doesn't exist
And Skiko works on Linux, but does not yet have a "proper" cross-compilation support from the build system
with JNI bindings for JVM - yes. Not sure about the status for K/N bridge there. I also doubt that it already has some code to init GPU context, but probably some code might be reused from JNI variant
but foundation and up (ui, material, etc.)
ui is under foundation, but both are depends on skiko and do not support linux/native, correct
j
ah, yeah. i always get that backwards because of the naming.
n
1. What is the purpose of
‎compose/foundation
and
‎compose/ui
? 2. How does iOS handle rendering? I assume it creates some native surface that Skia knows how to draw on? 3. How does iOS handle events? In Compose, who reads and processes such events?
Or does Skia/Skiko draw in some internal buffer and then copy its contents to an external surface?
i
1. https://developer.android.com/develop/ui/compose/layering 2. Yes, Compose creates Metal view and passes the handle to skia 3. Subscribe on platform events, wrap/convert it to compose types and pass it to Compose. For desktop you can find this in ComposeSceneMediator.desktop.kt
Or does Skia/Skiko draw in some internal buffer and then copy its contents to an external surface?
There is some cases where it happens, but in general it draws directly into GPU surface
n
> Yes, Compose creates Metal view and passes the handle to skia Awesome. That means I have to create a Wayland surface and pass it to Skia? I assume it would happen somewhere in the UI layer of the Compose? What modifications would Skiko need? Could you also please show me where iOS creates its Metal view to then pass it to Skia?
i
Wayland surface and pass it to Skia?
I guess it should be OpenGL context or fallback to skia software renderer if not supported
Could you also please show me where iOS creates its Metal view to then pass it to Skia?
tbh, iOS/Metal is too far from what you need to use it as example
n
> tbh, iOS/Metal is too far from what you need to use it as example How so? It's the only native CMP target. Android and Desktop are using JVM and Web is a world of its own. > I guess it should be OpenGL context or fallback to skia software renderer if not supported My confusion is skyrocketing with each new answer, hahaha. Do you mean that skia accepts an OpenGL context as a surface to draw on? And what is "skia software renderer"?
i
It's the only native CMP target.
Not really. Compose unofficially supports macOS/Native I guess you can unroll the chain from ComposeWindow.macos.kt
Do you mean that skia accepts an OpenGL context as a surface to draw on?
Yes
And what is "skia software renderer"?
Skia supports drawing via directx/opengl/vulcan/metal and software. So you can generate some png for example without touching GPU. See skia docs: https://skia.org/docs/user/api/skcanvas_creation/#raster On JVM/Desktop skiko as a chain of fallbacks for each platform so if something is not supported it end up with software renderer (slow cpu way)
t
I’m currently experimenting with Skiko/Compose on Linux arm64. I need to write a UI for a Linux arm64 based smart watch. Re-using existing UI code would be better than rewriting the UI in a different framework, so I am trying to get Compose working. I figured out that my watch supports drawing using EGL, and I was able to get Skiko up and running with a functioning canvas. Here is the first PR which adds support for linuxArm64 target to skiko: https://github.com/JetBrains/skiko/pull/1051 I will open a PR soon for the EGL support. Thanks a lot to the light-switch project! It really helped getting me started with this.
PRs for EGL support: https://github.com/JetBrains/skia-pack/pull/68 https://github.com/JetBrains/skiko/pull/1052 If it is too risky to enable EGL for all platforms I could add it only to Linux targets. Let me know your thoughts.
After these PRs, next step would be to port compose-multiplatform-core to Linux (x64 and arm64). For my use case I do not need window manager support, just like the light switch project. Would you accept a PR to compose-multiplatform-core adding Linux support with no additional window support? This would still allow the usage of compose on embedded linux devices with no windowing. Support for window managers could be added in the future of course. Additionally as I am targeting a Linux smart watch device I need the compose wear libraries. I could open a PR for this migrating these to Kotlin multiplatform, would you be open for this? Most code is not Android-specific so it should be very easy.
j
How are you planning to do input without a windowing system? Porting to raw input event like I did will mean windowing systems can't be used, even if desired. This is a problem with the current design, unfortunately.
t
For the smart watch I will just send pointer events to the compose scene, similar to your project. I am not sure what this means for the compose repository if I just don’t implement it there. I still need to investigate this as I only worked on porting skiko so far.
j
Have you seen https://github.com/JetBrains/skiko/pull/1048? I was wondering if that API would solve EGL for cases like ours.
👀 1
t
Interesting will check it out, I didn’t notice it as I already started working on this days ago. Maybe the EGL support can be skipped, would be nice.
Finally I was able to get compose UI running on the Linux watch. For some reason the SkSL parsing in Skia triggered some SIGSEGV errors, but was able to fix it (will eventually submit PR for this). Started working on cherry-picking some stuff from my compose Linux branch to PRs, which should be OK to be merged already: https://github.com/JetBrains/compose-multiplatform-core/pull/2006 https://github.com/JetBrains/compose-multiplatform-core/pull/2007
Any idea what to do about
org.jetbrains.compose.material:material-icons-core
? It is not currently available for Linux, and with no source code I cannot add the Linux target.
j
Yeah they removed the sources because the Kotlin compiler choked on it. It was entirely generated files, and if I remember correctly you had to throw a TON of RAM and prayers to get it to compilew
1000000921.png
t
Ah thanks for checking, that’s good to know. I guess I will figure something out. First I will try to get everything working locally, and for now will only cherry pick some stuff in PRs where possible.
i
🖖 I just want to thank you for contributions that mentioned above. Please note that it's quite busy time for the team now, so most likely we'll have a chance to look at it in detail only after 1.8/KotlinConf. Sorry for such delays
👍 1
n
Unresolved reference: platform
in
import platform.AppKit
or
import platform.darwin
. How do I resolve it? Where is this import coming from?
Oh, I assume it wasn't installed/configured by Gradle because I am not developing on mac.
t
when do you get the error? There should not be any AppKit references in linux code. What file is it referring to? The draft PR I opened contains no apple code in linux source sets
n
Yea, but I was just trying to create a
skiko/samples/SkiaLinuxSample
to try and draw at leas something natively. I was using other samples as examples an noticed an invalid import.
Did you actually manage to draw something with Skiko?
t
Yes, it you set up an EGL surface you can draw on it with skiko
Copy code
context = DirectContext.makeEGL()
Copy code
private fun draw() {
        if (canvas == null) {
            renderTarget = BackendRenderTarget.makeGL(
                width = width,
                height = height,
                sampleCnt = 0,
                stencilBits = 8,
                fbId = 0,
                fbFormat = FramebufferFormat.GR_GL_RGBA8,
            )

            surface = Surface.makeFromBackendRenderTarget(
                context,
                renderTarget!!,
                SurfaceOrigin.BOTTOM_LEFT,
                SurfaceColorFormat.RGBA_8888,
                ColorSpace.sRGB,
                SurfaceProps(),
            )!!
            canvas = surface!!.canvas
        }

        canvas!!.clear(Color.BLACK)
        scene.render(canvas!!.asComposeCanvas(), currentNanoTime())

        context.flush()
        surface!!.flushAndSubmit()
    }
Instead of scene.render which is compose you can draw on the canvas directly with skiko
Also check out this file which I linked here on the older skiko commit instead of compose: https://github.com/JakeWharton/composeui-lightswitch/blob/03525fb77800a2443685294539544cd0e7508ab9/src/commonMain/kotlin/main.kt