Compare commits

...
2 Commits
Author SHA1 Message Date
Bitsy eb44db915e refactor: finish the SDL3* -> Sdl3* rename and regenerate the API dumps
Picks up the file/type renames (SDL3Backend -> Sdl3Backend, SDL3FrameClock ->
Sdl3FrameClock, SDL3EventMapper.kt -> AppEvent.kt) and unifies the 15 references
the rename left behind. All were prose or strings, which is why it still
compiled:

  - a user-visible error message ("SDL3Backend received GpuMode.Auto ...")
  - two user-visible demo UI strings ("driven by the SDL3FrameClock ...")
  - the MARK headers in AppEvent.kt and Sdl3Backend.kt
  - KDoc cross-references, including to `SDL3EventMapper.kt` which no longer
    exists - repointed at AppEvent.kt
  - 3 PLAN.md references

The codebase now uses Sdl3* uniformly, matching the Sdl3MainDispatcher /
Sdl3Clipboard / Sdl3DragAndDropOwner / Sdl3ScrollConfig that already followed
that form. `sdl3.*` (the cinterop package) and SDL_* (the C API) are untouched.

API dumps regenerated: a clean 1:1 rename, 41 SDL3Backend -> 41 Sdl3Backend in
:ui and nothing else - the other renamed types were not public. Breaking, but
com.compose.sdl only; no androidx.compose.* / org.jetbrains.compose.* change.

