From 56178d97fd9a39d11d44d45aadb61cbdc53fabe4 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Mon, 12 Feb 2007 12:18:26 +0000 Subject: [PATCH] Make the rest of LWJGL thread safe. No attempt have been done to make the locking minimal. Instead, one global lock is shared by Display, Mouse, Keyboard and Cursor. The lock surrounds all public methods. --- src/java/org/lwjgl/input/Cursor.java | 94 +-- src/java/org/lwjgl/input/Keyboard.java | 90 +-- src/java/org/lwjgl/input/Mouse.java | 324 ++++++----- .../org/lwjgl/input/OpenGLPackageAccess.java | 49 ++ src/java/org/lwjgl/opengl/Display.java | 539 ++++++++++-------- src/java/org/lwjgl/opengl/GlobalLock.java | 9 + src/java/org/lwjgl/opengl/MacOSXDisplay.java | 4 +- src/java/org/lwjgl/opengl/WindowsDisplay.java | 4 +- 8 files changed, 653 insertions(+), 460 deletions(-) create mode 100644 src/java/org/lwjgl/input/OpenGLPackageAccess.java create mode 100644 src/java/org/lwjgl/opengl/GlobalLock.java diff --git a/src/java/org/lwjgl/input/Cursor.java b/src/java/org/lwjgl/input/Cursor.java index e8c6e401..0e14b490 100644 --- a/src/java/org/lwjgl/input/Cursor.java +++ b/src/java/org/lwjgl/input/Cursor.java @@ -83,27 +83,29 @@ public class Cursor { * @throws LWJGLException if the cursor could not be created for any reason */ public Cursor(int width, int height, int xHotspot, int yHotspot, int numImages, IntBuffer images, IntBuffer delays) throws LWJGLException { - if ((getCapabilities() & CURSOR_ONE_BIT_TRANSPARENCY) == 0) - throw new LWJGLException("Native cursors not supported"); - BufferChecks.checkBuffer(images, width*height*numImages); - if (!Mouse.isCreated()) - throw new IllegalStateException("Mouse must be created before creating cursor objects"); - if (width*height*numImages > images.remaining()) - throw new IllegalArgumentException("width*height*numImages > images.remaining()"); - if (delays != null && numImages > delays.remaining()) - BufferChecks.checkBuffer(delays, numImages); - if (xHotspot >= width || xHotspot < 0) - throw new IllegalArgumentException("xHotspot > width || xHotspot < 0"); - if (yHotspot >= height || yHotspot < 0) - throw new IllegalArgumentException("yHotspot > height || yHotspot < 0"); - - Sys.initialize(); - - // Hmm - yHotspot = height - 1 - yHotspot; - - // create cursor (or cursors if multiple images supplied) - cursors = createCursors(width, height, xHotspot, yHotspot, numImages, images, delays); + synchronized (OpenGLPackageAccess.global_lock) { + if ((getCapabilities() & CURSOR_ONE_BIT_TRANSPARENCY) == 0) + throw new LWJGLException("Native cursors not supported"); + BufferChecks.checkBuffer(images, width*height*numImages); + if (!Mouse.isCreated()) + throw new IllegalStateException("Mouse must be created before creating cursor objects"); + if (width*height*numImages > images.remaining()) + throw new IllegalArgumentException("width*height*numImages > images.remaining()"); + if (delays != null && numImages > delays.remaining()) + BufferChecks.checkBuffer(delays, numImages); + if (xHotspot >= width || xHotspot < 0) + throw new IllegalArgumentException("xHotspot > width || xHotspot < 0"); + if (yHotspot >= height || yHotspot < 0) + throw new IllegalArgumentException("yHotspot > height || yHotspot < 0"); + + Sys.initialize(); + + // Hmm + yHotspot = height - 1 - yHotspot; + + // create cursor (or cursors if multiple images supplied) + cursors = createCursors(width, height, xHotspot, yHotspot, numImages, images, delays); + } } /** @@ -114,9 +116,11 @@ public class Cursor { * @return the maximum size of a native cursor */ public static int getMinCursorSize() { - if (!Mouse.isCreated()) - throw new IllegalStateException("Mouse must be created."); - return Mouse.getImplementation().getMinCursorSize(); + synchronized (OpenGLPackageAccess.global_lock) { + if (!Mouse.isCreated()) + throw new IllegalStateException("Mouse must be created."); + return Mouse.getImplementation().getMinCursorSize(); + } } /** @@ -127,9 +131,11 @@ public class Cursor { * @return the maximum size of a native cursor */ public static int getMaxCursorSize() { - if (!Mouse.isCreated()) - throw new IllegalStateException("Mouse must be created."); - return Mouse.getImplementation().getMaxCursorSize(); + synchronized (OpenGLPackageAccess.global_lock) { + if (!Mouse.isCreated()) + throw new IllegalStateException("Mouse must be created."); + return Mouse.getImplementation().getMaxCursorSize(); + } } /** @@ -141,10 +147,12 @@ public class Cursor { * @return A bit mask with native cursor capabilities. */ public static int getCapabilities() { - if (Mouse.getImplementation() != null) - return Mouse.getImplementation().getNativeCursorCapabilities(); - else - return Mouse.createImplementation().getNativeCursorCapabilities(); + synchronized (OpenGLPackageAccess.global_lock) { + if (Mouse.getImplementation() != null) + return Mouse.getImplementation().getNativeCursorCapabilities(); + else + return OpenGLPackageAccess.createImplementation().getNativeCursorCapabilities(); + } } /** @@ -247,19 +255,21 @@ public class Cursor { * OS cursor) */ public void destroy() { - if (destroyed) - return; - if (Mouse.getNativeCursor() == this) { - try { - Mouse.setNativeCursor(null); - } catch (LWJGLException e) { - // ignore + synchronized (OpenGLPackageAccess.global_lock) { + if (destroyed) + return; + if (Mouse.getNativeCursor() == this) { + try { + Mouse.setNativeCursor(null); + } catch (LWJGLException e) { + // ignore + } } + for(int i=0; i @@ -152,7 +147,9 @@ public class Mouse { * @return the currently bound native cursor, if any. */ public static synchronized Cursor getNativeCursor() { - return currentCursor; + synchronized (OpenGLPackageAccess.global_lock) { + return currentCursor; + } } /** @@ -167,19 +164,21 @@ public class Mouse { * @throws LWJGLException if the cursor could not be set for any reason */ public static synchronized Cursor setNativeCursor(Cursor cursor) throws LWJGLException { - if ((Cursor.getCapabilities() & Cursor.CURSOR_ONE_BIT_TRANSPARENCY) == 0) - throw new IllegalStateException("Mouse doesn't support native cursors"); - Cursor oldCursor = currentCursor; - currentCursor = cursor; - if (isCreated()) { - if (currentCursor != null) { - implementation.setNativeCursor(currentCursor.getHandle()); - currentCursor.setTimeout(); - } else { - implementation.setNativeCursor(null); + synchronized (OpenGLPackageAccess.global_lock) { + if ((Cursor.getCapabilities() & Cursor.CURSOR_ONE_BIT_TRANSPARENCY) == 0) + throw new IllegalStateException("Mouse doesn't support native cursors"); + Cursor oldCursor = currentCursor; + currentCursor = cursor; + if (isCreated()) { + if (currentCursor != null) { + implementation.setNativeCursor(currentCursor.getHandle()); + currentCursor.setTimeout(); + } else { + implementation.setNativeCursor(null); + } } + return oldCursor; } - return oldCursor; } /** @@ -192,12 +191,14 @@ public class Mouse { * to the window origin. */ public static synchronized void setCursorPosition(int new_x, int new_y) { - if (!isCreated()) - throw new IllegalStateException("Mouse is not created"); - x = event_x = new_x; - y = event_y = new_y; - if (!isGrabbed() && (Cursor.getCapabilities() & Cursor.CURSOR_ONE_BIT_TRANSPARENCY) != 0) - implementation.setCursorPosition(x, y); + synchronized (OpenGLPackageAccess.global_lock) { + if (!isCreated()) + throw new IllegalStateException("Mouse is not created"); + x = event_x = new_x; + y = event_y = new_y; + if (!isGrabbed() && (Cursor.getCapabilities() & Cursor.CURSOR_ONE_BIT_TRANSPARENCY) != 0) + implementation.setCursorPosition(x, y); + } } /** @@ -225,23 +226,6 @@ public class Mouse { return implementation; } - static InputImplementation createImplementation() { - /* Use reflection since we can't make Display.getImplementation - * public - */ - try { - return (InputImplementation)AccessController.doPrivileged(new PrivilegedExceptionAction() { - public Object run() throws Exception { - Method getImplementation_method = Display.class.getDeclaredMethod("getImplementation", null); - getImplementation_method.setAccessible(true); - return getImplementation_method.invoke(null, null); - } - }); - } catch (PrivilegedActionException e) { - throw new Error(e); - } - } - /** * "Create" the mouse with the given custom implementation. This is used * reflectively by AWTInputAdapter. @@ -277,28 +261,34 @@ public class Mouse { * @throws LWJGLException if the mouse could not be created for any reason */ public static synchronized void create() throws LWJGLException { - if (!Display.isCreated()) throw new IllegalStateException("Display must be created."); + synchronized (OpenGLPackageAccess.global_lock) { + if (!Display.isCreated()) throw new IllegalStateException("Display must be created."); - create(createImplementation()); + create(OpenGLPackageAccess.createImplementation()); + } } /** * @return true if the mouse has been created */ public static synchronized boolean isCreated() { - return created; + synchronized (OpenGLPackageAccess.global_lock) { + return created; + } } /** * "Destroy" the mouse. */ public static synchronized void destroy() { - if (!created) return; - created = false; - buttons = null; - coord_buffer = null; - - implementation.destroyMouse(); + synchronized (OpenGLPackageAccess.global_lock) { + if (!created) return; + created = false; + buttons = null; + coord_buffer = null; + + implementation.destroyMouse(); + } } /** @@ -323,30 +313,32 @@ public class Mouse { * @see org.lwjgl.input.Mouse#getDWheel() */ public static synchronized void poll() { - if (!created) throw new IllegalStateException("Mouse must be created before you can poll it"); - implementation.pollMouse(coord_buffer, buttons); + synchronized (OpenGLPackageAccess.global_lock) { + if (!created) throw new IllegalStateException("Mouse must be created before you can poll it"); + implementation.pollMouse(coord_buffer, buttons); - /* If we're grabbed, poll returns mouse deltas, if not it returns absolute coordinates */ - int poll_coord1 = coord_buffer.get(0); - int poll_coord2 = coord_buffer.get(1); - /* The wheel is always relative */ - int poll_dwheel = coord_buffer.get(2); + /* If we're grabbed, poll returns mouse deltas, if not it returns absolute coordinates */ + int poll_coord1 = coord_buffer.get(0); + int poll_coord2 = coord_buffer.get(1); + /* The wheel is always relative */ + int poll_dwheel = coord_buffer.get(2); - if (isGrabbed()) { - dx += poll_coord1; - dy += poll_coord2; - x += poll_coord1; - y += poll_coord2; - } else { - dx = poll_coord1 - x; - dy = poll_coord2 - y; - x = poll_coord1; - y = poll_coord2; + if (isGrabbed()) { + dx += poll_coord1; + dy += poll_coord2; + x += poll_coord1; + y += poll_coord2; + } else { + dx = poll_coord1 - x; + dy = poll_coord2 - y; + x = poll_coord1; + y = poll_coord2; + } + x = Math.min(implementation.getWidth() - 1, Math.max(0, x)); + y = Math.min(implementation.getHeight() - 1, Math.max(0, y)); + dwheel += poll_dwheel; + read(); } - x = Math.min(implementation.getWidth() - 1, Math.max(0, x)); - y = Math.min(implementation.getHeight() - 1, Math.max(0, y)); - dwheel += poll_dwheel; - read(); } private static void read() { @@ -362,11 +354,13 @@ public class Mouse { * @return true if the specified button is down */ public static synchronized boolean isButtonDown(int button) { - if (!created) throw new IllegalStateException("Mouse must be created before you can poll the button state"); - if (button >= buttonCount || button < 0) - return false; - else - return buttons.get(button) == 1; + synchronized (OpenGLPackageAccess.global_lock) { + if (!created) throw new IllegalStateException("Mouse must be created before you can poll the button state"); + if (button >= buttonCount || button < 0) + return false; + else + return buttons.get(button) == 1; + } } /** @@ -375,10 +369,12 @@ public class Mouse { * @return a String with the button's human readable name in it or null if the button is unnamed */ public static synchronized String getButtonName(int button) { - if (button >= buttonName.length || button < 0) - return null; - else - return buttonName[button]; + synchronized (OpenGLPackageAccess.global_lock) { + if (button >= buttonName.length || button < 0) + return null; + else + return buttonName[button]; + } } /** @@ -386,11 +382,13 @@ public class Mouse { * @param buttonName The button name */ public static synchronized int getButtonIndex(String buttonName) { - Integer ret = (Integer) buttonMap.get(buttonName); - if (ret == null) - return -1; - else - return ret.intValue(); + synchronized (OpenGLPackageAccess.global_lock) { + Integer ret = (Integer) buttonMap.get(buttonName); + if (ret == null) + return -1; + else + return ret.intValue(); + } } /** @@ -403,37 +401,41 @@ public class Mouse { * @return true if a mouse event was read, false otherwise */ public static synchronized boolean next() { - if (!created) throw new IllegalStateException("Mouse must be created before you can read events"); - if (readBuffer.hasRemaining()) { - eventButton = readBuffer.get(); - eventState = readBuffer.get() != 0; - if (isGrabbed()) { - event_dx = readBuffer.getInt(); - event_dy = readBuffer.getInt(); - event_x += event_dx; - event_y += event_dy; - } else { - int new_event_x = readBuffer.getInt(); - int new_event_y = readBuffer.getInt(); - event_dx = new_event_x - event_x; - event_dy = new_event_y - event_y; - event_x = new_event_x; - event_y = new_event_y; - } - event_x = Math.min(implementation.getWidth() - 1, Math.max(0, event_x)); - event_y = Math.min(implementation.getHeight() - 1, Math.max(0, event_y)); - event_dwheel = readBuffer.getInt(); - event_nanos = readBuffer.getLong(); - return true; - } else - return false; + synchronized (OpenGLPackageAccess.global_lock) { + if (!created) throw new IllegalStateException("Mouse must be created before you can read events"); + if (readBuffer.hasRemaining()) { + eventButton = readBuffer.get(); + eventState = readBuffer.get() != 0; + if (isGrabbed()) { + event_dx = readBuffer.getInt(); + event_dy = readBuffer.getInt(); + event_x += event_dx; + event_y += event_dy; + } else { + int new_event_x = readBuffer.getInt(); + int new_event_y = readBuffer.getInt(); + event_dx = new_event_x - event_x; + event_dy = new_event_y - event_y; + event_x = new_event_x; + event_y = new_event_y; + } + event_x = Math.min(implementation.getWidth() - 1, Math.max(0, event_x)); + event_y = Math.min(implementation.getHeight() - 1, Math.max(0, event_y)); + event_dwheel = readBuffer.getInt(); + event_nanos = readBuffer.getLong(); + return true; + } else + return false; + } } /** * @return Current events button. Returns -1 if no button state was changed */ public static synchronized int getEventButton() { - return eventButton; + synchronized (OpenGLPackageAccess.global_lock) { + return eventButton; + } } /** @@ -441,42 +443,54 @@ public class Mouse { * @return Current events button state. */ public static synchronized boolean getEventButtonState() { - return eventState; + synchronized (OpenGLPackageAccess.global_lock) { + return eventState; + } } /** * @return Current events delta x. Only valid when the mouse is grabbed. */ public static synchronized int getEventDX() { - return event_dx; + synchronized (OpenGLPackageAccess.global_lock) { + return event_dx; + } } /** * @return Current events delta y. Only valid when the mouse is grabbed. */ public static synchronized int getEventDY() { - return event_dy; + synchronized (OpenGLPackageAccess.global_lock) { + return event_dy; + } } /** * @return Current events absolute x. Only valid when the mouse is not grabbed. */ public static synchronized int getEventX() { - return event_x; + synchronized (OpenGLPackageAccess.global_lock) { + return event_x; + } } /** * @return Current events absolute y. Only valid when the mouse is not grabbed. */ public static synchronized int getEventY() { - return event_y; + synchronized (OpenGLPackageAccess.global_lock) { + return event_y; + } } /** * @return Current events delta z */ public static synchronized int getEventDWheel() { - return event_dwheel; + synchronized (OpenGLPackageAccess.global_lock) { + return event_dwheel; + } } /** @@ -488,7 +502,9 @@ public class Mouse { * @return The time in nanoseconds of the current event */ public static synchronized long getEventNanoseconds() { - return event_nanos; + synchronized (OpenGLPackageAccess.global_lock) { + return event_nanos; + } } /** @@ -498,7 +514,9 @@ public class Mouse { * @return Absolute x axis position of mouse */ public static synchronized int getX() { - return x; + synchronized (OpenGLPackageAccess.global_lock) { + return x; + } } /** @@ -508,55 +526,69 @@ public class Mouse { * @return Absolute y axis position of mouse */ public static synchronized int getY() { - return y; + synchronized (OpenGLPackageAccess.global_lock) { + return y; + } } /** * @return Movement on the x axis since last time getDX() was called. Only valid when the mouse is grabbed. */ public static synchronized int getDX() { - int result = dx; - dx = 0; - return result; + synchronized (OpenGLPackageAccess.global_lock) { + int result = dx; + dx = 0; + return result; + } } /** * @return Movement on the y axis since last time getDY() was called. Only valid when the mouse is grabbed. */ public static synchronized int getDY() { - int result = dy; - dy = 0; - return result; + synchronized (OpenGLPackageAccess.global_lock) { + int result = dy; + dy = 0; + return result; + } } /** * @return Movement of the wheel since last time getDWheel() was called */ public static synchronized int getDWheel() { - int result = dwheel; - dwheel = 0; - return result; + synchronized (OpenGLPackageAccess.global_lock) { + int result = dwheel; + dwheel = 0; + return result; + } } /** * @return Number of buttons on this mouse */ public static synchronized int getButtonCount() { - return buttonCount; + synchronized (OpenGLPackageAccess.global_lock) { + return buttonCount; + } } /** * @return Whether or not this mouse has wheel support */ public static synchronized boolean hasWheel() { - return hasWheel; + synchronized (OpenGLPackageAccess.global_lock) { + return hasWheel; + } } /** * @return whether or not the mouse has grabbed the cursor */ public static synchronized boolean isGrabbed() { - return isGrabbed; + synchronized (OpenGLPackageAccess.global_lock) { + return isGrabbed; + } } /** @@ -568,14 +600,16 @@ public class Mouse { * @param grab whether the mouse should be grabbed */ public static synchronized void setGrabbed(boolean grab) { - isGrabbed = grab; - if (isCreated()) { - implementation.grabMouse(isGrabbed); - // Get latest values from native side - poll(); - event_x = x; - event_y = y; - resetMouse(); + synchronized (OpenGLPackageAccess.global_lock) { + isGrabbed = grab; + if (isCreated()) { + implementation.grabMouse(isGrabbed); + // Get latest values from native side + poll(); + event_x = x; + event_y = y; + resetMouse(); + } } } @@ -585,12 +619,14 @@ public class Mouse { * shouldn't be called otherwise */ public static synchronized void updateCursor() { - if (emulateCursorAnimation && currentCursor != null && currentCursor.hasTimedOut()) { - currentCursor.nextCursor(); - try { - setNativeCursor(currentCursor); - } catch (LWJGLException e) { - if (LWJGLUtil.DEBUG) e.printStackTrace(); + synchronized (OpenGLPackageAccess.global_lock) { + if (emulateCursorAnimation && currentCursor != null && currentCursor.hasTimedOut()) { + currentCursor.nextCursor(); + try { + setNativeCursor(currentCursor); + } catch (LWJGLException e) { + if (LWJGLUtil.DEBUG) e.printStackTrace(); + } } } } diff --git a/src/java/org/lwjgl/input/OpenGLPackageAccess.java b/src/java/org/lwjgl/input/OpenGLPackageAccess.java new file mode 100644 index 00000000..d8edaab7 --- /dev/null +++ b/src/java/org/lwjgl/input/OpenGLPackageAccess.java @@ -0,0 +1,49 @@ +package org.lwjgl.input; + +import org.lwjgl.opengl.InputImplementation; +import org.lwjgl.opengl.Display; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; +import java.security.PrivilegedActionException; + +/** + * This class contains utilities for accessing the org.lwjgl.opengl + * package through (privileged) reflection. + */ +final class OpenGLPackageAccess { + final static Object global_lock; + + static { + try { + global_lock = AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws Exception { + Field lock_field = Class.forName("org.lwjgl.opengl.GlobalLock").getDeclaredField("lock"); + lock_field.setAccessible(true); + return lock_field.get(null); + } + }); + } catch (PrivilegedActionException e) { + throw new Error(e); + } + } + + static InputImplementation createImplementation() { + /* Use reflection since we can't make Display.getImplementation + * public + */ + try { + return (InputImplementation)AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws Exception { + Method getImplementation_method = Display.class.getDeclaredMethod("getImplementation", null); + getImplementation_method.setAccessible(true); + return getImplementation_method.invoke(null, null); + } + }); + } catch (PrivilegedActionException e) { + throw new Error(e); + } + } +} diff --git a/src/java/org/lwjgl/opengl/Display.java b/src/java/org/lwjgl/opengl/Display.java index d7e34276..08200ebe 100644 --- a/src/java/org/lwjgl/opengl/Display.java +++ b/src/java/org/lwjgl/opengl/Display.java @@ -102,6 +102,9 @@ public final class Display { private static PeerInfo peer_info; private static Context context; + /** The Drawable instance that tracks the current Display context */ + private final static Drawable drawable; + private static boolean window_created = false; static { @@ -113,23 +116,22 @@ public final class Display { } catch (LWJGLException e) { throw new RuntimeException(e); } + drawable = new Drawable() { + public Context getContext() { + synchronized (GlobalLock.lock) { + return isCreated() ? context : null; + } + } + }; } /** * Fetch the Drawable from the Display. * - * @return the Drawable corresponding to the Display context, or null if display is - * not created. + * @return the Drawable corresponding to the Display context */ public static Drawable getDrawable() { - if (context != null) { - return new Drawable() { - public Context getContext() { - return context; - } - }; - } else - return null; + return drawable; } private static DisplayImplementation createDisplayImplementation() { @@ -167,22 +169,24 @@ public final class Display { * @return an array of all display modes the system reckons it can handle. */ public static DisplayMode[] getAvailableDisplayModes() throws LWJGLException { - DisplayMode[] unfilteredModes = display_impl.getAvailableDisplayModes(); + synchronized (GlobalLock.lock) { + DisplayMode[] unfilteredModes = display_impl.getAvailableDisplayModes(); - if (unfilteredModes == null) { - return new DisplayMode[0]; + if (unfilteredModes == null) { + return new DisplayMode[0]; + } + + // We'll use a HashSet to filter out the duplicated modes + HashSet modes = new HashSet(unfilteredModes.length); + + modes.addAll(Arrays.asList(unfilteredModes)); + DisplayMode[] filteredModes = new DisplayMode[modes.size()]; + modes.toArray(filteredModes); + + LWJGLUtil.log("Removed " + (unfilteredModes.length - filteredModes.length) + " duplicate displaymodes"); + + return filteredModes; } - - // We'll use a HashSet to filter out the duplicated modes - HashSet modes = new HashSet(unfilteredModes.length); - - modes.addAll(Arrays.asList(unfilteredModes)); - DisplayMode[] filteredModes = new DisplayMode[modes.size()]; - modes.toArray(filteredModes); - - LWJGLUtil.log("Removed " + (unfilteredModes.length - filteredModes.length) + " duplicate displaymodes"); - - return filteredModes; } /** @@ -190,7 +194,9 @@ public final class Display { * @return The current display mode */ public static DisplayMode getDisplayMode() { - return current_mode; + synchronized (GlobalLock.lock) { + return current_mode; + } } /** @@ -204,24 +210,26 @@ public final class Display { * @throws LWJGLException if the display mode could not be set */ public static void setDisplayMode(DisplayMode mode) throws LWJGLException { - if (mode == null) - throw new NullPointerException("mode must be non-null"); - current_mode = mode; - if (isCreated()) { - destroyWindow(); - // If mode is not fullscreen capable, make sure we are in windowed mode - if (!mode.isFullscreen()) - resetFullscreen(); - try { - if (fullscreen) - switchDisplayMode(); - createWindow(); - makeCurrent(); - } catch (LWJGLException e) { - destroyContext(); - destroyPeerInfo(); - display_impl.resetDisplayMode(); - throw e; + synchronized (GlobalLock.lock) { + if (mode == null) + throw new NullPointerException("mode must be non-null"); + current_mode = mode; + if (isCreated()) { + destroyWindow(); + // If mode is not fullscreen capable, make sure we are in windowed mode + if (!mode.isFullscreen()) + resetFullscreen(); + try { + if (fullscreen) + switchDisplayMode(); + createWindow(); + makeCurrent(); + } catch (LWJGLException e) { + destroyContext(); + destroyPeerInfo(); + display_impl.resetDisplayMode(); + throw e; + } } } } @@ -304,35 +312,37 @@ public final class Display { * @param contrast The contrast, larger than 0.0. */ public static void setDisplayConfiguration(float gamma, float brightness, float contrast) throws LWJGLException { - if (!isCreated()) { - throw new LWJGLException("Display not yet created."); + synchronized (GlobalLock.lock) { + if (!isCreated()) { + throw new LWJGLException("Display not yet created."); + } + if (brightness < -1.0f || brightness > 1.0f) + throw new IllegalArgumentException("Invalid brightness value"); + if (contrast < 0.0f) + throw new IllegalArgumentException("Invalid contrast value"); + int rampSize = display_impl.getGammaRampLength(); + if (rampSize == 0) { + throw new LWJGLException("Display configuration not supported"); + } + FloatBuffer gammaRamp = BufferUtils.createFloatBuffer(rampSize); + for (int i = 0; i < rampSize; i++) { + float intensity = (float)i/(rampSize - 1); + // apply gamma + float rampEntry = (float)java.lang.Math.pow(intensity, gamma); + // apply brightness + rampEntry += brightness; + // apply contrast + rampEntry = (rampEntry - 0.5f)*contrast + 0.5f; + // Clamp entry to [0, 1] + if (rampEntry > 1.0f) + rampEntry = 1.0f; + else if (rampEntry < 0.0f) + rampEntry = 0.0f; + gammaRamp.put(i, rampEntry); + } + display_impl.setGammaRamp(gammaRamp); + LWJGLUtil.log("Gamma set, gamma = " + gamma + ", brightness = " + brightness + ", contrast = " + contrast); } - if (brightness < -1.0f || brightness > 1.0f) - throw new IllegalArgumentException("Invalid brightness value"); - if (contrast < 0.0f) - throw new IllegalArgumentException("Invalid contrast value"); - int rampSize = display_impl.getGammaRampLength(); - if (rampSize == 0) { - throw new LWJGLException("Display configuration not supported"); - } - FloatBuffer gammaRamp = BufferUtils.createFloatBuffer(rampSize); - for (int i = 0; i < rampSize; i++) { - float intensity = (float)i/(rampSize - 1); - // apply gamma - float rampEntry = (float)java.lang.Math.pow(intensity, gamma); - // apply brightness - rampEntry += brightness; - // apply contrast - rampEntry = (rampEntry - 0.5f)*contrast + 0.5f; - // Clamp entry to [0, 1] - if (rampEntry > 1.0f) - rampEntry = 1.0f; - else if (rampEntry < 0.0f) - rampEntry = 0.0f; - gammaRamp.put(i, rampEntry); - } - display_impl.setGammaRamp(gammaRamp); - LWJGLUtil.log("Gamma set, gamma = " + gamma + ", brightness = " + brightness + ", contrast = " + contrast); } /** @@ -342,14 +352,16 @@ public final class Display { * @param fps The desired frame rate, in frames per second */ public static void sync3(int fps) { - float frameTime = 1.0f / (fps > 1 ? fps - 1 : 1); - timeNow = Sys.getTime(); - while (timeNow > timeThen && (float) (timeNow - timeThen) / (float) Sys.getTimerResolution() < frameTime) { - // This is a system-friendly way of allowing other stuff to use CPU if it wants to - Thread.yield(); + synchronized (GlobalLock.lock) { + float frameTime = 1.0f / (fps > 1 ? fps - 1 : 1); timeNow = Sys.getTime(); + while (timeNow > timeThen && (float) (timeNow - timeThen) / (float) Sys.getTimerResolution() < frameTime) { + // This is a system-friendly way of allowing other stuff to use CPU if it wants to + Thread.yield(); + timeNow = Sys.getTime(); + } + timeThen = timeNow; } - timeThen = timeNow; } private static long timeLate; @@ -360,20 +372,22 @@ public final class Display { * @param fps The desired frame rate, in frames per second */ public static void sync2(int fps) { - long gapTo = Sys.getTimerResolution() / fps + timeThen; - timeNow = Sys.getTime(); - - while (gapTo > timeNow + timeLate) { - Thread.yield(); + synchronized (GlobalLock.lock) { + long gapTo = Sys.getTimerResolution() / fps + timeThen; timeNow = Sys.getTime(); + + while (gapTo > timeNow + timeLate) { + Thread.yield(); + timeNow = Sys.getTime(); + } + + if (gapTo < timeNow) + timeLate = timeNow - gapTo; + else + timeLate = 0; + + timeThen = timeNow; } - - if (gapTo < timeNow) - timeLate = timeNow - gapTo; - else - timeLate = 0; - - timeThen = timeNow; } /** @@ -382,23 +396,25 @@ public final class Display { * @param fps The desired frame rate, in frames per second */ public static void sync(int fps) { - long gapTo = Sys.getTimerResolution() / fps + timeThen; - timeNow = Sys.getTime(); - - while (gapTo > timeNow + timeLate) { - try { - Thread.sleep(1); - } catch (InterruptedException e) { - } + synchronized (GlobalLock.lock) { + long gapTo = Sys.getTimerResolution() / fps + timeThen; timeNow = Sys.getTime(); + + while (gapTo > timeNow + timeLate) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + } + timeNow = Sys.getTime(); + } + + if (gapTo < timeNow) + timeLate = timeNow - gapTo; + else + timeLate = 0; + + timeThen = timeNow; } - - if (gapTo < timeNow) - timeLate = timeNow - gapTo; - else - timeLate = 0; - - timeThen = timeNow; } /** @@ -420,13 +436,17 @@ public final class Display { * @return the title of the window */ public static String getTitle() { - return title; + synchronized (GlobalLock.lock) { + return title; + } } private static void resetFullscreen() { - if (Display.fullscreen) { - Display.fullscreen = false; - display_impl.resetDisplayMode(); + synchronized (GlobalLock.lock) { + if (Display.fullscreen) { + Display.fullscreen = false; + display_impl.resetDisplayMode(); + } } } @@ -442,24 +462,26 @@ public final class Display { * from getAvailableDisplayModes() or if the mode switch fails. */ public static void setFullscreen(boolean fullscreen) throws LWJGLException { - if (Display.fullscreen != fullscreen) { - Display.fullscreen = fullscreen; - if (!isCreated()) - return; - destroyWindow(); - try { - if (fullscreen) { - switchDisplayMode(); - } else { + synchronized (GlobalLock.lock) { + if (Display.fullscreen != fullscreen) { + Display.fullscreen = fullscreen; + if (!isCreated()) + return; + destroyWindow(); + try { + if (fullscreen) { + switchDisplayMode(); + } else { + display_impl.resetDisplayMode(); + } + createWindow(); + makeCurrent(); + } catch (LWJGLException e) { + destroyContext(); + destroyPeerInfo(); display_impl.resetDisplayMode(); + throw e; } - createWindow(); - makeCurrent(); - } catch (LWJGLException e) { - destroyContext(); - destroyPeerInfo(); - display_impl.resetDisplayMode(); - throw e; } } } @@ -468,7 +490,9 @@ public final class Display { * @return whether the Display is in fullscreen mode */ public static boolean isFullscreen() { - return fullscreen; + synchronized (GlobalLock.lock) { + return fullscreen; + } } /** @@ -476,42 +500,50 @@ public final class Display { * @param newTitle The new window title */ public static void setTitle(String newTitle) { - if (newTitle == null) { - newTitle = ""; + synchronized (GlobalLock.lock) { + if (newTitle == null) { + newTitle = ""; + } + title = newTitle; + if (isCreated()) + display_impl.setTitle(title); } - title = newTitle; - if (isCreated()) - display_impl.setTitle(title); } /** * @return true if the user or operating system has asked the window to close */ public static boolean isCloseRequested() { - if (!isCreated()) - throw new IllegalStateException("Cannot determine close requested state of uncreated window"); - display_impl.update(); - return display_impl.isCloseRequested(); + synchronized (GlobalLock.lock) { + if (!isCreated()) + throw new IllegalStateException("Cannot determine close requested state of uncreated window"); + display_impl.update(); + return display_impl.isCloseRequested(); + } } /** * @return true if the window is visible, false if not */ public static boolean isVisible() { - if (!isCreated()) - throw new IllegalStateException("Cannot determine minimized state of uncreated window"); - display_impl.update(); - return display_impl.isVisible(); + synchronized (GlobalLock.lock) { + if (!isCreated()) + throw new IllegalStateException("Cannot determine minimized state of uncreated window"); + display_impl.update(); + return display_impl.isVisible(); + } } /** * @return true if window is active, that is, the foreground display of the operating system. */ public static boolean isActive() { - if (!isCreated()) - throw new IllegalStateException("Cannot determine focused state of uncreated window"); - display_impl.update(); - return display_impl.isActive(); + synchronized (GlobalLock.lock) { + if (!isCreated()) + throw new IllegalStateException("Cannot determine focused state of uncreated window"); + display_impl.update(); + return display_impl.isActive(); + } } /** @@ -525,10 +557,12 @@ public final class Display { * and needs to repaint itself */ public static boolean isDirty() { - if (!isCreated()) - throw new IllegalStateException("Cannot determine dirty state of uncreated window"); - display_impl.update(); - return display_impl.isDirty(); + synchronized (GlobalLock.lock) { + if (!isCreated()) + throw new IllegalStateException("Cannot determine dirty state of uncreated window"); + display_impl.update(); + return display_impl.isDirty(); + } } /** @@ -537,10 +571,12 @@ public final class Display { * the application. */ public static void processMessages() { - if (!isCreated()) - throw new IllegalStateException("Display not created"); + synchronized (GlobalLock.lock) { + if (!isCreated()) + throw new IllegalStateException("Display not created"); - display_impl.update(); + display_impl.update(); + } } /** @@ -549,11 +585,13 @@ public final class Display { * @throws OpenGLException if an OpenGL error has occured since the last call to GL11.glGetError() */ public static void swapBuffers() throws LWJGLException { - if (!isCreated()) - throw new IllegalStateException("Display not created"); + synchronized (GlobalLock.lock) { + if (!isCreated()) + throw new IllegalStateException("Display not created"); - Util.checkGLError(); - Context.swapBuffers(); + Util.checkGLError(); + Context.swapBuffers(); + } } /** @@ -562,20 +600,22 @@ public final class Display { * @throws OpenGLException if an OpenGL error has occured since the last call to GL11.glGetError() */ public static void update() { - if (!isCreated()) - throw new IllegalStateException("Display not created"); + synchronized (GlobalLock.lock) { + if (!isCreated()) + throw new IllegalStateException("Display not created"); - // We paint only when the window is visible or dirty - if (isVisible() || isDirty()) { - try { - swapBuffers(); - } catch (LWJGLException e) { - throw new RuntimeException(e); + // We paint only when the window is visible or dirty + if (isVisible() || isDirty()) { + try { + swapBuffers(); + } catch (LWJGLException e) { + throw new RuntimeException(e); + } } - } - processMessages(); - pollDevices(); + processMessages(); + pollDevices(); + } } static void pollDevices() { @@ -600,10 +640,12 @@ public final class Display { * @throws LWJGLException If the context could not be released */ public static void releaseContext() throws LWJGLException { - if (!isCreated()) - throw new IllegalStateException("Display is not created"); - if (context.isCurrent()) - Context.releaseCurrentContext(); + synchronized (GlobalLock.lock) { + if (!isCreated()) + throw new IllegalStateException("Display is not created"); + if (context.isCurrent()) + Context.releaseCurrentContext(); + } } /** @@ -612,9 +654,11 @@ public final class Display { * @throws LWJGLException If the context could not be made current */ public static void makeCurrent() throws LWJGLException { - if (!isCreated()) - throw new IllegalStateException("Display is not created"); - context.makeCurrent(); + synchronized (GlobalLock.lock) { + if (!isCreated()) + throw new IllegalStateException("Display is not created"); + context.makeCurrent(); + } } /** @@ -629,7 +673,9 @@ public final class Display { * @throws LWJGLException */ public static void create() throws LWJGLException { - create(new PixelFormat()); + synchronized (GlobalLock.lock) { + create(new PixelFormat()); + } } /** @@ -645,7 +691,9 @@ public final class Display { * @throws LWJGLException */ public static void create(PixelFormat pixel_format) throws LWJGLException { - create(pixel_format, null); + synchronized (GlobalLock.lock) { + create(pixel_format, null); + } } private static void removeShutdownHook() { @@ -680,38 +728,40 @@ public final class Display { * @throws LWJGLException */ public static void create(PixelFormat pixel_format, Drawable shared_drawable) throws LWJGLException { - if (isCreated()) - throw new IllegalStateException("Only one LWJGL context may be instantiated at any one time."); - if (pixel_format == null) - throw new NullPointerException("pixel_format cannot be null"); - removeShutdownHook(); - registerShutdownHook(); - if (fullscreen) - switchDisplayMode(); - try { - peer_info = display_impl.createPeerInfo(pixel_format); + synchronized (GlobalLock.lock) { + if (isCreated()) + throw new IllegalStateException("Only one LWJGL context may be instantiated at any one time."); + if (pixel_format == null) + throw new NullPointerException("pixel_format cannot be null"); + removeShutdownHook(); + registerShutdownHook(); + if (fullscreen) + switchDisplayMode(); try { - createWindow(); + peer_info = display_impl.createPeerInfo(pixel_format); try { - context = new Context(peer_info, shared_drawable != null ? shared_drawable.getContext() : null); + createWindow(); try { - makeCurrent(); - initContext(); + context = new Context(peer_info, shared_drawable != null ? shared_drawable.getContext() : null); + try { + makeCurrent(); + initContext(); + } catch (LWJGLException e) { + destroyContext(); + throw e; + } } catch (LWJGLException e) { - destroyContext(); + destroyWindow(); throw e; } } catch (LWJGLException e) { - destroyWindow(); + destroyPeerInfo(); throw e; } } catch (LWJGLException e) { - destroyPeerInfo(); + display_impl.resetDisplayMode(); throw e; } - } catch (LWJGLException e) { - display_impl.resetDisplayMode(); - throw e; } } @@ -779,17 +829,19 @@ public final class Display { * regardless of whether the Display was the current rendering context. */ public static void destroy() { - if (!isCreated()) { - return; - } + synchronized (GlobalLock.lock) { + if (!isCreated()) { + return; + } - destroyWindow(); - destroyContext(); - destroyPeerInfo(); - x = y = -1; - cached_icons = null; - reset(); - removeShutdownHook(); + destroyWindow(); + destroyContext(); + destroyPeerInfo(); + x = y = -1; + cached_icons = null; + reset(); + removeShutdownHook(); + } } private static void destroyPeerInfo() { @@ -819,7 +871,7 @@ public final class Display { /** * @return the unique Display context (or null, if the Display has not been created) */ - public static Context getContext() { + private static Context getContext() { return context; } @@ -827,7 +879,9 @@ public final class Display { * @return true if the window's native peer has been created */ public static boolean isCreated() { - return window_created; + synchronized (GlobalLock.lock) { + return window_created; + } } /** @@ -840,9 +894,11 @@ public final class Display { * @param sync true to synchronize; false to ignore synchronization */ public static void setSwapInterval(int value) { - swap_interval = value; - if (isCreated()) - Context.setSwapInterval(swap_interval); + synchronized (GlobalLock.lock) { + swap_interval = value; + if (isCreated()) + Context.setSwapInterval(swap_interval); + } } @@ -852,7 +908,9 @@ public final class Display { * @param sync true to synchronize; false to ignore synchronization */ public static void setVSyncEnabled(boolean sync) { - setSwapInterval(sync ? 1 : 0); + synchronized (GlobalLock.lock) { + setSwapInterval(sync ? 1 : 0); + } } /** @@ -864,19 +922,21 @@ public final class Display { * @param x The new window location on the x axis * @param y The new window location on the y axis */ - public static void setLocation(int x, int y) { - if (fullscreen) { - return; - } + public static void setLocation(int new_x, int new_y) { + synchronized (GlobalLock.lock) { + if (fullscreen) { + return; + } - // offset if already created - if(isCreated()) { - display_impl.reshape(x, y, current_mode.getWidth(), current_mode.getHeight()); - } + // cache position + x = new_x; + y = new_y; - // cache position - Display.x = x; - Display.y = y; + // offset if already created + if(isCreated()) { + display_impl.reshape(x, y, current_mode.getWidth(), current_mode.getHeight()); + } + } } /** @@ -885,7 +945,9 @@ public final class Display { * @return a String */ public static String getAdapter() { - return display_impl.getAdapter(); + synchronized (GlobalLock.lock) { + return display_impl.getAdapter(); + } } /** @@ -894,7 +956,9 @@ public final class Display { * @return a String */ public static String getVersion() { - return display_impl.getVersion(); + synchronized (GlobalLock.lock) { + return display_impl.getVersion(); + } } @@ -915,22 +979,23 @@ public final class Display { * @return number of icons used, or 0 if display hasn't been created */ public static int setIcon(ByteBuffer[] icons) { - - // make deep copy so we dont rely on the supplied buffers later on - // don't recache! - if(cached_icons != icons) { - cached_icons = new ByteBuffer[icons.length]; - for(int i=0;i