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** Text context menu (right-click copy/paste/select-all) - **RECONCILED: already
works, no reimplementation.** Static trace confirmed the menu is wired end-to-end
through the vendored LEGACY path: text-level `ContextMenuArea` (`ContextMenu.native.kt`)
works, no reimplementation.** Static trace confirmed the menu is wired end-to-end
through the vendored LEGACY path: text-level `ContextMenuArea` (`ContextMenu.native.kt`)
→ `CommonContextMenuArea` (`vendor/common/.../text/CommonContextMenuArea.kt`) → (native
`ComposeFoundationFlags.isNewContextMenuEnabled = false`, so the legacy branch) →
`contextmenu.ContextMenuArea` → `contextMenuGestures`/`onRightClickDown`
(`isSecondaryPressed`) → `ContextMenuPopup` → `Popup.native.kt` (hosted by
`LocalPopupHost` at the app root). Items are real: `TextFieldSelectionManager` /
`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
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.
**REMAINING:** an interactive right-click smoke check (no headless driver here - fold
into manual/WIN-SMOKE). Known limitation, not a breakage: Paste enablement is
plain-text-only (`ClipboardPasteState.hasClip = hasText`, see P2 below).
**REMAINING:** an interactive right-click smoke check (no headless driver here - fold
into manual/WIN-SMOKE). Known limitation, not a breakage: Paste enablement is
plain-text-only (`ClipboardPasteState.hasClip = hasText`, see P2 below).
- [ ] **P1** DatePicker/TimePicker localization - VERIFIED current state:
`material3/.../internal/PlatformDateFormat.native.kt` is a real kotlinx-datetime
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
first-day/24h; also unblocks `CalendarLocale.native.kt:20` (fixed `"en"`). Ships
readable English dates today - polish, not a P0 gate.
- [x] **P0** Float pointer coordinates - `SDL3EventMapper.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
- [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
`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
`.toInt()` at the three mapper construction sites, and removed the now-redundant
`.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
@@ -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
`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
**`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
substitute; Linux uses portal/zenity, keep deps), OpenGL + Metal contexts, Render (2D CPU-
@@ -554,7 +554,7 @@ joystick.
disabled SDL subsystems.)
- [ ] **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
`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
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
asserting nothing. Root cause: an OpenGL context is per-WINDOW but "current" is
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
swap against window B's context (silent corruption while both lived), and
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` |
| 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
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
the full build and verification workflow.
## Support
[Bitsycore's Discord](https://discord.gg/kGnraVAtDR)
## Documentation
- [TOOLING.md](TOOLING.md): building the native libs, vendoring, and verification.
@@ -11,12 +11,6 @@ plugins {
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
kotlin {
@@ -14,7 +14,6 @@ import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.snapshots.Snapshot
import com.compose.sdl.res.currentImageLoader
import kotlinx.coroutines.*
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain
@@ -141,7 +140,7 @@ fun nativeComposeApp(content: @Composable ApplicationScope.() -> Unit) {
runtime.scope = this
// App-level composition: no UI tree - it only declares Window()s.
val appClock = SDL3FrameClock()
val appClock = Sdl3FrameClock()
val appRecomposer = Recomposer(coroutineContext + appClock)
// The clock must be in the collector's context - runRecomposeAndApplyChanges
// awaits parent frames through it.
@@ -20,7 +20,7 @@ import sdl3.SDL_GetTicksNS
Both come from delegating to the runtime's BroadcastFrameClock, which is
exactly upstream's dispatch structure. */
internal class SDL3FrameClock : MonotonicFrameClock {
internal class Sdl3FrameClock : MonotonicFrameClock {
private val broadcast = BroadcastFrameClock()
override suspend fun <R> withFrameNanos(onFrame: (Long) -> R): R =
@@ -56,7 +56,7 @@ internal class WindowInstance(
private val initialHeight = inHeight
private val icon = inIcon
// Assigned by init() from the first backend that comes up (see makeBackend()).
lateinit var backend: SDL3Backend
lateinit var backend: Sdl3Backend
private set
private var renderBackend: RenderBackend? = null
lateinit var host: ComposeRootHost
@@ -65,7 +65,7 @@ internal class WindowInstance(
private set
private var popupHost: com.compose.sdl.window.PopupHostState? = null
val frameClock = SDL3FrameClock()
val frameClock = Sdl3FrameClock()
private var recomposer: Recomposer? = null
private var recomposeJob: Job? = null
private var composition: Composition? = null
@@ -128,11 +128,11 @@ internal class WindowInstance(
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
// down and returns null so the caller can retry with a different mode.
private fun makeBackend(mode: GpuMode): RenderBackend? {
val vBackend = SDL3Backend(
val vBackend = Sdl3Backend(
initialTitle, initialWidth, initialHeight, gpuMode = mode,
iconLightResourcePaths = icon?.light ?: 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]
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 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 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]
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]
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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 applyThemeIcon() // com.compose.sdl/SDL3Backend.applyThemeIcon|applyThemeIcon(){}[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 updateWindowSize() // com.compose.sdl/SDL3Backend.updateWindowSize|updateWindowSize(){}[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 init(): kotlin/Boolean // com.compose.sdl/Sdl3Backend.init|init(){}[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]
@@ -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_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_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 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_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_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/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/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]
@@ -8,7 +8,7 @@ import androidx.compose.ui.geometry.Offset
/* Actuals for the vendored `DragAndDrop.kt` expect classes.
* 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
* 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
* 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
* 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
* key. SDL3EventMapper picks the right `Key.X` by switch on SDL scancode
* (`kKeyForScancode` in `SDL3EventMapper.kt`). Comparison works because
* key. The SDL event mapper picks the right `Key.X` by switch on SDL scancode
* (`kKeyForScancode` in `AppEvent.kt`). Comparison works because
* Key is a value class - `Key(42L) == Key(42L)`.
*/
@kotlin.jvm.JvmInline
@@ -213,10 +213,10 @@ private fun sdlWriteEntry(entry: ClipEntry?) {
// The SDL3-backed Clipboard implementation. Singleton because SDL's clipboard
// is process-global - allocating a new one per Window() would only waste
// 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 setClipEntry(clipEntry: ClipEntry?) { sdlWriteEntry(clipEntry) }
override val nativeClipboard: NativeClipboard get() = nativeClipboardSentinel
}
fun platformClipboard(): Clipboard = SDL3Clipboard
fun platformClipboard(): Clipboard = Sdl3Clipboard
@@ -9,7 +9,7 @@ import kotlinx.cinterop.*
import sdl3.*
// ==================
// MARK: SDL3EventMapper
// MARK: AppEvent + the SDL3 event mapper
// ==================
/** 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? {
val type = e.type
return when (type) {
return when (e.type) {
SDL_EVENT_QUIT -> AppEvent.Quit
SDL_EVENT_MOUSE_BUTTON_DOWN -> {
@@ -7,8 +7,6 @@ import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.input.key.KeyEvent
import kotlinx.cinterop.COpaquePointer
import kotlinx.cinterop.reinterpret
import kotlinx.cinterop.toKString
import sdl3.SDL_GetRendererName
import sdl3.SDL_MaximizeWindow
import sdl3.SDL_MinimizeWindow
import sdl3.SDL_RaiseWindow
@@ -36,7 +34,7 @@ import sdl3.SDL_SetWindowTitle
them subscribes the caller to recomposition. The setter methods
call into SDL and also push the new value into the snapshot. */
class ComposeNativeWindow constructor(
private val backend: SDL3Backend,
private val backend: Sdl3Backend,
val gpuMode: GpuMode,
initialTitle: String,
) {
@@ -16,7 +16,7 @@ package com.compose.sdl
/** Create the render backend for the selected [GpuMode]; null when the
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). */
expect fun rendererPreferredGpuMode(): GpuMode
@@ -4,10 +4,10 @@ import kotlinx.cinterop.*
import sdl3.*
// ==================
// MARK: SDL3Backend
// MARK: Sdl3Backend
// ==================
class SDL3Backend(
class Sdl3Backend(
private val title: String = "ComposeNativeSDL3",
private val width: Int = 800,
private val height: Int = 600,
@@ -29,7 +29,7 @@ class SDL3Backend(
) {
init {
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
// ==================
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.SkiaMetalBridge
internal actual fun makeMetalBridge(backend: SDL3Backend): SkiaBridge? {
internal actual fun makeMetalBridge(backend: Sdl3Backend): SkiaBridge? {
val bridge = SkiaMetalBridge(backend)
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
(cheap - the DirectContext + device + queue persist). */
@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 mQueue: MTLCommandQueueProtocol? = null
private var mLayer: CAMetalLayer? = null
@@ -7,7 +7,7 @@ import kotlin.experimental.ExperimentalNativeApi
// MARK: Metal bridge factory (skia-only)
// ==================
internal expect fun makeMetalBridge(backend: SDL3Backend): SkiaBridge?
internal expect fun makeMetalBridge(backend: Sdl3Backend): SkiaBridge?
actual fun rendererPreferredGpuMode(): GpuMode {
@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
SDL3 module's createRenderBackend / rendererPreferredGpuMode in the same
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
return try {
SkiaRenderBackend(inSdl, vResolved)
@@ -4,13 +4,9 @@ import com.compose.sdl.*
import kotlinx.cinterop.*
import org.jetbrains.skia.BackendRenderTarget
import org.jetbrains.skia.Bitmap
import org.jetbrains.skia.Canvas
import org.jetbrains.skia.ColorAlphaType
import org.jetbrains.skia.ColorType
import org.jetbrains.skia.DirectContext
import org.jetbrains.skia.Image
import org.jetbrains.skia.ImageInfo
import org.jetbrains.skia.Surface
import org.jetbrains.skia.SurfaceColorFormat
import org.jetbrains.skia.SurfaceOrigin
@@ -28,7 +24,7 @@ import sdl3.SDL_GL_SwapWindow
Resize: tear down and rebuild the BackendRenderTarget + Surface; the
DirectContext is reused. The default GL framebuffer (id 0) is wrapped
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 mRT: BackendRenderTarget? = 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
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:
silent corruption while both live, and a hard crash (SIGSEGV in present)
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
manual-vendor. */
internal class SkiaRenderBackend(
private val sdl: SDL3Backend,
private val sdl: Sdl3Backend,
private val gpuMode: GpuMode,
) : RenderBackend {
@@ -18,7 +18,7 @@ import sdl3.*
/** 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,
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 mHeight = 0
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 →
// SkiaSurfaceBridge), so no GPU-context bridge is provided here yet. A D3D/Vulkan
// 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",
"animate*AsState, AnimationSpec (tween / spring / repeatable / infiniteRepeatable), " +
"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(
"pointerInput + gesture DSL",
"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
format the runtime (WindowIcon.kt / SDL3Backend) reads. */
format the runtime (WindowIcon.kt / Sdl3Backend) reads. */
fun rgbaBlob(inW: Int, inH: Int, inRgba: ByteArray): ByteArray {
val vOut = ByteArray(8 + inRgba.size)
le32(vOut, 0, inW)