Verified beyond compiling: apiCheck green, both metadata publish gates compile,
nav3test/backtest/multiwintest PASS, raw SDL handles still resolve
(gpu=OpenGL window=true glContext=true), and the Images screen renders.
2026-09-19 00:12:36 +02:00
Bitsy 1120abc488 Update README to remove Infrastructure and add Support
Removed the Infrastructure section and added a Support section with a Discord link.
2026-09-18 23:54:32 +02:00
27 changed files with 84 additions and 105 deletions
+14 -14
View File
@@ -391,21 +391,21 @@ re-vendoring.** The debt is **completing native-actual stubs**.
### P0 - blocks fidelity/correctness ### P0 - blocks fidelity/correctness
- [~] **P0** Text context menu (right-click copy/paste/select-all) - **RECONCILED: already - [~] **P0** Text context menu (right-click copy/paste/select-all) - **RECONCILED: already
works, no reimplementation.** Static trace confirmed the menu is wired end-to-end works, no reimplementation.** Static trace confirmed the menu is wired end-to-end
through the vendored LEGACY path: text-level `ContextMenuArea` (`ContextMenu.native.kt`) through the vendored LEGACY path: text-level `ContextMenuArea` (`ContextMenu.native.kt`)
→ `CommonContextMenuArea` (`vendor/common/.../text/CommonContextMenuArea.kt`) → (native → `CommonContextMenuArea` (`vendor/common/.../text/CommonContextMenuArea.kt`) → (native
`ComposeFoundationFlags.isNewContextMenuEnabled = false`, so the legacy branch) → `ComposeFoundationFlags.isNewContextMenuEnabled = false`, so the legacy branch) →
`contextmenu.ContextMenuArea` → `contextMenuGestures`/`onRightClickDown` `contextmenu.ContextMenuArea` → `contextMenuGestures`/`onRightClickDown`
(`isSecondaryPressed`) → `ContextMenuPopup` → `Popup.native.kt` (hosted by (`isSecondaryPressed`) → `ContextMenuPopup` → `Popup.native.kt` (hosted by
`LocalPopupHost` at the app root). Items are real: `TextFieldSelectionManager` / `LocalPopupHost` at the app root). Items are real: `TextFieldSelectionManager` /
`SelectionManager.contextMenuBuilder` emit Cut/Copy/Paste/SelectAll with real actions. `SelectionManager.contextMenuBuilder` emit Cut/Copy/Paste/SelectAll with real actions.
SDL right-click → `PointerButton.Secondary` (`SDL3EventMapper.kt`) → SDL right-click → `PointerButton.Secondary` (`AppEvent.kt`) →
`isSecondaryPressed` (`PointerEventBridge`). The three "NOP" seams belong to the `isSecondaryPressed` (`PointerEventBridge`). The three "NOP" seams belong to the
DISABLED *new* context-menu API and are unreachable - **DONE:** their misleading DISABLED *new* context-menu API and are unreachable - **DONE:** their misleading
`TODO(CMP-7819)` comments corrected to say so, in all three files. `TODO(CMP-7819)` comments corrected to say so, in all three files.
**REMAINING:** an interactive right-click smoke check (no headless driver here - fold **REMAINING:** an interactive right-click smoke check (no headless driver here - fold
into manual/WIN-SMOKE). Known limitation, not a breakage: Paste enablement is into manual/WIN-SMOKE). Known limitation, not a breakage: Paste enablement is
plain-text-only (`ClipboardPasteState.hasClip = hasText`, see P2 below). plain-text-only (`ClipboardPasteState.hasClip = hasText`, see P2 below).
- [ ] **P1** DatePicker/TimePicker localization - VERIFIED current state: - [ ] **P1** DatePicker/TimePicker localization - VERIFIED current state:
`material3/.../internal/PlatformDateFormat.native.kt` is a real kotlinx-datetime `material3/.../internal/PlatformDateFormat.native.kt` is a real kotlinx-datetime
formatter that DOES honor CLDR patterns/skeletons (renders "Jul 29, 2026" / formatter that DOES honor CLDR patterns/skeletons (renders "Jul 29, 2026" /
@@ -417,14 +417,14 @@ re-vendoring.** The debt is **completing native-actual stubs**.
(author):** bundle a CLDR subset or K/N i18n lib for localized names + locale-aware (author):** bundle a CLDR subset or K/N i18n lib for localized names + locale-aware
first-day/24h; also unblocks `CalendarLocale.native.kt:20` (fixed `"en"`). Ships first-day/24h; also unblocks `CalendarLocale.native.kt:20` (fixed `"en"`). Ships
readable English dates today - polish, not a P0 gate. readable English dates today - polish, not a P0 gate.
- [x] **P0** Float pointer coordinates - `SDL3EventMapper.kt` `.toInt()`-truncated SDL's - [x] **P0** Float pointer coordinates - `AppEvent.kt` `.toInt()`-truncated SDL's
Float `mb.x`/`mb.y`/`mm.x`/`mm.y` and the wheel `mouse_x/y` before the DPR multiply in Float `mb.x`/`mb.y`/`mm.x`/`mm.y` and the wheel `mouse_x/y` before the DPR multiply in
`ComposeWindow.kt`. On 2× displays a click at logical 100.9 → physical 200 not ~201, `ComposeWindow.kt`. On 2× displays a click at logical 100.9 → physical 200 not ~201,
quantizing the caret to 2px steps near glyph edges. **DONE:** widened quantizing the caret to 2px steps near glyph edges. **DONE:** widened
`LegacyPointerEvent.x/y` and `AppEvent.MouseWheel.x/y` to `Float`, dropped the `LegacyPointerEvent.x/y` and `AppEvent.MouseWheel.x/y` to `Float`, dropped the
`.toInt()` at the three mapper construction sites, and removed the now-redundant `.toInt()` at the three mapper construction sites, and removed the now-redundant
`.toFloat()` at the two `ComposeWindow` read sites. Consumers fully contained (verified `.toFloat()` at the two `ComposeWindow` read sites. Consumers fully contained (verified
by grep). Builds + runs clean on macosArm64. by grep). Builds + runs clean on macosArm64.
### P1 - quality / parity ### P1 - quality / parity
@@ -521,7 +521,7 @@ re-vendoring.** The debt is **completing native-actual stubs**.
Static lib built from source per host (`scripts/build-sdl/build-all.py`, ref Static lib built from source per host (`scripts/build-sdl/build-all.py`, ref
`release-3.4.12`); linked into the exe via `sdl3.def` (`staticLibraries=libSDL3.a`). Today `release-3.4.12`); linked into the exe via `sdl3.def` (`staticLibraries=libSDL3.a`). Today
only tests/examples + Windows D3D12/GPU are off (`build-all.py:311-319`). `SDL_Init` uses only tests/examples + Windows D3D12/GPU are off (`build-all.py:311-319`). `SDL_Init` uses
**`SDL_INIT_VIDEO` only** (`SDL3Backend.kt:49`). **`SDL_INIT_VIDEO` only** (`Sdl3Backend.kt:49`).
**USED (keep ON):** Video/window, Events, Clipboard, Dialog (file open/save - no cheaper **USED (keep ON):** Video/window, Events, Clipboard, Dialog (file open/save - no cheaper
substitute; Linux uses portal/zenity, keep deps), OpenGL + Metal contexts, Render (2D CPU- substitute; Linux uses portal/zenity, keep deps), OpenGL + Metal contexts, Render (2D CPU-
@@ -554,7 +554,7 @@ joystick.
disabled SDL subsystems.) disabled SDL subsystems.)
- [ ] **P2** (higher-risk, flag-don't-apply) Render-driver pruning to software-only. Only - [ ] **P2** (higher-risk, flag-don't-apply) Render-driver pruning to software-only. Only
the CPU-raster fallback uses `SDL_Render`; GL/Metal go direct. But the CPU-raster fallback uses `SDL_Render`; GL/Metal go direct. But
`SDL_CreateRenderer(window, null)` (`SDL3Backend.kt:117`) lets SDL pick the first `SDL_CreateRenderer(window, null)` (`Sdl3Backend.kt:117`) lets SDL pick the first
driver - with only software present it picks software (fine) but couples the fallback driver - with only software present it picks software (fine) but couples the fallback
to that assumption. **Conservative: leave render drivers alone.** to that assumption. **Conservative: leave render drivers alone.**
@@ -732,7 +732,7 @@ Rule-3 vendors (like `FontRasterizationSettings.native.kt` now has a `VENDOR-BAS
The multi-window probe exited 139 with no output, so it had silently been The multi-window probe exited 139 with no output, so it had silently been
asserting nothing. Root cause: an OpenGL context is per-WINDOW but "current" is asserting nothing. Root cause: an OpenGL context is per-WINDOW but "current" is
per-THREAD, and `SDL_GL_MakeCurrent` ran only once at creation per-THREAD, and `SDL_GL_MakeCurrent` ran only once at creation
(`SDL3Backend.init`) with nothing re-binding it per frame. With two windows open, (`Sdl3Backend.init`) with nothing re-binding it per frame. With two windows open,
whichever initialised last owned the thread - so window A issued its draw and whichever initialised last owned the thread - so window A issued its draw and
swap against window B's context (silent corruption while both lived), and swap against window B's context (silent corruption while both lived), and
destroying B left the current context dangling, faulting in `present()`. destroying B left the current context dangling, faulting in `present()`.
+4 -11
View File
@@ -273,17 +273,6 @@ mirrors or you will have every class twice.
| Collection | `androidx.collection:collection:1.5.0` | | Collection | `androidx.collection:collection:1.5.0` |
| Graphics Shapes | `androidx.graphics:graphics-shapes:1.1.0` | | Graphics Shapes | `androidx.graphics:graphics-shapes:1.1.0` |
### Infrastructure
| Library | Version |
|---|---|
| kotlinx-coroutines | 1.11.0 |
| kotlinx-serialization | 1.11.0 |
| kotlinx-datetime | 0.8.0 |
| kotlinx-io-okio | 0.9.1 |
| Okio | 3.18.2 |
| Ktor | 3.5.2 |
## Building ## Building
Build the native libraries once per machine, then build any app target: Build the native libraries once per machine, then build any app target:
@@ -298,6 +287,10 @@ command line tools; Linux needs gcc/g++ and the X11 / Wayland / audio dev
headers; Windows needs a mingw-w64 g++ on PATH. See [TOOLING.md](TOOLING.md) for headers; Windows needs a mingw-w64 g++ on PATH. See [TOOLING.md](TOOLING.md) for
the full build and verification workflow. the full build and verification workflow.
## Support
[Bitsycore's Discord](https://discord.gg/kGnraVAtDR)
## Documentation ## Documentation
- [TOOLING.md](TOOLING.md): building the native libs, vendoring, and verification. - [TOOLING.md](TOOLING.md): building the native libs, vendoring, and verification.
@@ -11,12 +11,6 @@ plugins {
alias(libs.plugins.kotlin.plugin.compose) alias(libs.plugins.kotlin.plugin.compose)
} }
repositories {
google()
mavenCentral()
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
}
val vHostSupportsMingw = rootProject.extra["vHostSupportsMingw"] as Boolean val vHostSupportsMingw = rootProject.extra["vHostSupportsMingw"] as Boolean
kotlin { kotlin {
@@ -14,7 +14,6 @@ import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.snapshots.Snapshot import androidx.compose.runtime.snapshots.Snapshot
import com.compose.sdl.res.currentImageLoader
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain import kotlinx.coroutines.test.setMain
@@ -141,7 +140,7 @@ fun nativeComposeApp(content: @Composable ApplicationScope.() -> Unit) {
runtime.scope = this runtime.scope = this
// App-level composition: no UI tree - it only declares Window()s. // App-level composition: no UI tree - it only declares Window()s.
val appClock = SDL3FrameClock() val appClock = Sdl3FrameClock()
val appRecomposer = Recomposer(coroutineContext + appClock) val appRecomposer = Recomposer(coroutineContext + appClock)
// The clock must be in the collector's context - runRecomposeAndApplyChanges // The clock must be in the collector's context - runRecomposeAndApplyChanges
// awaits parent frames through it. // awaits parent frames through it.
@@ -20,7 +20,7 @@ import sdl3.SDL_GetTicksNS
Both come from delegating to the runtime's BroadcastFrameClock, which is Both come from delegating to the runtime's BroadcastFrameClock, which is
exactly upstream's dispatch structure. */ exactly upstream's dispatch structure. */
internal class SDL3FrameClock : MonotonicFrameClock { internal class Sdl3FrameClock : MonotonicFrameClock {
private val broadcast = BroadcastFrameClock() private val broadcast = BroadcastFrameClock()
override suspend fun <R> withFrameNanos(onFrame: (Long) -> R): R = override suspend fun <R> withFrameNanos(onFrame: (Long) -> R): R =
@@ -56,7 +56,7 @@ internal class WindowInstance(
private val initialHeight = inHeight private val initialHeight = inHeight
private val icon = inIcon private val icon = inIcon
// Assigned by init() from the first backend that comes up (see makeBackend()). // Assigned by init() from the first backend that comes up (see makeBackend()).
lateinit var backend: SDL3Backend lateinit var backend: Sdl3Backend
private set private set
private var renderBackend: RenderBackend? = null private var renderBackend: RenderBackend? = null
lateinit var host: ComposeRootHost lateinit var host: ComposeRootHost
@@ -65,7 +65,7 @@ internal class WindowInstance(
private set private set
private var popupHost: com.compose.sdl.window.PopupHostState? = null private var popupHost: com.compose.sdl.window.PopupHostState? = null
val frameClock = SDL3FrameClock() val frameClock = Sdl3FrameClock()
private var recomposer: Recomposer? = null private var recomposer: Recomposer? = null
private var recomposeJob: Job? = null private var recomposeJob: Job? = null
private var composition: Composition? = null private var composition: Composition? = null
@@ -128,11 +128,11 @@ internal class WindowInstance(
needsFrame = true needsFrame = true
} }
// Creates + initialises an SDL3Backend for [mode] and its RenderBackend. On // Creates + initialises an Sdl3Backend for [mode] and its RenderBackend. On
// success commits `backend` and returns the renderer; else tears everything // success commits `backend` and returns the renderer; else tears everything
// down and returns null so the caller can retry with a different mode. // down and returns null so the caller can retry with a different mode.
private fun makeBackend(mode: GpuMode): RenderBackend? { private fun makeBackend(mode: GpuMode): RenderBackend? {
val vBackend = SDL3Backend( val vBackend = Sdl3Backend(
initialTitle, initialWidth, initialHeight, gpuMode = mode, initialTitle, initialWidth, initialHeight, gpuMode = mode,
iconLightResourcePaths = icon?.light ?: emptyList(), iconLightResourcePaths = icon?.light ?: emptyList(),
iconDarkResourcePaths = icon?.dark ?: emptyList(), iconDarkResourcePaths = icon?.dark ?: emptyList(),
+34 -34
View File
@@ -2827,7 +2827,7 @@ final class com.compose.sdl.window/PopupHostState { // com.compose.sdl.window/Po
} }
final class com.compose.sdl/ComposeNativeWindow { // com.compose.sdl/ComposeNativeWindow|null[0] final class com.compose.sdl/ComposeNativeWindow { // com.compose.sdl/ComposeNativeWindow|null[0]
constructor <init>(com.compose.sdl/SDL3Backend, com.compose.sdl/GpuMode, kotlin/String) // com.compose.sdl/ComposeNativeWindow.<init>|<init>(com.compose.sdl.SDL3Backend;com.compose.sdl.GpuMode;kotlin.String){}[0] constructor <init>(com.compose.sdl/Sdl3Backend, com.compose.sdl/GpuMode, kotlin/String) // com.compose.sdl/ComposeNativeWindow.<init>|<init>(com.compose.sdl.Sdl3Backend;com.compose.sdl.GpuMode;kotlin.String){}[0]
final val fps // com.compose.sdl/ComposeNativeWindow.fps|{}fps[0] final val fps // com.compose.sdl/ComposeNativeWindow.fps|{}fps[0]
final fun <get-fps>(): kotlin/Int // com.compose.sdl/ComposeNativeWindow.fps.<get-fps>|<get-fps>(){}[0] final fun <get-fps>(): kotlin/Int // com.compose.sdl/ComposeNativeWindow.fps.<get-fps>|<get-fps>(){}[0]
@@ -2912,39 +2912,39 @@ final class com.compose.sdl/RawSdlHandles { // com.compose.sdl/RawSdlHandles|nul
final fun toString(): kotlin/String // com.compose.sdl/RawSdlHandles.toString|toString(){}[0] final fun toString(): kotlin/String // com.compose.sdl/RawSdlHandles.toString|toString(){}[0]
} }
final class com.compose.sdl/SDL3Backend { // com.compose.sdl/SDL3Backend|null[0] final class com.compose.sdl/Sdl3Backend { // com.compose.sdl/Sdl3Backend|null[0]
constructor <init>(kotlin/String = ..., kotlin/Int = ..., kotlin/Int = ..., com.compose.sdl/GpuMode = ..., kotlin.collections/List<kotlin/String> = ..., kotlin.collections/List<kotlin/String> = ..., kotlin/Boolean = ..., kotlin/Boolean = ..., kotlin/Boolean = ..., kotlin/Boolean = ..., kotlin/Boolean = ...) // com.compose.sdl/SDL3Backend.<init>|<init>(kotlin.String;kotlin.Int;kotlin.Int;com.compose.sdl.GpuMode;kotlin.collections.List<kotlin.String>;kotlin.collections.List<kotlin.String>;kotlin.Boolean;kotlin.Boolean;kotlin.Boolean;kotlin.Boolean;kotlin.Boolean){}[0] constructor <init>(kotlin/String = ..., kotlin/Int = ..., kotlin/Int = ..., com.compose.sdl/GpuMode = ..., kotlin.collections/List<kotlin/String> = ..., kotlin.collections/List<kotlin/String> = ..., kotlin/Boolean = ..., kotlin/Boolean = ..., kotlin/Boolean = ..., kotlin/Boolean = ..., kotlin/Boolean = ...) // com.compose.sdl/Sdl3Backend.<init>|<init>(kotlin.String;kotlin.Int;kotlin.Int;com.compose.sdl.GpuMode;kotlin.collections.List<kotlin.String>;kotlin.collections.List<kotlin.String>;kotlin.Boolean;kotlin.Boolean;kotlin.Boolean;kotlin.Boolean;kotlin.Boolean){}[0]
final val gpuMode // com.compose.sdl/SDL3Backend.gpuMode|{}gpuMode[0] final val gpuMode // com.compose.sdl/Sdl3Backend.gpuMode|{}gpuMode[0]
final fun <get-gpuMode>(): com.compose.sdl/GpuMode // com.compose.sdl/SDL3Backend.gpuMode.<get-gpuMode>|<get-gpuMode>(){}[0] final fun <get-gpuMode>(): com.compose.sdl/GpuMode // com.compose.sdl/Sdl3Backend.gpuMode.<get-gpuMode>|<get-gpuMode>(){}[0]
final val pixelDensity // com.compose.sdl/SDL3Backend.pixelDensity|{}pixelDensity[0] final val pixelDensity // com.compose.sdl/Sdl3Backend.pixelDensity|{}pixelDensity[0]
final fun <get-pixelDensity>(): kotlin/Float // com.compose.sdl/SDL3Backend.pixelDensity.<get-pixelDensity>|<get-pixelDensity>(){}[0] final fun <get-pixelDensity>(): kotlin/Float // com.compose.sdl/Sdl3Backend.pixelDensity.<get-pixelDensity>|<get-pixelDensity>(){}[0]
final val windowId // com.compose.sdl/SDL3Backend.windowId|{}windowId[0] final val windowId // com.compose.sdl/Sdl3Backend.windowId|{}windowId[0]
final fun <get-windowId>(): kotlin/UInt // com.compose.sdl/SDL3Backend.windowId.<get-windowId>|<get-windowId>(){}[0] final fun <get-windowId>(): kotlin/UInt // com.compose.sdl/Sdl3Backend.windowId.<get-windowId>|<get-windowId>(){}[0]
final var glContext // com.compose.sdl/SDL3Backend.glContext|{}glContext[0] final var glContext // com.compose.sdl/Sdl3Backend.glContext|{}glContext[0]
final fun <get-glContext>(): kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? // com.compose.sdl/SDL3Backend.glContext.<get-glContext>|<get-glContext>(){}[0] final fun <get-glContext>(): kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? // com.compose.sdl/Sdl3Backend.glContext.<get-glContext>|<get-glContext>(){}[0]
final var metalView // com.compose.sdl/SDL3Backend.metalView|{}metalView[0] final var metalView // com.compose.sdl/Sdl3Backend.metalView|{}metalView[0]
final fun <get-metalView>(): kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? // com.compose.sdl/SDL3Backend.metalView.<get-metalView>|<get-metalView>(){}[0] final fun <get-metalView>(): kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? // com.compose.sdl/Sdl3Backend.metalView.<get-metalView>|<get-metalView>(){}[0]
final var pixelHeight // com.compose.sdl/SDL3Backend.pixelHeight|{}pixelHeight[0] final var pixelHeight // com.compose.sdl/Sdl3Backend.pixelHeight|{}pixelHeight[0]
final fun <get-pixelHeight>(): kotlin/Int // com.compose.sdl/SDL3Backend.pixelHeight.<get-pixelHeight>|<get-pixelHeight>(){}[0] final fun <get-pixelHeight>(): kotlin/Int // com.compose.sdl/Sdl3Backend.pixelHeight.<get-pixelHeight>|<get-pixelHeight>(){}[0]
final var pixelWidth // com.compose.sdl/SDL3Backend.pixelWidth|{}pixelWidth[0] final var pixelWidth // com.compose.sdl/Sdl3Backend.pixelWidth|{}pixelWidth[0]
final fun <get-pixelWidth>(): kotlin/Int // com.compose.sdl/SDL3Backend.pixelWidth.<get-pixelWidth>|<get-pixelWidth>(){}[0] final fun <get-pixelWidth>(): kotlin/Int // com.compose.sdl/Sdl3Backend.pixelWidth.<get-pixelWidth>|<get-pixelWidth>(){}[0]
final var renderer // com.compose.sdl/SDL3Backend.renderer|{}renderer[0] final var renderer // com.compose.sdl/Sdl3Backend.renderer|{}renderer[0]
final fun <get-renderer>(): kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? // com.compose.sdl/SDL3Backend.renderer.<get-renderer>|<get-renderer>(){}[0] final fun <get-renderer>(): kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? // com.compose.sdl/Sdl3Backend.renderer.<get-renderer>|<get-renderer>(){}[0]
final var vsyncEnabled // com.compose.sdl/SDL3Backend.vsyncEnabled|{}vsyncEnabled[0] final var vsyncEnabled // com.compose.sdl/Sdl3Backend.vsyncEnabled|{}vsyncEnabled[0]
final fun <get-vsyncEnabled>(): kotlin/Boolean // com.compose.sdl/SDL3Backend.vsyncEnabled.<get-vsyncEnabled>|<get-vsyncEnabled>(){}[0] final fun <get-vsyncEnabled>(): kotlin/Boolean // com.compose.sdl/Sdl3Backend.vsyncEnabled.<get-vsyncEnabled>|<get-vsyncEnabled>(){}[0]
final var window // com.compose.sdl/SDL3Backend.window|{}window[0] final var window // com.compose.sdl/Sdl3Backend.window|{}window[0]
final fun <get-window>(): kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? // com.compose.sdl/SDL3Backend.window.<get-window>|<get-window>(){}[0] final fun <get-window>(): kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? // com.compose.sdl/Sdl3Backend.window.<get-window>|<get-window>(){}[0]
final var windowHeight // com.compose.sdl/SDL3Backend.windowHeight|{}windowHeight[0] final var windowHeight // com.compose.sdl/Sdl3Backend.windowHeight|{}windowHeight[0]
final fun <get-windowHeight>(): kotlin/Int // com.compose.sdl/SDL3Backend.windowHeight.<get-windowHeight>|<get-windowHeight>(){}[0] final fun <get-windowHeight>(): kotlin/Int // com.compose.sdl/Sdl3Backend.windowHeight.<get-windowHeight>|<get-windowHeight>(){}[0]
final var windowWidth // com.compose.sdl/SDL3Backend.windowWidth|{}windowWidth[0] final var windowWidth // com.compose.sdl/Sdl3Backend.windowWidth|{}windowWidth[0]
final fun <get-windowWidth>(): kotlin/Int // com.compose.sdl/SDL3Backend.windowWidth.<get-windowWidth>|<get-windowWidth>(){}[0] final fun <get-windowWidth>(): kotlin/Int // com.compose.sdl/Sdl3Backend.windowWidth.<get-windowWidth>|<get-windowWidth>(){}[0]
final fun applyThemeIcon() // com.compose.sdl/SDL3Backend.applyThemeIcon|applyThemeIcon(){}[0] final fun applyThemeIcon() // com.compose.sdl/Sdl3Backend.applyThemeIcon|applyThemeIcon(){}[0]
final fun destroy(kotlin/Boolean = ...) // com.compose.sdl/SDL3Backend.destroy|destroy(kotlin.Boolean){}[0] final fun destroy(kotlin/Boolean = ...) // com.compose.sdl/Sdl3Backend.destroy|destroy(kotlin.Boolean){}[0]
final fun init(): kotlin/Boolean // com.compose.sdl/SDL3Backend.init|init(){}[0] final fun init(): kotlin/Boolean // com.compose.sdl/Sdl3Backend.init|init(){}[0]
final fun updateWindowSize() // com.compose.sdl/SDL3Backend.updateWindowSize|updateWindowSize(){}[0] final fun updateWindowSize() // com.compose.sdl/Sdl3Backend.updateWindowSize|updateWindowSize(){}[0]
} }
final value class <#A: kotlin/Any?> androidx.compose.ui/SessionMutex { // androidx.compose.ui/SessionMutex|null[0] final value class <#A: kotlin/Any?> androidx.compose.ui/SessionMutex { // androidx.compose.ui/SessionMutex|null[0]
@@ -4863,7 +4863,7 @@ final val com.compose.sdl/com_compose_sdl_GpuMode_CpuRaster$stableprop // com.co
final val com.compose.sdl/com_compose_sdl_GpuMode_Metal$stableprop // com.compose.sdl/com_compose_sdl_GpuMode_Metal$stableprop|#static{}com_compose_sdl_GpuMode_Metal$stableprop[0] final val com.compose.sdl/com_compose_sdl_GpuMode_Metal$stableprop // com.compose.sdl/com_compose_sdl_GpuMode_Metal$stableprop|#static{}com_compose_sdl_GpuMode_Metal$stableprop[0]
final val com.compose.sdl/com_compose_sdl_GpuMode_OpenGL$stableprop // com.compose.sdl/com_compose_sdl_GpuMode_OpenGL$stableprop|#static{}com_compose_sdl_GpuMode_OpenGL$stableprop[0] final val com.compose.sdl/com_compose_sdl_GpuMode_OpenGL$stableprop // com.compose.sdl/com_compose_sdl_GpuMode_OpenGL$stableprop|#static{}com_compose_sdl_GpuMode_OpenGL$stableprop[0]
final val com.compose.sdl/com_compose_sdl_RawSdlHandles$stableprop // com.compose.sdl/com_compose_sdl_RawSdlHandles$stableprop|#static{}com_compose_sdl_RawSdlHandles$stableprop[0] final val com.compose.sdl/com_compose_sdl_RawSdlHandles$stableprop // com.compose.sdl/com_compose_sdl_RawSdlHandles$stableprop|#static{}com_compose_sdl_RawSdlHandles$stableprop[0]
final val com.compose.sdl/com_compose_sdl_SDL3Backend$stableprop // com.compose.sdl/com_compose_sdl_SDL3Backend$stableprop|#static{}com_compose_sdl_SDL3Backend$stableprop[0] final val com.compose.sdl/com_compose_sdl_Sdl3Backend$stableprop // com.compose.sdl/com_compose_sdl_Sdl3Backend$stableprop|#static{}com_compose_sdl_Sdl3Backend$stableprop[0]
final val com.compose.sdl/com_compose_sdl_TextLayoutConfig$stableprop // com.compose.sdl/com_compose_sdl_TextLayoutConfig$stableprop|#static{}com_compose_sdl_TextLayoutConfig$stableprop[0] final val com.compose.sdl/com_compose_sdl_TextLayoutConfig$stableprop // com.compose.sdl/com_compose_sdl_TextLayoutConfig$stableprop|#static{}com_compose_sdl_TextLayoutConfig$stableprop[0]
final var androidx.compose.ui.platform/isDebugInspectorInfoEnabled // androidx.compose.ui.platform/isDebugInspectorInfoEnabled|{}isDebugInspectorInfoEnabled[0] final var androidx.compose.ui.platform/isDebugInspectorInfoEnabled // androidx.compose.ui.platform/isDebugInspectorInfoEnabled|{}isDebugInspectorInfoEnabled[0]
@@ -5387,9 +5387,9 @@ final fun com.compose.sdl/com_compose_sdl_GpuMode_CpuRaster$stableprop_getter():
final fun com.compose.sdl/com_compose_sdl_GpuMode_Metal$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_GpuMode_Metal$stableprop_getter|com_compose_sdl_GpuMode_Metal$stableprop_getter(){}[0] final fun com.compose.sdl/com_compose_sdl_GpuMode_Metal$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_GpuMode_Metal$stableprop_getter|com_compose_sdl_GpuMode_Metal$stableprop_getter(){}[0]
final fun com.compose.sdl/com_compose_sdl_GpuMode_OpenGL$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_GpuMode_OpenGL$stableprop_getter|com_compose_sdl_GpuMode_OpenGL$stableprop_getter(){}[0] final fun com.compose.sdl/com_compose_sdl_GpuMode_OpenGL$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_GpuMode_OpenGL$stableprop_getter|com_compose_sdl_GpuMode_OpenGL$stableprop_getter(){}[0]
final fun com.compose.sdl/com_compose_sdl_RawSdlHandles$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_RawSdlHandles$stableprop_getter|com_compose_sdl_RawSdlHandles$stableprop_getter(){}[0] final fun com.compose.sdl/com_compose_sdl_RawSdlHandles$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_RawSdlHandles$stableprop_getter|com_compose_sdl_RawSdlHandles$stableprop_getter(){}[0]
final fun com.compose.sdl/com_compose_sdl_SDL3Backend$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_SDL3Backend$stableprop_getter|com_compose_sdl_SDL3Backend$stableprop_getter(){}[0] final fun com.compose.sdl/com_compose_sdl_Sdl3Backend$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_Sdl3Backend$stableprop_getter|com_compose_sdl_Sdl3Backend$stableprop_getter(){}[0]
final fun com.compose.sdl/com_compose_sdl_TextLayoutConfig$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_TextLayoutConfig$stableprop_getter|com_compose_sdl_TextLayoutConfig$stableprop_getter(){}[0] final fun com.compose.sdl/com_compose_sdl_TextLayoutConfig$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_TextLayoutConfig$stableprop_getter|com_compose_sdl_TextLayoutConfig$stableprop_getter(){}[0]
final fun com.compose.sdl/createRenderBackend(com.compose.sdl/SDL3Backend, com.compose.sdl/GpuMode): com.compose.sdl/RenderBackend? // com.compose.sdl/createRenderBackend|createRenderBackend(com.compose.sdl.SDL3Backend;com.compose.sdl.GpuMode){}[0] final fun com.compose.sdl/createRenderBackend(com.compose.sdl/Sdl3Backend, com.compose.sdl/GpuMode): com.compose.sdl/RenderBackend? // com.compose.sdl/createRenderBackend|createRenderBackend(com.compose.sdl.Sdl3Backend;com.compose.sdl.GpuMode){}[0]
final fun com.compose.sdl/fileManagerName(): kotlin/String // com.compose.sdl/fileManagerName|fileManagerName(){}[0] final fun com.compose.sdl/fileManagerName(): kotlin/String // com.compose.sdl/fileManagerName|fileManagerName(){}[0]
final fun com.compose.sdl/hasComposeResource(kotlin/String): kotlin/Boolean // com.compose.sdl/hasComposeResource|hasComposeResource(kotlin.String){}[0] final fun com.compose.sdl/hasComposeResource(kotlin/String): kotlin/Boolean // com.compose.sdl/hasComposeResource|hasComposeResource(kotlin.String){}[0]
final fun com.compose.sdl/injectKey(kotlin/Int, kotlin/Boolean) // com.compose.sdl/injectKey|injectKey(kotlin.Int;kotlin.Boolean){}[0] final fun com.compose.sdl/injectKey(kotlin/Int, kotlin/Boolean) // com.compose.sdl/injectKey|injectKey(kotlin.Int;kotlin.Boolean){}[0]
@@ -8,7 +8,7 @@ import androidx.compose.ui.geometry.Offset
/* Actuals for the vendored `DragAndDrop.kt` expect classes. /* Actuals for the vendored `DragAndDrop.kt` expect classes.
* Backed by SDL3's SDL_EVENT_DROP_* stream (file / text / begin / position / * Backed by SDL3's SDL_EVENT_DROP_* stream (file / text / begin / position /
* complete) - see Sdl3DragAndDropOwner + SDL3EventMapper for the pump side. * complete) - see Sdl3DragAndDropOwner + AppEvent.kt for the pump side.
* *
* DragAndDropTransferData carries the payload a drop into the window * DragAndDropTransferData carries the payload a drop into the window
* delivered: zero or more file paths (SDL_EVENT_DROP_FILE) plus an optional * delivered: zero or more file paths (SDL_EVENT_DROP_FILE) plus an optional
@@ -10,7 +10,7 @@ import androidx.compose.ui.node.ModifierNodeElement
/** The per-window DragAndDropManager backing ComposeOwner. Holds a root /** The per-window DragAndDropManager backing ComposeOwner. Holds a root
* DragAndDropNode installed on the root LayoutNode via [modifier]; the SDL * DragAndDropNode installed on the root LayoutNode via [modifier]; the SDL
* event pump (see SDL3EventMapper + ComposeWindow) drives [handleDrop] which * event pump (see AppEvent.kt + ComposeWindow) drives [handleDrop] which
* accumulates BEGIN → FILE / TEXT / POSITION → COMPLETE into a single * accumulates BEGIN → FILE / TEXT / POSITION → COMPLETE into a single
* DragAndDropEvent and dispatches through the node tree. * DragAndDropEvent and dispatches through the node tree.
* *
@@ -8,8 +8,8 @@ package androidx.compose.ui.input.key
/** /**
* Project [Key] actual - assigns a unique sequential Long to each named * Project [Key] actual - assigns a unique sequential Long to each named
* key. SDL3EventMapper picks the right `Key.X` by switch on SDL scancode * key. The SDL event mapper picks the right `Key.X` by switch on SDL scancode
* (`kKeyForScancode` in `SDL3EventMapper.kt`). Comparison works because * (`kKeyForScancode` in `AppEvent.kt`). Comparison works because
* Key is a value class - `Key(42L) == Key(42L)`. * Key is a value class - `Key(42L) == Key(42L)`.
*/ */
@kotlin.jvm.JvmInline @kotlin.jvm.JvmInline
@@ -213,10 +213,10 @@ private fun sdlWriteEntry(entry: ClipEntry?) {
// The SDL3-backed Clipboard implementation. Singleton because SDL's clipboard // The SDL3-backed Clipboard implementation. Singleton because SDL's clipboard
// is process-global - allocating a new one per Window() would only waste // is process-global - allocating a new one per Window() would only waste
// memory. Consumed by :desktop-native-window when seeding LocalClipboard. // memory. Consumed by :desktop-native-window when seeding LocalClipboard.
private object SDL3Clipboard : Clipboard { private object Sdl3Clipboard : Clipboard {
override suspend fun getClipEntry(): ClipEntry? = sdlReadEntry() override suspend fun getClipEntry(): ClipEntry? = sdlReadEntry()
override suspend fun setClipEntry(clipEntry: ClipEntry?) { sdlWriteEntry(clipEntry) } override suspend fun setClipEntry(clipEntry: ClipEntry?) { sdlWriteEntry(clipEntry) }
override val nativeClipboard: NativeClipboard get() = nativeClipboardSentinel override val nativeClipboard: NativeClipboard get() = nativeClipboardSentinel
} }
fun platformClipboard(): Clipboard = SDL3Clipboard fun platformClipboard(): Clipboard = Sdl3Clipboard
@@ -9,7 +9,7 @@ import kotlinx.cinterop.*
import sdl3.* import sdl3.*
// ================== // ==================
// MARK: SDL3EventMapper // MARK: AppEvent + the SDL3 event mapper
// ================== // ==================
/** Events carry the SDL window id they belong to (0 = unknown - injected test /** Events carry the SDL window id they belong to (0 = unknown - injected test
@@ -83,8 +83,7 @@ fun pollEvents(): List<AppEvent> {
} }
private fun mapEvent(e: SDL_Event): AppEvent? { private fun mapEvent(e: SDL_Event): AppEvent? {
val type = e.type return when (e.type) {
return when (type) {
SDL_EVENT_QUIT -> AppEvent.Quit SDL_EVENT_QUIT -> AppEvent.Quit
SDL_EVENT_MOUSE_BUTTON_DOWN -> { SDL_EVENT_MOUSE_BUTTON_DOWN -> {
@@ -7,8 +7,6 @@ import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.input.key.KeyEvent import androidx.compose.ui.input.key.KeyEvent
import kotlinx.cinterop.COpaquePointer import kotlinx.cinterop.COpaquePointer
import kotlinx.cinterop.reinterpret import kotlinx.cinterop.reinterpret
import kotlinx.cinterop.toKString
import sdl3.SDL_GetRendererName
import sdl3.SDL_MaximizeWindow import sdl3.SDL_MaximizeWindow
import sdl3.SDL_MinimizeWindow import sdl3.SDL_MinimizeWindow
import sdl3.SDL_RaiseWindow import sdl3.SDL_RaiseWindow
@@ -36,7 +34,7 @@ import sdl3.SDL_SetWindowTitle
them subscribes the caller to recomposition. The setter methods them subscribes the caller to recomposition. The setter methods
call into SDL and also push the new value into the snapshot. */ call into SDL and also push the new value into the snapshot. */
class ComposeNativeWindow constructor( class ComposeNativeWindow constructor(
private val backend: SDL3Backend, private val backend: Sdl3Backend,
val gpuMode: GpuMode, val gpuMode: GpuMode,
initialTitle: String, initialTitle: String,
) { ) {
@@ -16,7 +16,7 @@ package com.compose.sdl
/** Create the render backend for the selected [GpuMode]; null when the /** Create the render backend for the selected [GpuMode]; null when the
backend can't initialise (caller falls back / reports). */ backend can't initialise (caller falls back / reports). */
expect fun createRenderBackend(inSdl: SDL3Backend, inGpu: GpuMode): RenderBackend? expect fun createRenderBackend(inSdl: Sdl3Backend, inGpu: GpuMode): RenderBackend?
/** The renderer module's per-OS default GpuMode (used for GpuMode.Auto). */ /** The renderer module's per-OS default GpuMode (used for GpuMode.Auto). */
expect fun rendererPreferredGpuMode(): GpuMode expect fun rendererPreferredGpuMode(): GpuMode
@@ -4,10 +4,10 @@ import kotlinx.cinterop.*
import sdl3.* import sdl3.*
// ================== // ==================
// MARK: SDL3Backend // MARK: Sdl3Backend
// ================== // ==================
class SDL3Backend( class Sdl3Backend(
private val title: String = "ComposeNativeSDL3", private val title: String = "ComposeNativeSDL3",
private val width: Int = 800, private val width: Int = 800,
private val height: Int = 600, private val height: Int = 600,
@@ -29,7 +29,7 @@ class SDL3Backend(
) { ) {
init { init {
require(gpuMode !is GpuMode.Auto) { require(gpuMode !is GpuMode.Auto) {
"SDL3Backend received GpuMode.Auto - resolve via preferredGpuMode() first" "Sdl3Backend received GpuMode.Auto - resolve via preferredGpuMode() first"
} }
} }
@@ -6,4 +6,4 @@ import com.compose.sdl.renderer.skia.SkiaBridge
// MARK: Linux GPU defaults // MARK: Linux GPU defaults
// ================== // ==================
internal actual fun makeMetalBridge(backend: SDL3Backend): SkiaBridge? = null internal actual fun makeMetalBridge(backend: Sdl3Backend): SkiaBridge? = null
@@ -3,7 +3,7 @@ package com.compose.sdl
import com.compose.sdl.renderer.skia.SkiaBridge import com.compose.sdl.renderer.skia.SkiaBridge
import com.compose.sdl.renderer.skia.SkiaMetalBridge import com.compose.sdl.renderer.skia.SkiaMetalBridge
internal actual fun makeMetalBridge(backend: SDL3Backend): SkiaBridge? { internal actual fun makeMetalBridge(backend: Sdl3Backend): SkiaBridge? {
val bridge = SkiaMetalBridge(backend) val bridge = SkiaMetalBridge(backend)
return if (bridge.init()) bridge else null return if (bridge.init()) bridge else null
} }
@@ -37,7 +37,7 @@ import sdl3.SDL_Metal_GetLayer
per frame, so we tear the BackendRenderTarget + Surface down every frame per frame, so we tear the BackendRenderTarget + Surface down every frame
(cheap - the DirectContext + device + queue persist). */ (cheap - the DirectContext + device + queue persist). */
@OptIn(ExperimentalForeignApi::class, BetaInteropApi::class) @OptIn(ExperimentalForeignApi::class, BetaInteropApi::class)
internal class SkiaMetalBridge(private val backend: SDL3Backend) : SkiaBridge { internal class SkiaMetalBridge(private val backend: Sdl3Backend) : SkiaBridge {
private var mDevice: MTLDeviceProtocol? = null private var mDevice: MTLDeviceProtocol? = null
private var mQueue: MTLCommandQueueProtocol? = null private var mQueue: MTLCommandQueueProtocol? = null
private var mLayer: CAMetalLayer? = null private var mLayer: CAMetalLayer? = null
@@ -7,7 +7,7 @@ import kotlin.experimental.ExperimentalNativeApi
// MARK: Metal bridge factory (skia-only) // MARK: Metal bridge factory (skia-only)
// ================== // ==================
internal expect fun makeMetalBridge(backend: SDL3Backend): SkiaBridge? internal expect fun makeMetalBridge(backend: Sdl3Backend): SkiaBridge?
actual fun rendererPreferredGpuMode(): GpuMode { actual fun rendererPreferredGpuMode(): GpuMode {
@OptIn(ExperimentalNativeApi::class) @OptIn(ExperimentalNativeApi::class)
@@ -10,7 +10,7 @@ import com.compose.sdl.renderer.skia.SkiaRenderBackend
actual delegates here when Skia is the selected renderer). Mirrors the actual delegates here when Skia is the selected renderer). Mirrors the
SDL3 module's createRenderBackend / rendererPreferredGpuMode in the same SDL3 module's createRenderBackend / rendererPreferredGpuMode in the same
package; exactly one renderer module is included per target. */ package; exactly one renderer module is included per target. */
actual fun createRenderBackend(inSdl: SDL3Backend, inGpu: GpuMode): RenderBackend? { actual fun createRenderBackend(inSdl: Sdl3Backend, inGpu: GpuMode): RenderBackend? {
val vResolved = if (inGpu is GpuMode.Auto) rendererPreferredGpuMode() else inGpu val vResolved = if (inGpu is GpuMode.Auto) rendererPreferredGpuMode() else inGpu
return try { return try {
SkiaRenderBackend(inSdl, vResolved) SkiaRenderBackend(inSdl, vResolved)
@@ -4,13 +4,9 @@ import com.compose.sdl.*
import kotlinx.cinterop.* import kotlinx.cinterop.*
import org.jetbrains.skia.BackendRenderTarget import org.jetbrains.skia.BackendRenderTarget
import org.jetbrains.skia.Bitmap
import org.jetbrains.skia.Canvas import org.jetbrains.skia.Canvas
import org.jetbrains.skia.ColorAlphaType
import org.jetbrains.skia.ColorType
import org.jetbrains.skia.DirectContext import org.jetbrains.skia.DirectContext
import org.jetbrains.skia.Image import org.jetbrains.skia.Image
import org.jetbrains.skia.ImageInfo
import org.jetbrains.skia.Surface import org.jetbrains.skia.Surface
import org.jetbrains.skia.SurfaceColorFormat import org.jetbrains.skia.SurfaceColorFormat
import org.jetbrains.skia.SurfaceOrigin import org.jetbrains.skia.SurfaceOrigin
@@ -28,7 +24,7 @@ import sdl3.SDL_GL_SwapWindow
Resize: tear down and rebuild the BackendRenderTarget + Surface; the Resize: tear down and rebuild the BackendRenderTarget + Surface; the
DirectContext is reused. The default GL framebuffer (id 0) is wrapped DirectContext is reused. The default GL framebuffer (id 0) is wrapped
directly; we don't manage an offscreen FBO. */ directly; we don't manage an offscreen FBO. */
internal class SkiaGLBridge(private val backend: SDL3Backend) : SkiaBridge { internal class SkiaGLBridge(private val backend: Sdl3Backend) : SkiaBridge {
private var mContext: DirectContext? = null private var mContext: DirectContext? = null
private var mRT: BackendRenderTarget? = null private var mRT: BackendRenderTarget? = null
private var mSurface: Surface? = null private var mSurface: Surface? = null
@@ -40,7 +36,7 @@ internal class SkiaGLBridge(private val backend: SDL3Backend) : SkiaBridge {
A GL context is per-window but "current" is per-THREAD state, so with more A GL context is per-window but "current" is per-THREAD state, so with more
than one window open the last one to touch GL owns the thread. SDL makes a than one window open the last one to touch GL owns the thread. SDL makes a
context current once at creation (SDL3Backend.init) and nothing re-bound it context current once at creation (Sdl3Backend.init) and nothing re-bound it
per frame, so window A issued its draw and swap against window B's context: per frame, so window A issued its draw and swap against window B's context:
silent corruption while both live, and a hard crash (SIGSEGV in present) silent corruption while both live, and a hard crash (SIGSEGV in present)
the moment B is destroyed and the current context is left dangling. the moment B is destroyed and the current context is left dangling.
@@ -25,7 +25,7 @@ import org.jetbrains.skia.Color as SkColor
elevation shadows ride behind it via skiaLeafDrawer + the SkiaBackedCanvas elevation shadows ride behind it via skiaLeafDrawer + the SkiaBackedCanvas
manual-vendor. */ manual-vendor. */
internal class SkiaRenderBackend( internal class SkiaRenderBackend(
private val sdl: SDL3Backend, private val sdl: Sdl3Backend,
private val gpuMode: GpuMode, private val gpuMode: GpuMode,
) : RenderBackend { ) : RenderBackend {
@@ -18,7 +18,7 @@ import sdl3.*
/** Owns the CPU pixel buffer Skia draws into, plus the streaming SDL_Texture /** Owns the CPU pixel buffer Skia draws into, plus the streaming SDL_Texture
used to push that buffer onto the window. All three (buffer, Skia surface, used to push that buffer onto the window. All three (buffer, Skia surface,
SDL texture) get reallocated when the window resizes. */ SDL texture) get reallocated when the window resizes. */
internal class SkiaSurfaceBridge(private val backend: SDL3Backend) : SkiaBridge { internal class SkiaSurfaceBridge(private val backend: Sdl3Backend) : SkiaBridge {
private var mWidth = 0 private var mWidth = 0
private var mHeight = 0 private var mHeight = 0
private var mPixels: CPointer<UByteVar>? = null private var mPixels: CPointer<UByteVar>? = null
@@ -9,4 +9,4 @@ import com.compose.sdl.renderer.skia.SkiaBridge
// No Metal on Windows. The default Skia bridge is CPU raster (GpuMode.CpuRaster → // No Metal on Windows. The default Skia bridge is CPU raster (GpuMode.CpuRaster →
// SkiaSurfaceBridge), so no GPU-context bridge is provided here yet. A D3D/Vulkan // SkiaSurfaceBridge), so no GPU-context bridge is provided here yet. A D3D/Vulkan
// bridge can be added later (Route 1a milestone 2). // bridge can be added later (Route 1a milestone 2).
internal actual fun makeMetalBridge(backend: SDL3Backend): SkiaBridge? = null internal actual fun makeMetalBridge(backend: Sdl3Backend): SkiaBridge? = null
@@ -36,7 +36,7 @@ internal fun AnimationScreen() {
"animation.core", "animation.core",
"animate*AsState, AnimationSpec (tween / spring / repeatable / infiniteRepeatable), " + "animate*AsState, AnimationSpec (tween / spring / repeatable / infiniteRepeatable), " +
"Easing (Linear, FastOutSlowIn, CubicBezier), rememberInfiniteTransition. " + "Easing (Linear, FastOutSlowIn, CubicBezier), rememberInfiniteTransition. " +
"All driven by the SDL3FrameClock via withFrameNanos.", "All driven by the Sdl3FrameClock via withFrameNanos.",
) )
// ============ // ============
@@ -31,7 +31,7 @@ internal fun GestureScreen() {
ScreenTitle( ScreenTitle(
"pointerInput + gesture DSL", "pointerInput + gesture DSL",
"Suspending Modifier.pointerInput { } with detectTapGestures / detectDragGestures " + "Suspending Modifier.pointerInput { } with detectTapGestures / detectDragGestures " +
"on top. Awaits resume on the SDL3FrameClock; no per-event polling.", "on top. Awaits resume on the Sdl3FrameClock; no per-event polling.",
) )
// ============ // ============
@@ -278,7 +278,7 @@ internal object IconCodec {
} }
/** An 8-byte little-endian [width][height] header + straight-alpha RGBA - the /** An 8-byte little-endian [width][height] header + straight-alpha RGBA - the
format the runtime (WindowIcon.kt / SDL3Backend) reads. */ format the runtime (WindowIcon.kt / Sdl3Backend) reads. */
fun rgbaBlob(inW: Int, inH: Int, inRgba: ByteArray): ByteArray { fun rgbaBlob(inW: Int, inH: Int, inRgba: ByteArray): ByteArray {
val vOut = ByteArray(8 + inRgba.size) val vOut = ByteArray(8 + inRgba.size)
le32(vOut, 0, inW) le32(vOut, 0, inW)