Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ cd imgui-java
./gradlew :example:run
# SDL3 + SDL_GPU backend smoke test:
./gradlew :example:run -PmainClass=MainSdl
# SDL3 + SDL_Renderer backend smoke test:
./gradlew :example:run -PmainClass=MainSdlRenderer
```

`example/src/main/java/` contains a runnable showcase for every widget and extension — start with `Main.java`, then
Expand All @@ -68,10 +70,11 @@ On Apple Silicon nothing extra is needed: the published macOS native is a univer

### SDL3 backend (imgui-app)

`imgui-app` now supports two backends via `Configuration#setBackend(...)`:
`imgui-app` now supports three backends via `Configuration#setBackend(...)`:

- `Backend.GLFW` (default)
- `Backend.SDL` (SDL3 + SDL_GPU)
- `Backend.SDL_RENDERER` (SDL3 + `SDL_Renderer`)

Use SDL3 by switching backend in `configure(...)`:

Expand All @@ -88,7 +91,13 @@ For a ready smoke-test entry point, run `MainSdl`:
./gradlew :example:run -PmainClass=MainSdl
```

At the moment, SDL backend in `imgui-app` is focused on single-window flow (multi-viewport is not supported).
The SDL_Renderer variant is available through `Backend.SDL_RENDERER` and the `MainSdlRenderer` smoke test:

```
./gradlew :example:run -PmainClass=MainSdlRenderer
```

At the moment, SDL backends in `imgui-app` are focused on single-window flow (multi-viewport is not supported).

## Application

Expand Down
29 changes: 29 additions & 0 deletions example/src/main/java/MainSdlRenderer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import imgui.ImGui;
import imgui.app.Application;
import imgui.app.Backend;
import imgui.app.Configuration;

/**
* Smoke-test entry point for the SDL3 + SDL_Renderer backend. Run with:
* <pre>
* ./gradlew :example:run -PmainClass=MainSdlRenderer
* </pre>
*/
public class MainSdlRenderer extends Application {
@Override
protected void configure(final Configuration config) {
config.setTitle("Example Application — SDL3 + SDL_Renderer");
config.setBackend(Backend.SDL_RENDERER);
}

@Override
public void process() {
ImGui.begin("SDL3 + SDL_Renderer");
ImGui.text("Hello from the SDL_Renderer backend!");
ImGui.end();
}

public static void main(final String[] args) {
launch(new MainSdlRenderer());
}
}
16 changes: 14 additions & 2 deletions imgui-app/src/main/java/imgui/app/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
* <h2>Life-cycle</h2>
* <p>The entry point for ImGui applications is the Application class and {@link #launch(Application)} method.
* It builds a {@link Configuration}, lets the user customize it (including selecting a {@link Backend}), then
* instantiates the matching {@link Window} subclass ({@link WindowGlfw} or {@link WindowSdl}) and drives the
* instantiates the matching {@link Window} subclass ({@link WindowGlfw}, {@link WindowSdl} or
* {@link WindowSdlRenderer}) and drives the
* application loop:
*
* <ol>
Expand Down Expand Up @@ -134,7 +135,18 @@ public final Color getColorBg() {
public static void launch(final Application app) {
final Configuration config = new Configuration();
app.configure(config);
app.window = (config.getBackend() == Backend.SDL) ? new WindowSdl() : new WindowGlfw();
switch (config.getBackend()) {
case SDL:
app.window = new WindowSdl();
break;
case SDL_RENDERER:
app.window = new WindowSdlRenderer();
break;
case GLFW:
default:
app.window = new WindowGlfw();
break;
}
app.window.setOwner(app);
app.window.init(config);
app.preRun();
Expand Down
6 changes: 5 additions & 1 deletion imgui-app/src/main/java/imgui/app/Backend.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,9 @@ public enum Backend {
/**
* SDL3 platform + SDL_GPU renderer.
*/
SDL
SDL,
/**
* SDL3 platform + SDL_Renderer renderer.
*/
SDL_RENDERER
}
2 changes: 1 addition & 1 deletion imgui-app/src/main/java/imgui/app/Window.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

/**
* Abstract base for an application window. Subclasses bind a specific platform/renderer pair
* (see {@link WindowGlfw}, {@link WindowSdl}) and forward lifecycle hooks back to the owning
* (see {@link WindowGlfw}, {@link WindowSdl}, {@link WindowSdlRenderer}) and forward lifecycle hooks back to the owning
* {@link Application} via {@link #owner}.
*
* <p>Direct subclassing is supported but the supported integration path is to extend
Expand Down
139 changes: 139 additions & 0 deletions imgui-app/src/main/java/imgui/app/WindowSdlRenderer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package imgui.app;

import imgui.ImGui;
import imgui.sdl3.ImGuiImplSdl3;
import imgui.sdl3.ImGuiImplSdlRenderer3;
import org.lwjgl.sdl.SDL_Event;

import static org.lwjgl.sdl.SDLError.SDL_GetError;
import static org.lwjgl.sdl.SDLEvents.SDL_EVENT_QUIT;
import static org.lwjgl.sdl.SDLEvents.SDL_EVENT_WINDOW_CLOSE_REQUESTED;
import static org.lwjgl.sdl.SDLEvents.SDL_PollEvent;
import static org.lwjgl.sdl.SDLInit.SDL_INIT_GAMEPAD;
import static org.lwjgl.sdl.SDLInit.SDL_INIT_VIDEO;
import static org.lwjgl.sdl.SDLInit.SDL_Init;
import static org.lwjgl.sdl.SDLInit.SDL_Quit;
import static org.lwjgl.sdl.SDLRender.SDL_DestroyRenderer;
import static org.lwjgl.sdl.SDLRender.SDL_RenderClear;
import static org.lwjgl.sdl.SDLRender.SDL_RenderPresent;
import static org.lwjgl.sdl.SDLRender.SDL_SetRenderDrawColorFloat;
import static org.lwjgl.sdl.SDLRender.SDL_SetRenderScale;
import static org.lwjgl.sdl.SDLRender.SDL_SetRenderVSync;
import static org.lwjgl.sdl.SDLRender.nSDL_CreateRenderer;
import static org.lwjgl.sdl.SDLVideo.SDL_CreateWindow;
import static org.lwjgl.sdl.SDLVideo.SDL_DestroyWindow;
import static org.lwjgl.sdl.SDLVideo.SDL_MaximizeWindow;
import static org.lwjgl.sdl.SDLVideo.SDL_WINDOW_HIGH_PIXEL_DENSITY;
import static org.lwjgl.sdl.SDLVideo.SDL_WINDOW_RESIZABLE;

/**
* SDL3 + {@code SDL_Renderer} implementation of {@link Window}.
*
* <p>Pumps SDL events through {@link ImGuiImplSdl3#processEvent(long)} each frame and renders through
* {@link ImGuiImplSdlRenderer3#renderDrawData(long, long)}. Multi-viewport is not supported.
*/
public class WindowSdlRenderer extends Window {
private final ImGuiImplSdl3 imGuiSdl3 = new ImGuiImplSdl3();
private final ImGuiImplSdlRenderer3 imGuiSdlRenderer3 = new ImGuiImplSdlRenderer3();

private long handle;
private long renderer;
private boolean shouldClose;

@Override
protected void init(final Configuration config) {
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) {
throw new IllegalStateException("Unable to initialize SDL3: " + SDL_GetError());
}

final long flags = SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_RESIZABLE;
handle = SDL_CreateWindow(config.getTitle(), config.getWidth(), config.getHeight(), flags);
if (handle == 0L) {
throw new RuntimeException("Failed to create the SDL window: " + SDL_GetError());
}
if (config.isFullScreen()) {
SDL_MaximizeWindow(handle);
}

// Null driver name lets SDL pick the best available renderer.
renderer = nSDL_CreateRenderer(handle, 0L);
if (renderer == 0L) {
throw new RuntimeException("Failed to create the SDL renderer: " + SDL_GetError());
}
SDL_SetRenderVSync(renderer, 1);

owner.initImGui(config);

imGuiSdl3.initForSDLRenderer(handle, renderer);
imGuiSdlRenderer3.init(renderer);
}

@Override
protected void dispose() {
imGuiSdlRenderer3.shutdown();
imGuiSdl3.shutdown();
owner.disposeImGui();

if (renderer != 0L) {
SDL_DestroyRenderer(renderer);
renderer = 0L;
}
if (handle != 0L) {
SDL_DestroyWindow(handle);
handle = 0L;
}
SDL_Quit();
}

@Override
protected void run() {
while (!shouldClose) {
runFrame();
}
}

@Override
protected void runFrame() {
try (SDL_Event event = SDL_Event.calloc()) {
while (SDL_PollEvent(event)) {
final int type = event.type();
imGuiSdl3.processEvent(event.address());
if (type == SDL_EVENT_QUIT || type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) {
shouldClose = true;
}
}
}

imGuiSdlRenderer3.newFrame();
imGuiSdl3.newFrame();
ImGui.newFrame();

owner.preProcess();
owner.process();
owner.postProcess();

ImGui.render();

// ImGui works in window coordinates (points) while SDL_Renderer draws in output pixels.
// Scale here so the geometry, clip rects and mouse coordinates all share the same space.
SDL_SetRenderScale(renderer, ImGui.getIO().getDisplayFramebufferScaleX(), ImGui.getIO().getDisplayFramebufferScaleY());
SDL_SetRenderDrawColorFloat(renderer, colorBg.getRed(), colorBg.getGreen(), colorBg.getBlue(), colorBg.getAlpha());
SDL_RenderClear(renderer);
imGuiSdlRenderer3.renderDrawData(ImGui.getDrawData());
SDL_RenderPresent(renderer);
}

/**
* @return native {@code SDL_Window*} handle
*/
public final long getHandle() {
return handle;
}

/**
* @return native {@code SDL_Renderer*} handle
*/
public final long getRenderer() {
return renderer;
}
}
Loading
Loading