dev.sargunv:compose-glfw-direct3d-windows-arm64

Windows arm64 Direct3D runtime dependencies for Compose GLFW.


License
MIT

Documentation

Compose GLFW

Maven Central Version Kotlin Version Compose Version API Reference

This library provides a JVM Compose host that runs Compose UI in a GLFW window instead of the default AWT/Swing desktop host.

I built this because the default Compose Desktop AWT/Swing host is a bad user experience, especially on Linux. Resize is slow, Wayland isn't supported, fractional scaling isn't supported, and the GPU context isn't readily available for advanced rendering. Here I fix all of that with a more robust windowing toolkit.

Installation

Warning

This project uses internal Compose APIs and may break with future Compose versions; pay attention to the Compose version listed in the release notes.

Add the core library and the runtime modules you want to ship:

dependencies {
  implementation("dev.sargunv:compose-glfw:<version>")
  runtimeOnly("dev.sargunv:compose-glfw-opengl-linux-arm64:<version>")
  runtimeOnly("dev.sargunv:compose-glfw-opengl-linux-x64:<version>")
  runtimeOnly("dev.sargunv:compose-glfw-metal-macos-arm64:<version>")
  runtimeOnly("dev.sargunv:compose-glfw-metal-macos-x64:<version>")
  runtimeOnly("dev.sargunv:compose-glfw-direct3d-windows-x64:<version>")
  runtimeOnly("dev.sargunv:compose-glfw-direct3d-windows-arm64:<version>")
}

On recent JDKs, we also require native access to be enabled:

--enable-native-access=ALL-UNNAMED

Status and limitations

The following features are supported:

  • App composition and window state
  • HiDPI and fractional scaling
  • Pointer, scroll, keyboard, text, and IME input
    • Not yet touch/stylus input
  • Clipboard integration
  • Custom cursors
  • Dark/light theme detection
  • Native file and folder pickers
  • Dragged file drops
    • Just the final drop event; not events like enter, exit, drag
  • Preferred frame rates
  • Graphics context access for advanced rendering integrations

The following features are not yet supported:

  • Screen reader, native menus, or the system tray
  • Keep screen on and frame rate voting
  • Interop views like SwingPanel. Advanced users can instead use the host graphics context for integrating custom components.
  • Draggable window area (custom title bar)

See ROADMAP.md for a detailed list of known gaps. Please contribute!

Linux notes

On Linux, by default, the host prefers Wayland when WAYLAND_DISPLAY is set. You can force the GLFW display server backend with:

-Dcompose.glfw.platform=wayland
-Dcompose.glfw.platform=x11

Certain features are unavailable under Wayland:

  • Window positioning and always-on-top are not supported due to limitations in the Wayland protocol.
  • Native file dialogs are supported but not parented to the window due to a limitation of lwjgl-nfd
  • Dragged file drops are not supported on Wayland (unclear why; it should work)

macOS notes

On macOS, apps must be launched on AppKit's first thread:

-XstartOnFirstThread

Usage

Run your Compose content with glfwApplication:

fun main() = glfwApplication {
  Window(
    onCloseRequest = ::exitApplication,
    title = "Example",
    state = rememberWindowState(size = DpSize(960.dp, 640.dp)),
  ) {
    App()
  }
}

@Composable
fun App() {
  // Your Compose UI.
}

Window configuration

Configure the GLFW window further with WindowOptions:

Window(
  onCloseRequest = ::exitApplication,
  title = "Example",
  undecorated = true,
  transparent = true,
  resizable = true,
  focusOnShow = false,
  options = WindowOptions {
    textToolbar = { state, actions ->
      // Draw a custom text context menu / toolbar.
    }
  },
) {
  App()
}

Custom cursors

Use cursorImagePointerIcon with the pointerHoverIcon modifier to use a custom ImageBitmap cursor:

Modifier.pointerHoverIcon(
  cursorImagePointerIcon(image, imageScale = 1f, hotSpot = hotSpot),
)

File drops

Use the fileDropTarget modifier to receive file drops delivered by the host:

Modifier.fileDropTarget { files ->
  files.paths.forEach { path ->
    // Handle dropped file.
  }
}

File pickers

Use LocalWindow to show native file and folder pickers from composables:

@Composable
fun OpenImageButton() {
  val filePicker = LocalWindow.current.filePicker

  Button(
    onClick = {
      val image =
        filePicker.openFile(
          filters = listOf(FileDialogFilter("Images", listOf("png", "jpg", "jpeg"))),
        )
      // Handle selected path, or null if canceled.
    }
  ) {
    Text("Open image")
  }
}

On Linux, file pickers use xdg-desktop-portal so they work with your desktop environment's file picker.

Graphics interop

Advanced renderers can access the host graphics context from composables:

@Composable
fun RendererHost() {
  val renderContext = LocalWindow.current.renderContext

  val openGl = renderContext as? OpenGlRenderContext
  // or
  val metal = renderContext as? MetalRenderContext
  // or
  val direct3d = renderContext as? Direct3DRenderContext

  App()
}

App icons

Compose GLFW does not expose GLFW's glfwSetWindowIcon API. Modern Linux Wayland and macOS ignore that per-window API; app icons should come from platform app metadata instead. Use Freedesktop desktop-entry/icon-theme metadata on Linux, app bundle Info.plist icon metadata on macOS, and executable icon resources on Windows.