diff --git a/pom.xml b/pom.xml
index 791b881..b0d1f98 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0
org.lwjglx
lwjglx-debug
- 1.0.4
+ 1.0.5
LWJGLX/debug
Debug LWJGL3 OpenGL applications
2017
diff --git a/src/org/lwjglx/debug/InterceptClassGenerator.java b/src/org/lwjglx/debug/InterceptClassGenerator.java
index d0aed6a..95cf4cb 100644
--- a/src/org/lwjglx/debug/InterceptClassGenerator.java
+++ b/src/org/lwjglx/debug/InterceptClassGenerator.java
@@ -39,7 +39,10 @@ of this software and associated documentation files (the "Software"), to deal
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
+import java.util.NavigableSet;
import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
@@ -148,6 +151,23 @@ class InterceptClassGenerator implements Opcodes {
"SDL_GL_MakeCurrent", "SDL_GL_GetCurrentContext", "SDL_GL_SwapWindow", "SDL_GL_DestroyContext"
)));
+ private static final Set SDL_PRE_INIT_METHODS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
+ "SDL_Init", "SDL_InitSubSystem", "SDL_SetMemoryFunctions", "SDL_GetMemoryFunctions",
+ "SDL_malloc", "SDL_calloc", "SDL_realloc", "SDL_free", "SDL_GetVersion", "SDL_GetRevision",
+ "SDL_GetPlatform", "SDL_GetError", "SDL_ClearError", "SDL_SetError", "SDL_SetMainReady",
+ "SDL_RunApp"
+ )));
+
+ private static final NavigableSet SDL_PRE_INIT_PREFIXES =
+ Collections.unmodifiableNavigableSet(new TreeSet<>(Arrays.asList(
+ "SDL_Hint", "SDL_SetHint", "SDL_GetHint", "SDL_ResetHint", "SDL_AddHint", "SDL_RemoveHint",
+ "SDL_Environment", "SDL_SetEnvironment", "SDL_GetEnvironment", "SDL_UnsetEnvironment",
+ "SDL_CreateEnvironment", "SDL_DestroyEnvironment", "SDL_Log", "SDL_CreateProperties",
+ "SDL_DestroyProperties", "SDL_SetProperty", "SDL_GetProperty", "SDL_SetStringProperty",
+ "SDL_GetStringProperty", "SDL_SetNumberProperty", "SDL_GetNumberProperty", "SDL_SetFloatProperty",
+ "SDL_GetFloatProperty", "SDL_SetPointerProperty", "SDL_GetPointerProperty"
+ )));
+
private static boolean isGLcall(InterceptedCall call) {
return (call.name.startsWith("gl") || call.name.startsWith("ngl")) && call.resolvedReceiverInternalName.startsWith("org/lwjgl/opengl/");
}
@@ -195,11 +215,70 @@ private static boolean requiresGlfwInit(InterceptedCall call) {
private static boolean requiresSdlInit(InterceptedCall call) {
if (call.resolvedReceiverInternalName.startsWith("org/lwjgl/sdl/")) {
- return call.name.startsWith("SDL_") && !call.name.equals("SDL_Init") && !call.name.equals("SDL_InitSubSystem") && !call.name.equals("SDL_SetMemoryFunctions");
+ switch (call.resolvedReceiverInternalName) {
+ case "org/lwjgl/sdl/SDLStdinc":
+ case "org/lwjgl/sdl/SDLHints":
+ case "org/lwjgl/sdl/SDLError":
+ case "org/lwjgl/sdl/SDLLog":
+ case "org/lwjgl/sdl/SDLProperties":
+ case "org/lwjgl/sdl/SDLVersion":
+ case "org/lwjgl/sdl/SDLInit":
+ return false;
+ }
+ if (!call.name.startsWith("SDL_")) {
+ return false;
+ }
+ if (SDL_PRE_INIT_METHODS.contains(call.name)) {
+ return false;
+ }
+
+ String prefix = SDL_PRE_INIT_PREFIXES.floor(call.name);
+ return null == prefix || !call.name.startsWith(prefix);
}
return false;
}
+ private static int getRequiredSdlSubsystem(String receiver) {
+ switch (receiver) {
+ case "org/lwjgl/sdl/SDLVideo":
+ case "org/lwjgl/sdl/SDLRenderer":
+ case "org/lwjgl/sdl/SDLMessagebox":
+ case "org/lwjgl/sdl/SDLMouse":
+ case "org/lwjgl/sdl/SDLKeyboard":
+ case "org/lwjgl/sdl/SDLTouch":
+ case "org/lwjgl/sdl/SDLPen":
+ case "org/lwjgl/sdl/SDLGPU":
+ case "org/lwjgl/sdl/SDLMetal":
+ case "org/lwjgl/sdl/SDLVulkan":
+ case "org/lwjgl/sdl/SDLSystray":
+ return 0x00000020; // SDL_INIT_VIDEO
+
+ case "org/lwjgl/sdl/SDLAudio":
+ return 0x00000010; // SDL_INIT_AUDIO
+
+ case "org/lwjgl/sdl/SDLJoystick":
+ return 0x00000200; // SDL_INIT_JOYSTICK
+
+ case "org/lwjgl/sdl/SDLGamepad":
+ return 0x00002000; // SDL_INIT_GAMEPAD
+
+ case "org/lwjgl/sdl/SDLEvents":
+ return 0x00004000; // SDL_INIT_EVENTS
+
+ case "org/lwjgl/sdl/SDLSensor":
+ return 0x00008000; // SDL_INIT_SENSOR
+
+ case "org/lwjgl/sdl/SDLCamera":
+ return 0x00010000; // SDL_INIT_CAMERA
+
+ case "org/lwjgl/sdl/SDLHaptic":
+ return 0x00001000; // SDL_INIT_HAPTIC
+
+ }
+ return 0;
+ }
+
+
private static void checkFunctionSupported(MethodVisitor mv, String name) {
mv.visitFieldInsn(GETFIELD, "org/lwjgl/opengl/GLCapabilities", name, "J");
mv.visitLdcInsn(name);
@@ -274,7 +353,8 @@ public static Class> generate(ClassLoader classLoader, String proxyInternalNam
/* and whether it was an SDL method that requires SDL_Init() to have been called */
if (requiresSdlInit(call)) {
mv.visitLdcInsn(call.name);
- mv.visitMethodInsn(INVOKESTATIC, RT_InternalName, "checkSdlInitialized", "(Ljava/lang/String;)V", false);
+ mv.visitLdcInsn(getRequiredSdlSubsystem(call.resolvedReceiverInternalName));
+ mv.visitMethodInsn(INVOKESTATIC, RT_InternalName, "checkSdlInitialized", "(Ljava/lang/String;I)V", false);
}
}
/* Validate buffer arguments and also load all arguments onto stack */
diff --git a/src/org/lwjglx/debug/RT.java b/src/org/lwjglx/debug/RT.java
index 605e263..5d31768 100644
--- a/src/org/lwjglx/debug/RT.java
+++ b/src/org/lwjglx/debug/RT.java
@@ -23,6 +23,7 @@ of this software and associated documentation files (the "Software"), to deal
package org.lwjglx.debug;
import org.lwjgl.PointerBuffer;
+import org.lwjgl.glfw.GLFW;
import org.lwjglx.debug.org.lwjgl.opengl.Context;
import java.nio.Buffer;
@@ -41,6 +42,7 @@ of this software and associated documentation files (the "Software"), to deal
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
+import static org.lwjgl.sdl.SDLInit.SDL_WasInit;
import static org.lwjglx.debug.Log.error;
import static org.lwjglx.debug.Log.trace;
import static org.lwjglx.debug.org.lwjgl.opengl.Context.CONTEXTS;
@@ -703,12 +705,14 @@ public static MethodCall paramGlfwWindow(MethodCall mc, long window) {
}
public static MethodCall paramGlfwMonitor(MethodCall mc, long monitor) {
- PointerBuffer monitors = org.lwjgl.glfw.GLFW.glfwGetMonitors();
- for (int i = 0; i < monitors.remaining(); i++) {
- long m = monitors.get(i);
- if (m == monitor) {
- mc.paramEnum("monitor[" + i + "]");
- return mc;
+ PointerBuffer monitors = GLFW.glfwGetMonitors();
+ if (monitors != null) {
+ for (int i = 0; i < monitors.remaining(); i++) {
+ long m = monitors.get(i);
+ if (m == monitor) {
+ mc.paramEnum("monitor[" + i + "]");
+ return mc;
+ }
}
}
mc.param(monitor);
@@ -758,12 +762,14 @@ public static long returnValueGlfwWindow(long window, MethodCall mc) {
}
public static long returnValueGlfwMonitor(long monitor, MethodCall mc) {
- PointerBuffer monitors = org.lwjgl.glfw.GLFW.glfwGetMonitors();
- for (int i = 0; i < monitors.remaining(); i++) {
- long m = monitors.get(i);
- if (m == monitor) {
- mc.returnValueEnum("monitor[" + i + "]");
- return monitor;
+ PointerBuffer monitors = GLFW.glfwGetMonitors();
+ if (monitors != null) {
+ for (int i = 0; i < monitors.remaining(); i++) {
+ long m = monitors.get(i);
+ if (m == monitor) {
+ mc.returnValueEnum("monitor[" + i + "]");
+ return monitor;
+ }
}
}
mc.returnValue(monitor);
@@ -1250,10 +1256,12 @@ public static void checkGlfwInitialized(String methodName) {
public static void checkGlfwMonitor(long monitor) {
if (monitor == 0L)
return;
- PointerBuffer pb = org.lwjgl.glfw.GLFW.glfwGetMonitors();
- for (int i = 0; i < pb.remaining(); i++) {
- if (pb.get(i) == monitor)
- return;
+ PointerBuffer pb = GLFW.glfwGetMonitors();
+ if (pb != null) {
+ for (int i = 0; i < pb.remaining(); i++) {
+ if (pb.get(i) == monitor)
+ return;
+ }
}
throwIAEOrLogError("Provided 'monitor' argument is not a valid GLFW monitor handle: " + monitor);
}
@@ -1324,9 +1332,24 @@ public static long returnValueSdlGlContext(long context, MethodCall mc) {
return context;
}
- public static void checkSdlInitialized(String methodName) {
- if (!sdlInitialized) {
- throwISEOrLogError("Method " + methodName + " was called before initializing SDL via SDL_Init().");
+ public static void checkSdlInitialized(String methodName, int requiredFlags) {
+ boolean initialized = false;
+ try {
+ if (requiredFlags == 0) {
+ initialized = sdlInitialized || SDL_WasInit(0) != 0;
+ } else {
+ initialized = (SDL_WasInit(requiredFlags) & requiredFlags) == requiredFlags;
+ }
+ } catch (Throwable t) {
+ t.printStackTrace();
+ initialized = sdlInitialized;
+ }
+ if (!initialized) {
+ if (requiredFlags == 0) {
+ throwISEOrLogError("Method " + methodName + " was called before initializing SDL.");
+ } else {
+ throwISEOrLogError("Method " + methodName + " was called before initializing the required SDL subsystem.");
+ }
}
}
diff --git a/src/org/lwjglx/debug/org/lwjgl/sdl/SDLInit.java b/src/org/lwjglx/debug/org/lwjgl/sdl/SDLInit.java
index 9c30354..479a1b4 100644
--- a/src/org/lwjglx/debug/org/lwjgl/sdl/SDLInit.java
+++ b/src/org/lwjglx/debug/org/lwjgl/sdl/SDLInit.java
@@ -29,27 +29,23 @@
public class SDLInit {
public static boolean SDL_Init(int flags) {
- boolean ret = org.lwjgl.sdl.SDLInit.SDL_Init(flags);
- if (ret) {
+ boolean result = org.lwjgl.sdl.SDLInit.SDL_Init(flags);
+ if (result) {
RT.sdlInitialized = true;
- } else {
- if (Properties.VALIDATE.enabled) {
- error("SDL_Init returned false");
- }
+ } else if (Properties.VALIDATE.enabled) {
+ error("SDL_Init returned false");
}
- return ret;
+ return result;
}
public static boolean SDL_InitSubSystem(int flags) {
- boolean ret = org.lwjgl.sdl.SDLInit.SDL_InitSubSystem(flags);
- if (ret) {
+ boolean result = org.lwjgl.sdl.SDLInit.SDL_InitSubSystem(flags);
+ if (result) {
RT.sdlInitialized = true;
- } else {
- if (Properties.VALIDATE.enabled) {
- error("SDL_InitSubSystem returned false");
- }
+ } else if (Properties.VALIDATE.enabled) {
+ error("SDL_InitSubSystem returned false");
}
- return ret;
+ return result;
}
public static void SDL_Quit() {
diff --git a/test/test/DebugSDLIT.java b/test/test/DebugSDLIT.java
index 4fcd5d8..c406bf5 100644
--- a/test/test/DebugSDLIT.java
+++ b/test/test/DebugSDLIT.java
@@ -4,9 +4,15 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
+import org.lwjgl.sdl.SDLError;
+import org.lwjgl.sdl.SDLHints;
+import org.lwjgl.sdl.SDLInit;
+import org.lwjgl.sdl.SDLStdinc;
+import org.lwjgl.sdl.SDLTimer;
import org.lwjglx.debug.Properties;
import org.lwjglx.debug.org.lwjgl.opengl.Context;
+import java.nio.ByteBuffer;
import java.util.concurrent.CountDownLatch;
import java.util.regex.Pattern;
@@ -172,4 +178,54 @@ public void testGLError() {
createCapabilities();
assertThrowsWithMessage(IllegalStateException.class, () -> glEnable(GL_VERTEX_ARRAY_POINTER), Pattern.compile("glEnable produced error: 1280 \\(GL_INVALID_ENUM\\)"));
}
+
+ @Test
+ public void testPreInitAllowed() {
+ SDL_Quit();
+ alreadyTerminated = true;
+ assertTrue(SDLHints.SDL_SetHint("SDL_VIDEO_DRIVER", "dummy"));
+ assertEquals("dummy", SDLHints.SDL_GetHint("SDL_VIDEO_DRIVER"));
+ SDLError.SDL_GetError();
+ ByteBuffer buf = SDLStdinc.SDL_malloc(16);
+ if (buf != null) {
+ SDLStdinc.SDL_free(buf);
+ }
+ assertTrue(SDL_Init(SDL_INIT_VIDEO));
+ alreadyTerminated = false;
+ window = SDL_CreateWindow("Test PreInit Allowed", 320, 240, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN);
+ assertNotNull(window);
+ }
+
+ @Test
+ public void testSubsystemRefcounting() {
+ SDL_Quit();
+ alreadyTerminated = true;
+ assertTrue(SDL_Init(SDL_INIT_VIDEO));
+ assertTrue(SDL_Init(SDL_INIT_VIDEO));
+ alreadyTerminated = false;
+ window = SDL_CreateWindow("Test Refcount 1", 320, 240, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN);
+ assertNotNull(window);
+ SDLInit.SDL_QuitSubSystem(SDL_INIT_VIDEO);
+ window2 = SDL_CreateWindow("Test Refcount 2", 320, 240, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN);
+ assertNotNull(window2);
+ SDL_DestroyWindow(window);
+ window = 0L;
+ SDL_DestroyWindow(window2);
+ window2 = 0L;
+ SDLInit.SDL_QuitSubSystem(SDL_INIT_VIDEO);
+ alreadyTerminated = true;
+ assertThrows(IllegalStateException.class, () -> SDL_CreateWindow("Should Fail", 320, 240, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN));
+ }
+
+ @Test
+ public void testInitZero() {
+ SDL_Quit();
+ alreadyTerminated = true;
+ assertTrue(SDL_Init(0));
+ // core/timer method should succeed
+ long ticks = SDLTimer.SDL_GetTicks();
+ assertTrue(ticks >= 0L);
+ // video method should throw IllegalStateException because video subsystem is not initialized
+ assertThrows(IllegalStateException.class, () -> SDL_CreateWindow("Should Fail", 320, 240, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN));
+ }
}