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.

This commit is contained in:
Elias Naur 2007-02-12 12:18:26 +00:00
parent c880c689ff
commit 56178d97fd
8 changed files with 653 additions and 460 deletions

View File

@ -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<cursors.length; i++) {
Mouse.getImplementation().destroyCursor(cursors[i].cursorHandle);
}
destroyed = true;
}
for(int i=0; i<cursors.length; i++) {
Mouse.getImplementation().destroyCursor(cursors[i].cursorHandle);
}
destroyed = true;
}
/**

View File

@ -307,9 +307,11 @@ public class Keyboard {
* @throws LWJGLException if the keyboard 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(Mouse.createImplementation());
create(OpenGLPackageAccess.createImplementation());
}
}
private static void reset() {
@ -325,18 +327,22 @@ public class Keyboard {
* @return true if the keyboard has been created
*/
public static synchronized boolean isCreated() {
return created;
synchronized (OpenGLPackageAccess.global_lock) {
return created;
}
}
/**
* "Destroy" the keyboard
*/
public static synchronized void destroy() {
if (!created)
return;
created = false;
implementation.destroyKeyboard();
reset();
synchronized (OpenGLPackageAccess.global_lock) {
if (!created)
return;
created = false;
implementation.destroyKeyboard();
reset();
}
}
/**
@ -358,10 +364,12 @@ public class Keyboard {
* @see org.lwjgl.input.Keyboard#getEventCharacter()
*/
public static synchronized void poll() {
if (!created)
throw new IllegalStateException("Keyboard must be created before you can poll the device");
implementation.pollKeyboard(keyDownBuffer);
read();
synchronized (OpenGLPackageAccess.global_lock) {
if (!created)
throw new IllegalStateException("Keyboard must be created before you can poll the device");
implementation.pollKeyboard(keyDownBuffer);
read();
}
}
private static void read() {
@ -376,9 +384,11 @@ public class Keyboard {
* @return true if the key is down according to the last poll()
*/
public static synchronized boolean isKeyDown(int key) {
if (!created)
throw new IllegalStateException("Keyboard must be created before you can query key state");
return keyDownBuffer.get(key) != 0;
synchronized (OpenGLPackageAccess.global_lock) {
if (!created)
throw new IllegalStateException("Keyboard must be created before you can query key state");
return keyDownBuffer.get(key) != 0;
}
}
/**
@ -419,9 +429,11 @@ public class Keyboard {
* @return the number of keyboard events
*/
public static synchronized int getNumKeyboardEvents() {
if (!created)
throw new IllegalStateException("Keyboard must be created before you can read events");
return readBuffer.remaining()/EVENT_SIZE;
synchronized (OpenGLPackageAccess.global_lock) {
if (!created)
throw new IllegalStateException("Keyboard must be created before you can read events");
return readBuffer.remaining()/EVENT_SIZE;
}
}
/**
@ -436,17 +448,19 @@ public class Keyboard {
* @return true if a keyboard event was read, false otherwise
*/
public static synchronized boolean next() {
if (!created)
throw new IllegalStateException("Keyboard must be created before you can read events");
synchronized (OpenGLPackageAccess.global_lock) {
if (!created)
throw new IllegalStateException("Keyboard must be created before you can read events");
if (readBuffer.hasRemaining()) {
eventKey = readBuffer.getInt() & 0xFF;
eventState = readBuffer.get() != 0;
eventCharacter = readBuffer.getInt();
eventNanos = readBuffer.getLong();
return true;
} else {
return false;
if (readBuffer.hasRemaining()) {
eventKey = readBuffer.getInt() & 0xFF;
eventState = readBuffer.get() != 0;
eventCharacter = readBuffer.getInt();
eventNanos = readBuffer.getLong();
return true;
} else {
return false;
}
}
}
@ -454,14 +468,18 @@ public class Keyboard {
* @return Number of keys on this keyboard
*/
public static synchronized int getKeyCount() {
return keyCount;
synchronized (OpenGLPackageAccess.global_lock) {
return keyCount;
}
}
/**
* @return The character from the current event
*/
public static synchronized char getEventCharacter() {
return (char)eventCharacter;
synchronized (OpenGLPackageAccess.global_lock) {
return (char)eventCharacter;
}
}
/**
@ -472,7 +490,9 @@ public class Keyboard {
* @return The key from the current event
*/
public static synchronized int getEventKey() {
return eventKey;
synchronized (OpenGLPackageAccess.global_lock) {
return eventKey;
}
}
/**
@ -482,7 +502,9 @@ public class Keyboard {
* @return True if key was down, or false if released
*/
public static synchronized boolean getEventKeyState() {
return eventState;
synchronized (OpenGLPackageAccess.global_lock) {
return eventState;
}
}
/**
@ -493,6 +515,8 @@ public class Keyboard {
* @return The time in nanoseconds of the current event
*/
public static synchronized long getEventNanoseconds() {
return eventNanos;
synchronized (OpenGLPackageAccess.global_lock) {
return eventNanos;
}
}
}

View File

@ -43,11 +43,6 @@ import org.lwjgl.Sys;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.InputImplementation;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
/**
* <br>
@ -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();
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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<icons.length; i++) {
cached_icons[i] = BufferUtils.createByteBuffer(icons[i].capacity());
cached_icons[i].put(icons[i]);
cached_icons[i].flip();
synchronized (GlobalLock.lock) {
// 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<icons.length; i++) {
cached_icons[i] = BufferUtils.createByteBuffer(icons[i].capacity());
cached_icons[i].put(icons[i]);
cached_icons[i].flip();
}
}
if(Display.isCreated()) {
return display_impl.setIcon(cached_icons);
} else {
return 0;
}
}
if(Display.isCreated()) {
return display_impl.setIcon(cached_icons);
} else {
return 0;
}
}
}

View File

@ -0,0 +1,9 @@
package org.lwjgl.opengl;
/**
* This class contains the global lock that LWJGL will use to
* synchronize access to Display.
*/
final class GlobalLock {
final static Object lock = new Object();
}

View File

@ -247,13 +247,13 @@ final class MacOSXDisplay implements DisplayImplementation {
*/
if (Display.isFullscreen() && (frame.getCanvas().syncCanvasPainted() || should_update)) {
try {
MacOSXContextImplementation.resetView(Display.getContext().getPeerInfo(), Display.getContext());
MacOSXContextImplementation.resetView(Display.getDrawable().getContext().getPeerInfo(), Display.getDrawable().getContext());
} catch (LWJGLException e) {
LWJGLUtil.log("Failed to reset context: " + e);
}
}
if (should_update) {
Display.getContext().update();
Display.getDrawable().getContext().update();
/* This is necessary to make sure the context won't "forget" about the view size */
GL11.glGetInteger(GL11.GL_VIEWPORT, current_viewport);
GL11.glViewport(current_viewport.get(0), current_viewport.get(1), current_viewport.get(2), current_viewport.get(3));

View File

@ -304,8 +304,8 @@ final class WindowsDisplay implements DisplayImplementation {
* is maximized helps some gfx recover from fullscreen
*/
try {
if (Display.getContext() != null && Display.getContext().isCurrent())
Display.getContext().makeCurrent();
if (Display.getDrawable().getContext() != null && Display.getDrawable().getContext().isCurrent())
Display.getDrawable().getContext().makeCurrent();
} catch (LWJGLException e) {
LWJGLUtil.log("Exception occurred while trying to make context current: " + e);
}