diff --git a/build.xml b/build.xml index e0cb53b4..45b88278 100644 --- a/build.xml +++ b/build.xml @@ -322,12 +322,14 @@ - + + + diff --git a/platform_build/macosx_ant/build.xml b/platform_build/macosx_ant/build.xml index b1696a34..0cde0b42 100644 --- a/platform_build/macosx_ant/build.xml +++ b/platform_build/macosx_ant/build.xml @@ -1,103 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/src/java/org/lwjgl/MacOSXSysImplementation.java b/src/java/org/lwjgl/MacOSXSysImplementation.java index 54506609..fa1d56da 100644 --- a/src/java/org/lwjgl/MacOSXSysImplementation.java +++ b/src/java/org/lwjgl/MacOSXSysImplementation.java @@ -31,9 +31,11 @@ */ package org.lwjgl; -import java.awt.Toolkit; - import com.apple.eio.FileManager; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.PrivilegedExceptionAction; +import java.lang.UnsatisfiedLinkError; /** * @@ -45,10 +47,23 @@ final class MacOSXSysImplementation extends J2SESysImplementation { private static final int JNI_VERSION = 23; static { - // Make sure AWT is properly initialized. This avoids hangs on Mac OS X 10.3 - Toolkit.getDefaultToolkit(); + // Manually start the AWT Application Loop + java.awt.Toolkit.getDefaultToolkit(); + + // manually load libjawt.dylib into vm, needed since Java 7 + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + try { + System.loadLibrary("jawt"); + } catch (UnsatisfiedLinkError e) { + // catch and ignore an already loaded in another classloader + // exception, as vm already has it loaded + } + return null; + } + }); } - + public int getRequiredJNIVersion() { return JNI_VERSION; } diff --git a/src/java/org/lwjgl/input/Cursor.java b/src/java/org/lwjgl/input/Cursor.java index 6eec7d0b..7f257f63 100644 --- a/src/java/org/lwjgl/input/Cursor.java +++ b/src/java/org/lwjgl/input/Cursor.java @@ -163,17 +163,32 @@ public class Cursor { IntBuffer images_copy = BufferUtils.createIntBuffer(images.remaining()); flipImages(width, height, numImages, images, images_copy); - // Win32 doesn't (afaik) allow for animation based cursors, except when they're - // in the .ani format, which we don't support. + // Mac and Windows doesn't (afaik) allow for animation based cursors, except in the .ani + // format on Windows, which we don't support. // The cursor animation was therefor developed using java side time tracking. // unfortunately X flickers when changing cursor. We therefore check for either - // Win32 or X and do accordingly. This hasn't been implemented on Mac, but we - // might want to split it into a X/Win/Mac cursor if it gets too cluttered + // Windows, Mac or X and do accordingly. + // we might want to split it into a X/Win/Mac cursor if it gets too cluttered CursorElement[] cursors; switch (LWJGLUtil.getPlatform()) { case LWJGLUtil.PLATFORM_MACOSX: - /* Fall through */ + + // OS X requires the image format to be in ABGR format + convertARGBtoABGR(images_copy); + + // create our cursor elements + cursors = new CursorElement[numImages]; + for(int i=0; i>> 24); + byte blue = (byte)(argbColor >>> 16); + byte green = (byte)(argbColor >>> 8); + byte red = (byte)argbColor; + + int abgrColor = ((alpha & 0xff) << 24 ) + ((red & 0xff) << 16 ) + ((green & 0xff) << 8 ) + ((blue & 0xff) ); + + imageBuffer.put(i, abgrColor); + } + } /** * Flips the images so they're oriented according to opengl diff --git a/src/java/org/lwjgl/input/Keyboard.java b/src/java/org/lwjgl/input/Keyboard.java index a5d733f9..00c7652b 100644 --- a/src/java/org/lwjgl/input/Keyboard.java +++ b/src/java/org/lwjgl/input/Keyboard.java @@ -158,7 +158,11 @@ public class Keyboard { public static final int KEY_F13 = 0x64; /* (NEC PC98) */ public static final int KEY_F14 = 0x65; /* (NEC PC98) */ public static final int KEY_F15 = 0x66; /* (NEC PC98) */ + public static final int KEY_F16 = 0x67; /* Extended Function keys - (Mac) */ + public static final int KEY_F17 = 0x68; + public static final int KEY_F18 = 0x69; public static final int KEY_KANA = 0x70; /* (Japanese keyboard) */ + public static final int KEY_F19 = 0x71; /* Extended Function keys - (Mac) */ public static final int KEY_CONVERT = 0x79; /* (Japanese keyboard) */ public static final int KEY_NOCONVERT = 0x7B; /* (Japanese keyboard) */ public static final int KEY_YEN = 0x7D; /* (Japanese keyboard) */ @@ -173,10 +177,12 @@ public class Keyboard { public static final int KEY_UNLABELED = 0x97; /* (J3100) */ public static final int KEY_NUMPADENTER = 0x9C; /* Enter on numeric keypad */ public static final int KEY_RCONTROL = 0x9D; + public static final int KEY_SECTION = 0xA7; /* Section symbol (Mac) */ public static final int KEY_NUMPADCOMMA = 0xB3; /* , on numeric keypad (NEC PC98) */ public static final int KEY_DIVIDE = 0xB5; /* / on numeric keypad */ public static final int KEY_SYSRQ = 0xB7; public static final int KEY_RMENU = 0xB8; /* right Alt */ + public static final int KEY_FUNCTION = 0xC4; /* Function (Mac) */ public static final int KEY_PAUSE = 0xC5; /* Pause */ public static final int KEY_HOME = 0xC7; /* Home on arrow keypad */ public static final int KEY_UP = 0xC8; /* UpArrow on arrow keypad */ @@ -188,7 +194,8 @@ public class Keyboard { public static final int KEY_NEXT = 0xD1; /* PgDn on arrow keypad */ public static final int KEY_INSERT = 0xD2; /* Insert on arrow keypad */ public static final int KEY_DELETE = 0xD3; /* Delete on arrow keypad */ - public static final int KEY_LMETA = 0xDB; /* Left Windows/Option key */ + public static final int KEY_CLEAR = 0xDA; /* Clear key (Mac) */ + public static final int KEY_LMETA = 0xDB; /* Left Windows/Option key */ /** * The left windows key, mapped to KEY_LMETA * @@ -229,7 +236,8 @@ public class Keyboard { && Modifier.isPublic(field.getModifiers()) && Modifier.isFinal(field.getModifiers()) && field.getType().equals(int.class) - && field.getName().startsWith("KEY_") ) { + && field.getName().startsWith("KEY_") + && !field.getName().endsWith("WIN") ) { /* Don't use deprecated names */ int key = field.getInt(null); String name = field.getName().substring(4); diff --git a/src/java/org/lwjgl/opengl/AWTSurfaceLock.java b/src/java/org/lwjgl/opengl/AWTSurfaceLock.java index a428115f..b0935dbc 100644 --- a/src/java/org/lwjgl/opengl/AWTSurfaceLock.java +++ b/src/java/org/lwjgl/opengl/AWTSurfaceLock.java @@ -82,16 +82,13 @@ final class AWTSurfaceLock { // It is only needed on first call, so we avoid it on all subsequent calls // due to performance.. - // Allow the use of a Core Animation Layer only when using non fullscreen Display.setParent() or AWTGLCanvas - final boolean allowCALayer = ((Display.getParent() != null && !Display.isFullscreen()) || component instanceof AWTGLCanvas) && isApplet(component) && LWJGLUtil.isMacOSXEqualsOrBetterThan(10, 6); - if (firstLockSucceeded) - return lockAndInitHandle(lock_buffer, component, allowCALayer); + return lockAndInitHandle(lock_buffer, component); else try { firstLockSucceeded = AccessController.doPrivileged(new PrivilegedExceptionAction() { public Boolean run() throws LWJGLException { - return lockAndInitHandle(lock_buffer, component, allowCALayer); + return lockAndInitHandle(lock_buffer, component); } }); return firstLockSucceeded; @@ -100,29 +97,11 @@ final class AWTSurfaceLock { } } - private static native boolean lockAndInitHandle(ByteBuffer lock_buffer, Canvas component, boolean allowCALayer) throws LWJGLException; + private static native boolean lockAndInitHandle(ByteBuffer lock_buffer, Canvas component) throws LWJGLException; void unlock() throws LWJGLException { nUnlock(lock_buffer); } private static native void nUnlock(ByteBuffer lock_buffer) throws LWJGLException; - - /** - * This method will return true if the component is running in an applet - */ - public boolean isApplet(Canvas component) { - - Component parent = component.getParent(); - - while (parent != null) { - if (parent instanceof Applet) { - return true; - } - parent = parent.getParent(); - } - - // not an applet - return false; - } } diff --git a/src/java/org/lwjgl/opengl/Display.java b/src/java/org/lwjgl/opengl/Display.java index 2e9c6584..95a24127 100644 --- a/src/java/org/lwjgl/opengl/Display.java +++ b/src/java/org/lwjgl/opengl/Display.java @@ -249,7 +249,7 @@ public final class Display { try { if ( was_fullscreen && !isFullscreen() ) display_impl.resetDisplayMode(); - else if ( isFullscreen() ) + else if ( isFullscreen() ) switchDisplayMode(); createWindow(); makeCurrentAndSetSwapInterval(); diff --git a/src/java/org/lwjgl/opengl/MacOSXCanvasPeerInfo.java b/src/java/org/lwjgl/opengl/MacOSXCanvasPeerInfo.java index 3ebf4502..d43e1d0d 100644 --- a/src/java/org/lwjgl/opengl/MacOSXCanvasPeerInfo.java +++ b/src/java/org/lwjgl/opengl/MacOSXCanvasPeerInfo.java @@ -45,18 +45,26 @@ import org.lwjgl.LWJGLUtil; */ abstract class MacOSXCanvasPeerInfo extends MacOSXPeerInfo { private final AWTSurfaceLock awt_surface = new AWTSurfaceLock(); - + public ByteBuffer window_handle; + protected MacOSXCanvasPeerInfo(PixelFormat pixel_format, ContextAttribs attribs, boolean support_pbuffer) throws LWJGLException { super(pixel_format, attribs, true, true, support_pbuffer, true); } protected void initHandle(Canvas component) throws LWJGLException { - // Allow the use of a Core Animation Layer only when using non fullscreen Display.setParent() or AWTGLCanvas - final boolean allowCALayer = ((Display.getParent() != null && !Display.isFullscreen()) || component instanceof AWTGLCanvas) && awt_surface.isApplet(component) && LWJGLUtil.isMacOSXEqualsOrBetterThan(10, 6); + boolean forceCALayer = true; + String javaVersion = System.getProperty("java.version"); - nInitHandle(awt_surface.lockAndGetHandle(component), getHandle(), allowCALayer); + if (javaVersion.startsWith("1.5") || javaVersion.startsWith("1.6")) { + // On Java 7 and newer CALayer mode is the only way to use OpenGL with AWT + // therefore force it on all JVM's except for the older Java 5 and Java 6 + // where the older cocoaViewRef NSView method maybe be available. + forceCALayer = false; + } + + window_handle = nInitHandle(awt_surface.lockAndGetHandle(component), getHandle(), window_handle, forceCALayer); } - private static native void nInitHandle(ByteBuffer surface_buffer, ByteBuffer peer_info_handle, boolean allowCALayer) throws LWJGLException; + private static native ByteBuffer nInitHandle(ByteBuffer surface_buffer, ByteBuffer peer_info_handle, ByteBuffer window_handle, boolean forceCALayer) throws LWJGLException; protected void doUnlock() throws LWJGLException { awt_surface.unlock(); diff --git a/src/java/org/lwjgl/opengl/MacOSXDisplay.java b/src/java/org/lwjgl/opengl/MacOSXDisplay.java index c6032c72..b392cffd 100644 --- a/src/java/org/lwjgl/opengl/MacOSXDisplay.java +++ b/src/java/org/lwjgl/opengl/MacOSXDisplay.java @@ -39,7 +39,6 @@ package org.lwjgl.opengl; */ import java.awt.Canvas; -import java.awt.Cursor; import java.awt.Robot; import java.nio.ByteBuffer; import java.nio.FloatBuffer; @@ -50,96 +49,153 @@ import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.List; +import org.lwjgl.input.Cursor; import org.lwjgl.BufferUtils; +import org.lwjgl.MemoryUtil; import org.lwjgl.LWJGLException; import org.lwjgl.LWJGLUtil; -import com.apple.eawt.Application; -import com.apple.eawt.ApplicationAdapter; -import com.apple.eawt.ApplicationEvent; - import static org.lwjgl.opengl.GL11.*; final class MacOSXDisplay implements DisplayImplementation { private static final int PBUFFER_HANDLE_SIZE = 24; private static final int GAMMA_LENGTH = 256; - private MacOSXCanvasListener canvas_listener; - private MacOSXFrame frame; + //private MacOSXCanvasListener canvas_listener; private Canvas canvas; private Robot robot; private MacOSXMouseEventQueue mouse_queue; private KeyboardEventQueue keyboard_queue; - private java.awt.DisplayMode requested_mode; - - /* States */ + private DisplayMode requested_mode; + + /* Members for native window use */ + private MacOSXNativeMouse mouse; + private MacOSXNativeKeyboard keyboard; + private ByteBuffer window; + private ByteBuffer context; + + private boolean skipViewportValue = false; + private static final IntBuffer current_viewport = BufferUtils.createIntBuffer(16); + + private boolean mouseInsideWindow; + private boolean close_requested; + + private boolean native_mode = true; + + private boolean updateNativeCursor = false; + + private long currentNativeCursor = 0; MacOSXDisplay() { - try { - AccessController.doPrivileged(new PrivilegedExceptionAction() { - public Object run() throws Exception { - Application.getApplication().addApplicationListener(new ApplicationAdapter() { - public void handleQuit(ApplicationEvent event) { - doHandleQuit(); - } - }); - return null; - } - }); - } catch (Throwable e) { - /** - * In an applet environment, referencing com.apple.eawt.Application can fail with - * a native exception. So log any exceptions instead of re-throwing. - */ - LWJGLUtil.log("Failed to register quit handler: " + e.getMessage()); - } + } + private native ByteBuffer nCreateWindow(int x, int y, int width, int height, boolean fullscreen, boolean undecorated, boolean resizable, boolean parented, ByteBuffer peer_info_handle, ByteBuffer window_handle) throws LWJGLException; + + private native Object nGetCurrentDisplayMode(); + + private native void nGetDisplayModes(Object modesList); + + private native boolean nIsMiniaturized(ByteBuffer window_handle); + + private native boolean nIsFocused(ByteBuffer window_handle); + + private native void nSetResizable(ByteBuffer window_handle, boolean resizable); + + private native void nResizeWindow(ByteBuffer window_handle, int x, int y, int width, int height); + + private native boolean nWasResized(ByteBuffer window_handle); + + private native int nGetX(ByteBuffer window_handle); + + private native int nGetY(ByteBuffer window_handle); + + private native int nGetWidth(ByteBuffer window_handle); + + private native int nGetHeight(ByteBuffer window_handle); + + private native boolean nIsNativeMode(ByteBuffer peer_info_handle); + + private static boolean isUndecorated() { + return Display.getPrivilegedBoolean("org.lwjgl.opengl.Window.undecorated"); + } + public void createWindow(final DrawableLWJGL drawable, DisplayMode mode, Canvas parent, int x, int y) throws LWJGLException { boolean fullscreen = Display.isFullscreen(); - hideUI(fullscreen); + boolean resizable = Display.isResizable(); + boolean parented = (parent != null) && !fullscreen; + + if (parented) this.canvas = parent; + else this.canvas = null; + close_requested = false; + + DrawableGL gl_drawable = (DrawableGL)Display.getDrawable(); + PeerInfo peer_info = gl_drawable.peer_info; + ByteBuffer peer_handle = peer_info.lockAndGetHandle(); + ByteBuffer window_handle = parented ? ((MacOSXCanvasPeerInfo)peer_info).window_handle : window; + try { - if (parent == null) { - frame = new MacOSXFrame(mode, requested_mode, fullscreen, x, y); - canvas = frame.getCanvas(); - } else { - frame = null; - canvas = parent; + + window = nCreateWindow(x, y, mode.getWidth(), mode.getHeight(), + fullscreen, isUndecorated(), resizable, + parented, peer_handle, window_handle); + + if (fullscreen) { + // when going to fullscreen viewport is set to screen size by Cocoa, ignore this value + skipViewportValue = true; + // if starting in fullscreen then set initial viewport to displaymode size + if (current_viewport.get(2) == 0 && current_viewport.get(3) == 0) { + current_viewport.put(2, mode.getWidth()); + current_viewport.put(3, mode.getHeight()); + } } - canvas_listener = new MacOSXCanvasListener(canvas); - robot = AWTUtil.createRobot(canvas); + + native_mode = nIsNativeMode(peer_handle); + + if (!native_mode) { + robot = AWTUtil.createRobot(canvas); + } + } catch (LWJGLException e) { destroyWindow(); throw e; + } finally { + peer_info.unlock(); } } - private void doHandleQuit() { + public void doHandleQuit() { synchronized (this) { close_requested = true; } } + + public void mouseInsideWindow(boolean inside) { + synchronized (this) { + mouseInsideWindow = inside; + } + updateNativeCursor = true; + } + + public native void nDestroyCALayer(ByteBuffer peer_info_handle); + + public native void nDestroyWindow(ByteBuffer window_handle); public void destroyWindow() { - if (canvas_listener != null) { - canvas_listener.disableListeners(); - canvas_listener = null; + + if (!native_mode) { + DrawableGL gl_drawable = (DrawableGL)Display.getDrawable(); + PeerInfo peer_info = gl_drawable.peer_info; + if (peer_info != null) { + ByteBuffer peer_handle = peer_info.getHandle(); + nDestroyCALayer(peer_handle); + } + robot = null; } - if (frame != null) { - AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { - if (MacOSXFrame.getDevice().getFullScreenWindow() == frame) - MacOSXFrame.getDevice().setFullScreenWindow(null); - return null; - } - }); - if (frame.isDisplayable()) - frame.dispose(); - frame = null; - } - hideUI(false); + + nDestroyWindow(window); } public int getGammaRampLength() { @@ -155,81 +211,79 @@ final class MacOSXDisplay implements DisplayImplementation { public String getVersion() { return null; } - - private static boolean equals(java.awt.DisplayMode awt_mode, DisplayMode mode) { - return awt_mode.getWidth() == mode.getWidth() && awt_mode.getHeight() == mode.getHeight() - && awt_mode.getBitDepth() == mode.getBitsPerPixel() && awt_mode.getRefreshRate() == mode.getFrequency(); + + private static boolean equals(DisplayMode mode1, DisplayMode mode2) { + return mode1.getWidth() == mode2.getWidth() && mode1.getHeight() == mode2.getHeight() + && mode1.getBitsPerPixel() == mode2.getBitsPerPixel() && mode1.getFrequency() == mode2.getFrequency(); } public void switchDisplayMode(DisplayMode mode) throws LWJGLException { - java.awt.DisplayMode[] awt_modes = MacOSXFrame.getDevice().getDisplayModes(); - for ( java.awt.DisplayMode awt_mode : awt_modes ) { - if (equals(awt_mode, mode)) { - requested_mode = awt_mode; + DisplayMode[] modes = getAvailableDisplayModes(); + + for (DisplayMode available_mode : modes) { + if (equals(available_mode, mode)) { + requested_mode = available_mode; return; } } + throw new LWJGLException(mode + " is not supported"); } public void resetDisplayMode() { - if (MacOSXFrame.getDevice().getFullScreenWindow() != null) - MacOSXFrame.getDevice().setFullScreenWindow(null); - requested_mode = null; + requested_mode = null; restoreGamma(); } private native void restoreGamma(); - private static DisplayMode createLWJGLDisplayMode(java.awt.DisplayMode awt_mode) { - int bit_depth; - int refresh_rate; - int awt_bit_depth = awt_mode.getBitDepth(); - int awt_refresh_rate = awt_mode.getRefreshRate(); - if (awt_bit_depth != java.awt.DisplayMode.BIT_DEPTH_MULTI) - bit_depth = awt_bit_depth; - else - bit_depth = 32; // Assume the best bit depth - if (awt_refresh_rate != java.awt.DisplayMode.REFRESH_RATE_UNKNOWN) - refresh_rate = awt_refresh_rate; - else - refresh_rate = 0; - return new DisplayMode(awt_mode.getWidth(), awt_mode.getHeight(), bit_depth, refresh_rate); + public Object createDisplayMode(int width, int height, int bitsPerPixel, int refreshRate) { + return new DisplayMode(width, height, bitsPerPixel, refreshRate); } - + public DisplayMode init() throws LWJGLException { - return createLWJGLDisplayMode(MacOSXFrame.getDevice().getDisplayMode()); + return (DisplayMode) nGetCurrentDisplayMode(); + } + + public void addDisplayMode(Object modesList, int width, int height, int bitsPerPixel, int refreshRate) { + List modes = (List) modesList; + DisplayMode displayMode = new DisplayMode(width, height, bitsPerPixel, refreshRate); + modes.add(displayMode); } public DisplayMode[] getAvailableDisplayModes() throws LWJGLException { - java.awt.DisplayMode[] awt_modes = MacOSXFrame.getDevice().getDisplayModes(); List modes = new ArrayList(); - for ( java.awt.DisplayMode awt_mode : awt_modes ) - if ( awt_mode.getBitDepth() >= 16 ) - modes.add(createLWJGLDisplayMode(awt_mode)); + nGetDisplayModes(modes); // will populate the above list return modes.toArray(new DisplayMode[modes.size()]); } + private native void nSetTitle(ByteBuffer window_handle, ByteBuffer title_buffer); + public void setTitle(String title) { - if (frame != null) - frame.setTitle(title); + ByteBuffer buffer = MemoryUtil.encodeUTF8(title); + nSetTitle(window, buffer); } public boolean isCloseRequested() { boolean result; synchronized (this) { - result = close_requested || (frame != null && frame.syncIsCloseRequested()); + result = close_requested; close_requested = false; } return result; } public boolean isVisible() { - return frame == null || frame.syncIsVisible(); + return true; } public boolean isActive() { - return canvas.isFocusOwner(); + if (native_mode) { + return nIsFocused(window); + } + else { + return Display.getParent().hasFocus(); + } } public Canvas getCanvas() { @@ -237,7 +291,7 @@ final class MacOSXDisplay implements DisplayImplementation { } public boolean isDirty() { - return frame != null && frame.getCanvas().syncIsDirty(); + return false; } public PeerInfo createPeerInfo(PixelFormat pixel_format, ContextAttribs attribs) throws LWJGLException { @@ -248,71 +302,32 @@ final class MacOSXDisplay implements DisplayImplementation { } } - private static final IntBuffer current_viewport = BufferUtils.createIntBuffer(16); public void update() { - boolean should_update = canvas_listener.syncShouldUpdateContext(); - /* - * Workaround for the "white screen in fullscreen mode" problem - * - * Sometimes, switching from windowed mode to fullscreen or simply creating the Display - * in fullscreen mode will result in the context not being bound to the window correctly. - * The program runs fine, but the canvas background (white) is shown instead of the context - * front buffer. - * - * I've discovered that re-binding the context with another setView() won't fix the problem, while a - * clearDrawable() followed by a setView() does work. A straightforward workaround would be - * to simply run the combination at every update(). This is not sufficient, since the clearDrawable() - * call makes the the background appear shortly, causing visual artifacts. - * What we really need is a way to detect that a setView() failed, and then do the magic combo. I've not - * been able to find such a detection so alternatively I'm triggering the combo if the display is fullscreen - * (I've not seen the white screen problem in windowed mode) and if the canvas has gotten a paint message or - * if its update flag was set. - * - * My testing seems to indicate that the workaround works, since I've not seen the problem after the fix. - * - * - elias - */ + boolean should_update = true; + DrawableGL drawable = (DrawableGL)Display.getDrawable(); - if (Display.isFullscreen() && (frame != null && frame.getCanvas().syncCanvasPainted() || should_update)) { - try { - MacOSXContextImplementation.resetView(drawable.peer_info, drawable.context); - } catch (LWJGLException e) { - LWJGLUtil.log("Failed to reset context: " + e); - } - } if (should_update) { drawable.context.update(); /* This is necessary to make sure the context won't "forget" about the view size */ - glGetInteger(GL_VIEWPORT, current_viewport); + if (skipViewportValue) skipViewportValue = false; + else glGetInteger(GL_VIEWPORT, current_viewport); glViewport(current_viewport.get(0), current_viewport.get(1), current_viewport.get(2), current_viewport.get(3)); } - if (frame != null && mouse_queue != null) { - if (frame.syncShouldReleaseCursor()) - MacOSXMouseEventQueue.nGrabMouse(false); - if (frame.syncShouldWarpCursor()) - mouse_queue.warpCursor(); + + if (native_mode && updateNativeCursor) { + updateNativeCursor = false; + try { + setNativeCursor(currentNativeCursor); + } catch (LWJGLException e) { + e.printStackTrace(); + } } } - /** - * This is an interface to the native Carbon call - * SetSystemUIMode. It is used to hide the dock in a way - * that will prevent AWT from shifting the fullscreen window - * - * The workaround is not necessary on 10.4, and since 10.4 shows - * instability problems calling SetSystemUIMode, we'll only call it - * when the OS version is 10.3 or lower. - */ - private void hideUI(boolean hide) { - if (!LWJGLUtil.isMacOSXEqualsOrBetterThan(10, 4)) - nHideUI(hide); - } - - private native void nHideUI(boolean hide); - public void reshape(int x, int y, int width, int height) { - if (frame != null) - frame.resize(x, y, width, height); + //if (native_mode) { + // nResizeWindow(window, x, y, width, height); + //} } /* Mouse */ @@ -325,42 +340,94 @@ final class MacOSXDisplay implements DisplayImplementation { } public void createMouse() throws LWJGLException { - this.mouse_queue = new MacOSXMouseEventQueue(canvas); - mouse_queue.register(); + if (native_mode) { + mouse = new MacOSXNativeMouse(this, window); + mouse.register(); + } + else { + this.mouse_queue = new MacOSXMouseEventQueue(canvas); + mouse_queue.register(); + } } public void destroyMouse() { - if (mouse_queue != null) { - MacOSXMouseEventQueue.nGrabMouse(false); - mouse_queue.unregister(); + if (native_mode) { + // restore default native cursor + try { + MacOSXNativeMouse.setCursor(0); + } catch (LWJGLException e) {}; + + // release any mouse grab + grabMouse(false); + + if (mouse != null) { + mouse.unregister(); + } + mouse = null; + } + else { + if (mouse_queue != null) { + MacOSXMouseEventQueue.nGrabMouse(false); + mouse_queue.unregister(); + } + this.mouse_queue = null; } - this.mouse_queue = null; } public void pollMouse(IntBuffer coord_buffer, ByteBuffer buttons_buffer) { - mouse_queue.poll(coord_buffer, buttons_buffer); + if (native_mode) { + mouse.poll(coord_buffer, buttons_buffer); + } + else { + mouse_queue.poll(coord_buffer, buttons_buffer); + } } public void readMouse(ByteBuffer buffer) { - mouse_queue.copyEvents(buffer); + if (native_mode) { + mouse.copyEvents(buffer); + } + else { + mouse_queue.copyEvents(buffer); + } } public void grabMouse(boolean grab) { - mouse_queue.setGrabbed(grab); + if (native_mode) { + mouse.setGrabbed(grab); + } + else { + mouse_queue.setGrabbed(grab); + } } public int getNativeCursorCapabilities() { + if (native_mode) { + return Cursor.CURSOR_ONE_BIT_TRANSPARENCY | Cursor.CURSOR_8_BIT_ALPHA | Cursor.CURSOR_ANIMATION; + } + return AWTUtil.getNativeCursorCapabilities(); } public void setCursorPosition(int x, int y) { - AWTUtil.setCursorPosition(canvas, robot, x, y); + if (native_mode) { + if (mouse != null) { + mouse.setCursorPosition(x, y); + } + } + //else { + //MacOSXMouseEventQueue.nWarpCursor(x, y); + //} } public void setNativeCursor(Object handle) throws LWJGLException { - Cursor awt_cursor = (Cursor)handle; - if (frame != null) - frame.setCursor(awt_cursor); + if (native_mode) { + currentNativeCursor = getCursorHandle(handle); + if (Display.isCreated()) { + if (mouseInsideWindow) MacOSXNativeMouse.setCursor(currentNativeCursor); + else MacOSXNativeMouse.setCursor(0); // restore default cursor if outside Display + } + } } public int getMinCursorSize() { @@ -373,61 +440,77 @@ final class MacOSXDisplay implements DisplayImplementation { /* Keyboard */ public void createKeyboard() throws LWJGLException { - this.keyboard_queue = new KeyboardEventQueue(canvas); - keyboard_queue.register(); + if (native_mode) { + this.keyboard = new MacOSXNativeKeyboard(window); + keyboard.register(); + } + else { + this.keyboard_queue = new KeyboardEventQueue(canvas); + keyboard_queue.register(); + } } public void destroyKeyboard() { - if (keyboard_queue != null) - keyboard_queue.unregister(); - this.keyboard_queue = null; + if (native_mode) { + if (keyboard != null) { + keyboard.unregister(); + } + keyboard = null; + } + else { + if (keyboard_queue != null) { + keyboard_queue.unregister(); + } + this.keyboard_queue = null; + } } public void pollKeyboard(ByteBuffer keyDownBuffer) { - keyboard_queue.poll(keyDownBuffer); + if (native_mode) { + keyboard.poll(keyDownBuffer); + } + else { + keyboard_queue.poll(keyDownBuffer); + } } public void readKeyboard(ByteBuffer buffer) { - keyboard_queue.copyEvents(buffer); - } - -/* public int isStateKeySet(int key) { - int awt_key; - switch (key) { - case Keyboard.KEY_CAPITAL: - awt_key = KeyEvent.VK_CAPS_LOCK; - break; - case Keyboard.KEY_NUMLOCK: - awt_key = KeyEvent.VK_NUM_LOCK; - break; - case Keyboard.KEY_SYSRQ: - awt_key = KeyEvent.VK_SCROLL_LOCK; - break; - default: - return Keyboard.STATE_UNKNOWN; + if (native_mode) { + keyboard.copyEvents(buffer); } - try { - boolean state = Toolkit.getDefaultToolkit().getLockingKeyState(awt_key); - return state ? Keyboard.STATE_ON : Keyboard.STATE_OFF; - } catch (Exception e) { - LWJGLUtil.log("Failed to query key state: " + e); - return Keyboard.STATE_UNKNOWN; + else { + keyboard_queue.copyEvents(buffer); } } -*/ + /** Native cursor handles */ public Object createCursor(int width, int height, int xHotspot, int yHotspot, int numImages, IntBuffer images, IntBuffer delays) throws LWJGLException { - return AWTUtil.createCursor(width, height, xHotspot, yHotspot, numImages, images, delays); + if (native_mode) { + long cursor = MacOSXNativeMouse.createCursor(width, height, xHotspot, yHotspot, numImages, images, delays); + return cursor; + } + else { + return AWTUtil.createCursor(width, height, xHotspot, yHotspot, numImages, images, delays); + } } public void destroyCursor(Object cursor_handle) { + long handle = getCursorHandle(cursor_handle); + + // reset current cursor if same + if (currentNativeCursor == handle) { + currentNativeCursor = 0; + } + + MacOSXNativeMouse.destroyCursor(handle); + } + + private static long getCursorHandle(Object cursor_handle) { + return cursor_handle != null ? (Long)cursor_handle : 0; } public int getPbufferCapabilities() { - if (LWJGLUtil.isMacOSXEqualsOrBetterThan(10, 3)) - return Pbuffer.PBUFFER_SUPPORTED; - else - return 0; + return Pbuffer.PBUFFER_SUPPORTED; } public boolean isBufferLost(PeerInfo handle) { @@ -465,7 +548,7 @@ final class MacOSXDisplay implements DisplayImplementation { * @return number of icons used. */ public int setIcon(ByteBuffer[] icons) { -/* int size = 0; + /*int size = 0; int biggest = -1; for (int i=0;i() { - public Object run() throws Exception { - getDevice().setFullScreenWindow(MacOSXFrame.this); - getDevice().setDisplayMode(requested_mode); - java.awt.DisplayMode real_mode = getDevice().getDisplayMode(); - /** For some strange reason, the display mode is sometimes silently capped even though the mode is reported as supported */ - if ( requested_mode.getWidth() != real_mode.getWidth() || requested_mode.getHeight() != real_mode.getHeight() ) { - getDevice().setFullScreenWindow(null); - if (isDisplayable()) - dispose(); - throw new LWJGLException("AWT capped mode: requested mode = " + requested_mode.getWidth() + "x" + requested_mode.getHeight() + - " but got " + real_mode.getWidth() + " " + real_mode.getHeight()); - } - return null; - } - }); - } catch (PrivilegedActionException e) { - throw new LWJGLException(e); - } - } - pack(); - resize(x, y, mode.getWidth(), mode.getHeight()); - setVisible(true); - requestFocus(); - canvas.requestFocus(); - updateBounds(); - } - - public void resize(int x, int y, int width, int height) { - Insets insets = getInsets(); - setBounds(x, y, width + insets.left + insets.right, height + insets.top + insets.bottom); - } - - public int getWidth() { - Insets insets = getInsets(); - return super.getWidth() - insets.left - insets.right; - } - - public int getHeight() { - Insets insets = getInsets(); - return super.getHeight() - insets.top - insets.bottom; - } - - public Rectangle syncGetBounds() { - synchronized ( this ) { - return bounds; - } - } - - public void componentShown(ComponentEvent e) { - } - - public void componentHidden(ComponentEvent e) { - } - - private void updateBounds() { - synchronized ( this ) { - bounds = getBounds(); - } - } - - public void componentResized(ComponentEvent e) { - updateBounds(); - } - - public void componentMoved(ComponentEvent e) { - updateBounds(); - } - - public static GraphicsDevice getDevice() { - GraphicsEnvironment g_env = GraphicsEnvironment.getLocalGraphicsEnvironment(); - GraphicsDevice device = g_env.getDefaultScreenDevice(); - return device; - } - - public void windowIconified(WindowEvent e) { - synchronized ( this ) { - minimized = true; - } - } - - public void windowDeiconified(WindowEvent e) { - synchronized ( this ) { - minimized = false; - } - } - - public void windowOpened(WindowEvent e) { - } - - public void windowClosed(WindowEvent e) { - } - - public void windowClosing(WindowEvent e) { - synchronized ( this ) { - close_requested = true; - } - } - - public void windowDeactivated(WindowEvent e) { - synchronized ( this ) { - active = false; - should_release_cursor = true; - should_warp_cursor = false; - } - } - - public void windowActivated(WindowEvent e) { - synchronized ( this ) { - active = true; - should_warp_cursor = true; - should_release_cursor = false; - } - } - - public boolean syncIsCloseRequested() { - boolean result; - synchronized ( this ) { - result = close_requested; - close_requested = false; - } - return result; - } - - public boolean syncIsVisible() { - synchronized ( this ) { - return !minimized; - } - } - - public boolean syncIsActive() { - synchronized ( this ) { - return active; - } - } - - public MacOSXGLCanvas getCanvas() { - return canvas; - } - - public boolean syncShouldReleaseCursor() { - boolean result; - synchronized ( this ) { - result = should_release_cursor; - should_release_cursor = false; - } - return result; - } - - public boolean syncShouldWarpCursor() { - boolean result; - synchronized ( this ) { - result = should_warp_cursor; - should_warp_cursor = false; - } - return result; - } -} diff --git a/src/java/org/lwjgl/opengl/MacOSXMouseEventQueue.java b/src/java/org/lwjgl/opengl/MacOSXMouseEventQueue.java index b232becd..3104c18c 100644 --- a/src/java/org/lwjgl/opengl/MacOSXMouseEventQueue.java +++ b/src/java/org/lwjgl/opengl/MacOSXMouseEventQueue.java @@ -96,13 +96,13 @@ final class MacOSXMouseEventQueue extends MouseEventQueue { // If we're going to warp the cursor position, we'll skip the next event to avoid bogus delta values skip_event = isGrabbed(); } -/* if (isGrabbed()) { + if (isGrabbed()) { Rectangle bounds = getComponent().getBounds(); Point location_on_screen = getComponent().getLocationOnScreen(); int x = location_on_screen.x + bounds.width/2; int y = location_on_screen.y + bounds.height/2; nWarpCursor(x, y); - }*/ + } } private static native void getMouseDeltas(IntBuffer delta_buffer); @@ -110,4 +110,4 @@ final class MacOSXMouseEventQueue extends MouseEventQueue { private static native void nWarpCursor(int x, int y); static native void nGrabMouse(boolean grab); -} +} \ No newline at end of file diff --git a/src/java/org/lwjgl/opengl/MacOSXNativeKeyboard.java b/src/java/org/lwjgl/opengl/MacOSXNativeKeyboard.java new file mode 100644 index 00000000..5c631e56 --- /dev/null +++ b/src/java/org/lwjgl/opengl/MacOSXNativeKeyboard.java @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.lwjgl.opengl; + +/** + * An AWT implementation of a LWJGL compatible Keyboard event queue. + * @author elias_naur + */ + +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.util.HashMap; +import java.awt.Component; +import java.nio.ByteBuffer; + +import org.lwjgl.input.Keyboard; + +final class MacOSXNativeKeyboard extends EventQueue { + private final byte[] key_states = new byte[Keyboard.KEYBOARD_SIZE]; + + /** Event scratch array */ + private final ByteBuffer event = ByteBuffer.allocate(Keyboard.EVENT_SIZE); + + private ByteBuffer window_handle; + + private boolean has_deferred_event; + private long deferred_nanos; + private int deferred_key_code; + private byte deferred_key_state; + private int deferred_character; + + private HashMap nativeToLwjglMap; + + MacOSXNativeKeyboard(ByteBuffer window_handle) { + super(Keyboard.EVENT_SIZE); + nativeToLwjglMap = new HashMap(); + initKeyboardMappings(); + this.window_handle = window_handle; + } + + private native void nRegisterKeyListener(ByteBuffer window_handle); + + private native void nUnregisterKeyListener(ByteBuffer window_handle); + + // These are from: + private void initKeyboardMappings() { + nativeToLwjglMap.put((Short)(short)0x1D, Keyboard.KEY_0); + nativeToLwjglMap.put((Short)(short)0x12, Keyboard.KEY_1); + nativeToLwjglMap.put((Short)(short)0x13, Keyboard.KEY_2); + nativeToLwjglMap.put((Short)(short)0x14, Keyboard.KEY_3); + nativeToLwjglMap.put((Short)(short)0x15, Keyboard.KEY_4); + nativeToLwjglMap.put((Short)(short)0x17, Keyboard.KEY_5); + nativeToLwjglMap.put((Short)(short)0x16, Keyboard.KEY_6); + nativeToLwjglMap.put((Short)(short)0x1A, Keyboard.KEY_7); + nativeToLwjglMap.put((Short)(short)0x1C, Keyboard.KEY_8); + nativeToLwjglMap.put((Short)(short)0x19, Keyboard.KEY_9); + nativeToLwjglMap.put((Short)(short)0x00, Keyboard.KEY_A); + nativeToLwjglMap.put((Short)(short)0x0B, Keyboard.KEY_B); + nativeToLwjglMap.put((Short)(short)0x08, Keyboard.KEY_C); + nativeToLwjglMap.put((Short)(short)0x02, Keyboard.KEY_D); + nativeToLwjglMap.put((Short)(short)0x0E, Keyboard.KEY_E); + nativeToLwjglMap.put((Short)(short)0x03, Keyboard.KEY_F); + nativeToLwjglMap.put((Short)(short)0x05, Keyboard.KEY_G); + nativeToLwjglMap.put((Short)(short)0x04, Keyboard.KEY_H); + nativeToLwjglMap.put((Short)(short)0x22, Keyboard.KEY_I); + nativeToLwjglMap.put((Short)(short)0x26, Keyboard.KEY_J); + nativeToLwjglMap.put((Short)(short)0x28, Keyboard.KEY_K); + nativeToLwjglMap.put((Short)(short)0x25, Keyboard.KEY_L); + nativeToLwjglMap.put((Short)(short)0x2E, Keyboard.KEY_M); + nativeToLwjglMap.put((Short)(short)0x2D, Keyboard.KEY_N); + nativeToLwjglMap.put((Short)(short)0x1F, Keyboard.KEY_O); + nativeToLwjglMap.put((Short)(short)0x23, Keyboard.KEY_P); + nativeToLwjglMap.put((Short)(short)0x0C, Keyboard.KEY_Q); + nativeToLwjglMap.put((Short)(short)0x0F, Keyboard.KEY_R); + nativeToLwjglMap.put((Short)(short)0x01, Keyboard.KEY_S); + nativeToLwjglMap.put((Short)(short)0x11, Keyboard.KEY_T); + nativeToLwjglMap.put((Short)(short)0x20, Keyboard.KEY_U); + nativeToLwjglMap.put((Short)(short)0x09, Keyboard.KEY_V); + nativeToLwjglMap.put((Short)(short)0x0D, Keyboard.KEY_W); + nativeToLwjglMap.put((Short)(short)0x07, Keyboard.KEY_X); + nativeToLwjglMap.put((Short)(short)0x10, Keyboard.KEY_Y); + nativeToLwjglMap.put((Short)(short)0x06, Keyboard.KEY_Z); + + nativeToLwjglMap.put((Short)(short)0x2A, Keyboard.KEY_BACKSLASH); + nativeToLwjglMap.put((Short)(short)0x2B, Keyboard.KEY_COMMA); + nativeToLwjglMap.put((Short)(short)0x18, Keyboard.KEY_EQUALS); + nativeToLwjglMap.put((Short)(short)0x21, Keyboard.KEY_LBRACKET); + nativeToLwjglMap.put((Short)(short)0x1B, Keyboard.KEY_MINUS); + nativeToLwjglMap.put((Short)(short)0x27, Keyboard.KEY_APOSTROPHE); + nativeToLwjglMap.put((Short)(short)0x1E, Keyboard.KEY_RBRACKET); + nativeToLwjglMap.put((Short)(short)0x29, Keyboard.KEY_SEMICOLON); + nativeToLwjglMap.put((Short)(short)0x2C, Keyboard.KEY_SLASH); + nativeToLwjglMap.put((Short)(short)0x2F, Keyboard.KEY_PERIOD); + nativeToLwjglMap.put((Short)(short)0x32, Keyboard.KEY_CIRCUMFLEX); + + nativeToLwjglMap.put((Short)(short)0x41, Keyboard.KEY_DECIMAL); + nativeToLwjglMap.put((Short)(short)0x43, Keyboard.KEY_MULTIPLY); + nativeToLwjglMap.put((Short)(short)0x45, Keyboard.KEY_ADD); + nativeToLwjglMap.put((Short)(short)0x47, Keyboard.KEY_CLEAR); + nativeToLwjglMap.put((Short)(short)0x4B, Keyboard.KEY_DIVIDE); + nativeToLwjglMap.put((Short)(short)0x4C, Keyboard.KEY_NUMPADENTER); + nativeToLwjglMap.put((Short)(short)0x4E, Keyboard.KEY_SUBTRACT); + nativeToLwjglMap.put((Short)(short)0x51, Keyboard.KEY_NUMPADEQUALS); + + nativeToLwjglMap.put((Short)(short)0x52, Keyboard.KEY_NUMPAD0); + nativeToLwjglMap.put((Short)(short)0x53, Keyboard.KEY_NUMPAD1); + nativeToLwjglMap.put((Short)(short)0x54, Keyboard.KEY_NUMPAD2); + nativeToLwjglMap.put((Short)(short)0x55, Keyboard.KEY_NUMPAD3); + nativeToLwjglMap.put((Short)(short)0x56, Keyboard.KEY_NUMPAD4); + nativeToLwjglMap.put((Short)(short)0x57, Keyboard.KEY_NUMPAD5); + nativeToLwjglMap.put((Short)(short)0x58, Keyboard.KEY_NUMPAD6); + nativeToLwjglMap.put((Short)(short)0x59, Keyboard.KEY_NUMPAD7); + nativeToLwjglMap.put((Short)(short)0x5B, Keyboard.KEY_NUMPAD8); + nativeToLwjglMap.put((Short)(short)0x5C, Keyboard.KEY_NUMPAD9); + + + nativeToLwjglMap.put((Short)(short)0x24, Keyboard.KEY_RETURN); + nativeToLwjglMap.put((Short)(short)0x30, Keyboard.KEY_TAB); + nativeToLwjglMap.put((Short)(short)0x31, Keyboard.KEY_SPACE); + nativeToLwjglMap.put((Short)(short)0x33, Keyboard.KEY_BACK); + nativeToLwjglMap.put((Short)(short)0x35, Keyboard.KEY_ESCAPE); + nativeToLwjglMap.put((Short)(short)0x36, Keyboard.KEY_RMETA); // not in Events.h - works on MBP + nativeToLwjglMap.put((Short)(short)0x37, Keyboard.KEY_LMETA); + nativeToLwjglMap.put((Short)(short)0x38, Keyboard.KEY_LSHIFT); + nativeToLwjglMap.put((Short)(short)0x39, Keyboard.KEY_CAPITAL); + nativeToLwjglMap.put((Short)(short)0x3A, Keyboard.KEY_LMENU); + nativeToLwjglMap.put((Short)(short)0x3B, Keyboard.KEY_LCONTROL); + nativeToLwjglMap.put((Short)(short)0x3C, Keyboard.KEY_RSHIFT); + nativeToLwjglMap.put((Short)(short)0x3D, Keyboard.KEY_RMENU); + nativeToLwjglMap.put((Short)(short)0x3E, Keyboard.KEY_RCONTROL); + + nativeToLwjglMap.put((Short)(short)0x3F, Keyboard.KEY_FUNCTION); + nativeToLwjglMap.put((Short)(short)0x77, Keyboard.KEY_END); + + nativeToLwjglMap.put((Short)(short)0x7A, Keyboard.KEY_F1); + nativeToLwjglMap.put((Short)(short)0x78, Keyboard.KEY_F2); + nativeToLwjglMap.put((Short)(short)0x63, Keyboard.KEY_F3); + nativeToLwjglMap.put((Short)(short)0x76, Keyboard.KEY_F4); + nativeToLwjglMap.put((Short)(short)0x60, Keyboard.KEY_F5); + nativeToLwjglMap.put((Short)(short)0x61, Keyboard.KEY_F6); + nativeToLwjglMap.put((Short)(short)0x62, Keyboard.KEY_F7); + nativeToLwjglMap.put((Short)(short)0x64, Keyboard.KEY_F8); + nativeToLwjglMap.put((Short)(short)0x65, Keyboard.KEY_F9); + nativeToLwjglMap.put((Short)(short)0x6D, Keyboard.KEY_F10); + nativeToLwjglMap.put((Short)(short)0x67, Keyboard.KEY_F11); + nativeToLwjglMap.put((Short)(short)0x6F, Keyboard.KEY_F12); + nativeToLwjglMap.put((Short)(short)0x69, Keyboard.KEY_F13); + nativeToLwjglMap.put((Short)(short)0x6B, Keyboard.KEY_F14); + nativeToLwjglMap.put((Short)(short)0x71, Keyboard.KEY_F15); + nativeToLwjglMap.put((Short)(short)0x6A, Keyboard.KEY_F16); + nativeToLwjglMap.put((Short)(short)0x40, Keyboard.KEY_F17); + nativeToLwjglMap.put((Short)(short)0x4F, Keyboard.KEY_F18); + nativeToLwjglMap.put((Short)(short)0x50, Keyboard.KEY_F19); + // nativeToLwjglMap.put((Short)(short)0x5A, Keyboard.KEY_F20); + + nativeToLwjglMap.put((Short)(short)0x75, Keyboard.KEY_DELETE); + nativeToLwjglMap.put((Short)(short)0x72, Keyboard.KEY_INSERT); // 'Help' in Events.h + nativeToLwjglMap.put((Short)(short)0x73, Keyboard.KEY_HOME); + // nativeToLwjglMap.put((Short)(short)0xA4, Keyboard.KEY_MUTE); + nativeToLwjglMap.put((Short)(short)0x79, Keyboard.KEY_NEXT); + nativeToLwjglMap.put((Short)(short)0x74, Keyboard.KEY_PRIOR); + // nativeToLwjglMap.put((Short)(short)0x49, Keyboard.KEY_VOLUMEDOWN); + // nativeToLwjglMap.put((Short)(short)0x48, Keyboard.KEY_VOLUMEUP); + nativeToLwjglMap.put((Short)(short)0x7B, Keyboard.KEY_LEFT); + nativeToLwjglMap.put((Short)(short)0x7C, Keyboard.KEY_RIGHT); + nativeToLwjglMap.put((Short)(short)0x7D, Keyboard.KEY_DOWN); + nativeToLwjglMap.put((Short)(short)0x7E, Keyboard.KEY_UP); + + nativeToLwjglMap.put((Short)(short)0x0A, Keyboard.KEY_SECTION); + + nativeToLwjglMap.put((Short)(short)0x6E, Keyboard.KEY_APPS); // not in Events.h + nativeToLwjglMap.put((Short)(short)0x129, Keyboard.KEY_COLON); // not in Events.h -- do we need it? + } + + public void register() { + nRegisterKeyListener(window_handle); + } + + public void unregister() { + nUnregisterKeyListener(window_handle); + } + + public void putKeyboardEvent(int key_code, byte state, int character, long nanos, boolean repeat) { + event.clear(); + event.putInt(key_code).put(state).putInt(character).putLong(nanos).put(repeat ? (byte)1 : (byte)0); + event.flip(); + putEvent(event); + } + + public synchronized void poll(ByteBuffer key_down_buffer) { + flushDeferredEvent(); + int old_position = key_down_buffer.position(); + key_down_buffer.put(key_states); + key_down_buffer.position(old_position); + } + + public synchronized void copyEvents(ByteBuffer dest) { + flushDeferredEvent(); + super.copyEvents(dest); + } + + private synchronized void handleKey(int key_code, byte state, int character, long nanos) { + if (character == KeyEvent.CHAR_UNDEFINED) + character = Keyboard.CHAR_NONE; + if (state == 1) { + boolean repeat = false; + if (has_deferred_event) { + if ((nanos == deferred_nanos && deferred_key_code == key_code)) { + has_deferred_event = false; + repeat = true; // Repeat event + } else + flushDeferredEvent(); + } + putKeyEvent(key_code, state, character, nanos, repeat); + } else { + flushDeferredEvent(); + has_deferred_event = true; + deferred_nanos = nanos; + deferred_key_code = key_code; + deferred_key_state = state; + deferred_character = character; + } + } + + private void flushDeferredEvent() { + if (has_deferred_event) { + putKeyEvent(deferred_key_code, deferred_key_state, deferred_character, deferred_nanos, false); + has_deferred_event = false; + } + } + + public void putKeyEvent(int key_code, byte state, int character, long nanos, boolean repeat) { + /* Ignore repeating presses */ + int mapped_code = getMappedKeyCode((short)key_code); + if (mapped_code < 0) { + System.out.println("Unrecognized keycode: " + key_code); + /* Unrecognized / unmapped code, do nothing */ + return; + } + if ( key_states[mapped_code] == state ) + repeat = true; + key_states[mapped_code] = state; + int key_int_char = character & 0xffff; + putKeyboardEvent(mapped_code, state, key_int_char, nanos, repeat); + } + + private int getMappedKeyCode(short key_code) { + if (nativeToLwjglMap.containsKey(key_code)) { + return nativeToLwjglMap.get(key_code); + } + return -1; + } + + public void keyPressed(int key_code, int character, long nanos) { + handleKey(key_code, (byte)1, character, nanos); + } + + public void keyReleased(int key_code, int character, long nanos) { + handleKey(key_code, (byte)0, character, nanos); + } + + public void keyTyped(KeyEvent e) { + } +} diff --git a/src/java/org/lwjgl/opengl/MacOSXNativeMouse.java b/src/java/org/lwjgl/opengl/MacOSXNativeMouse.java new file mode 100644 index 00000000..6b1af8c3 --- /dev/null +++ b/src/java/org/lwjgl/opengl/MacOSXNativeMouse.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.lwjgl.opengl; + +/** + * A Cocoa implementation of a LWJGL compatible Mouse. + * @author mojang + */ + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +import org.lwjgl.input.Mouse; +import org.lwjgl.LWJGLException; + +import java.lang.reflect.*; +import java.lang.Integer; +import java.lang.Long; + +import org.lwjgl.BufferUtils; + +final class MacOSXNativeMouse extends EventQueue { + private static final int WHEEL_SCALE = 120; + private static final int NUM_BUTTONS = 3; + + private ByteBuffer window_handle; + private MacOSXDisplay display; + + private boolean grabbed; + + /** The accumulated mouse deltas returned by poll() */ + private float accum_dx; + private float accum_dy; + private int accum_dz; + + /** The last mouse position */ + private float last_x; + private float last_y; + + /** Saved control key state for ctrl-click right button emulation */ + private boolean saved_control_state; + + private final ByteBuffer event = ByteBuffer.allocate(Mouse.EVENT_SIZE); + private IntBuffer delta_buffer = BufferUtils.createIntBuffer(2); + private int skip_event; + + private final byte[] buttons = new byte[NUM_BUTTONS]; + + MacOSXNativeMouse(MacOSXDisplay display, ByteBuffer window_handle) { + super(Mouse.EVENT_SIZE); + this.display = display; + this.window_handle = window_handle; + } + + private native void nSetCursorPosition(ByteBuffer window_handle, int x, int y); + + public static native void nGrabMouse(boolean grab); + + private native void nRegisterMouseListener(ByteBuffer window_handle); + + private native void nUnregisterMouseListener(ByteBuffer window_handle); + + private static native long nCreateCursor(int width, int height, int xHotspot, int yHotspot, int numImages, IntBuffer images, int images_offset, IntBuffer delays, int delays_offset) throws LWJGLException; + + private static native void nDestroyCursor(long cursor_handle); + + private static native void nSetCursor(long cursor_handle) throws LWJGLException; + + public synchronized void register() { + nRegisterMouseListener(window_handle); + } + + public static long createCursor(int width, int height, int xHotspot, int yHotspot, int numImages, IntBuffer images, IntBuffer delays) throws LWJGLException { + try { + return nCreateCursor(width, height, xHotspot, yHotspot, numImages, images, images.position(), delays, delays != null ? delays.position() : -1); + } catch (LWJGLException e) { + throw e; + } + } + + public static void destroyCursor(long cursor_handle) { + nDestroyCursor(cursor_handle); + } + + public static void setCursor(long cursor_handle) throws LWJGLException { + try { + nSetCursor(cursor_handle); + } catch (LWJGLException e) { + throw e; + } + } + + public synchronized void setCursorPosition(int x, int y) { + nSetCursorPosition(window_handle, x, y); + } + + public synchronized void unregister() { + nUnregisterMouseListener(window_handle); + } + + public synchronized void setGrabbed(boolean grabbed) { + this.grabbed = grabbed; + nGrabMouse(grabbed); + skip_event = 1; + accum_dx = accum_dy = 0; + } + + public synchronized boolean isGrabbed() { + return grabbed; + } + + protected void resetCursorToCenter() { + clearEvents(); + accum_dx = accum_dy = 0; + if (display != null) { + last_x = display.getWidth() / 2; + last_y = display.getHeight() / 2; + } + } + + private void putMouseEvent(byte button, byte state, int dz, long nanos) { + if (grabbed) + putMouseEventWithCoords(button, state, 0, 0, dz, nanos); + else + putMouseEventWithCoords(button, state, (int)last_x, (int)last_y, dz, nanos); + } + + protected void putMouseEventWithCoords(byte button, byte state, int coord1, int coord2, int dz, long nanos) { + event.clear(); + event.put(button).put(state).putInt(coord1).putInt(coord2).putInt(dz).putLong(nanos); + event.flip(); + putEvent(event); + } + + public synchronized void poll(IntBuffer coord_buffer, ByteBuffer buttons_buffer) { + if (grabbed) { + coord_buffer.put(0, (int)accum_dx); + coord_buffer.put(1, (int)accum_dy); + } else { + coord_buffer.put(0, (int)last_x); + coord_buffer.put(1, (int)last_y); + } + coord_buffer.put(2, accum_dz); + accum_dx = accum_dy = accum_dz = 0; + int old_position = buttons_buffer.position(); + buttons_buffer.put(buttons, 0, buttons.length); + buttons_buffer.position(old_position); + } + + private void setCursorPos(float x, float y, long nanos) { + if ( grabbed ) + return; + float dx = x - last_x; + float dy = y - last_y; + addDelta(dx, dy); + last_x = x; + last_y = y; + putMouseEventWithCoords((byte)-1, (byte)0, (int)x, (int)y, 0, nanos); + } + + protected void addDelta(float dx, float dy) { + accum_dx += dx; + accum_dy += -dy; + } + + public synchronized void setButton(int button, int state, long nanos) { + buttons[button] = (byte)state; + putMouseEvent((byte)button, (byte)state, 0, nanos); + } + + public synchronized void mouseMoved(float x, float y, float dx, float dy, float dz, long nanos) { + if (skip_event > 0) { + skip_event--; + if (skip_event == 0) { + last_x = x; + last_y = y; + } + return; + } + if (grabbed) { + if ( dx != 0 || dy != 0 ) { + putMouseEventWithCoords((byte)-1, (byte)0, (int)dx, (int)-dy, 0, nanos); + addDelta(dx, dy); + } + } else { + setCursorPos(x, y, nanos); + } + if ( dz != 0 ) { + int wheel_amount = (int)(dy * WHEEL_SCALE); + accum_dz += wheel_amount; + putMouseEvent((byte)-1, (byte)0, wheel_amount, nanos); + } + } +} diff --git a/src/java/org/lwjgl/opengl/MacOSXPeerInfo.java b/src/java/org/lwjgl/opengl/MacOSXPeerInfo.java index 74ba523c..17942a3b 100644 --- a/src/java/org/lwjgl/opengl/MacOSXPeerInfo.java +++ b/src/java/org/lwjgl/opengl/MacOSXPeerInfo.java @@ -45,9 +45,7 @@ import org.lwjgl.LWJGLUtil; abstract class MacOSXPeerInfo extends PeerInfo { MacOSXPeerInfo(PixelFormat pixel_format, ContextAttribs attribs, boolean use_display_bpp, boolean support_window, boolean support_pbuffer, boolean double_buffered) throws LWJGLException { super(createHandle()); - if (pixel_format.isFloatingPoint() && !LWJGLUtil.isMacOSXEqualsOrBetterThan(10, 4)) - throw new LWJGLException("Floating point pixel format requested, but it requires MacOS X 10.4 or newer"); - + boolean gl32 = attribs != null && attribs.getMajorVersion() == 3 && attribs.getMinorVersion() == 2 && attribs.isProfileCore(); if ( gl32 && !LWJGLUtil.isMacOSXEqualsOrBetterThan(10, 7) ) throw new LWJGLException("OpenGL 3.2 requested, but it requires MacOS X 10.7 or newer"); diff --git a/src/java/org/lwjgl/opengl/MouseEventQueue.java b/src/java/org/lwjgl/opengl/MouseEventQueue.java index fa537cd6..356b471f 100644 --- a/src/java/org/lwjgl/opengl/MouseEventQueue.java +++ b/src/java/org/lwjgl/opengl/MouseEventQueue.java @@ -81,15 +81,19 @@ class MouseEventQueue extends EventQueue implements MouseListener, MouseMotionLi public synchronized void register() { resetCursorToCenter(); - component.addMouseListener(this); - component.addMouseMotionListener(this); - component.addMouseWheelListener(this); + if (component != null) { + component.addMouseListener(this); + component.addMouseMotionListener(this); + component.addMouseWheelListener(this); + } } public synchronized void unregister() { - component.removeMouseListener(this); - component.removeMouseMotionListener(this); - component.removeMouseWheelListener(this); + if (component != null) { + component.removeMouseListener(this); + component.removeMouseMotionListener(this); + component.removeMouseWheelListener(this); + } } protected Component getComponent() { @@ -105,18 +109,23 @@ class MouseEventQueue extends EventQueue implements MouseListener, MouseMotionLi return grabbed; } - private int transformY(int y) { - return component.getHeight() - 1 - y; + protected int transformY(int y) { + if (component != null) { + return component.getHeight() - 1 - y; + } + return y; } protected void resetCursorToCenter() { clearEvents(); accum_dx = accum_dy = 0; - Point cursor_location = AWTUtil.getCursorPosition(component); - if (cursor_location != null) { - last_x = cursor_location.x; - last_y = cursor_location.y; - } + if (component != null) { + Point cursor_location = AWTUtil.getCursorPosition(component); + if (cursor_location != null) { + last_x = cursor_location.x; + last_y = cursor_location.y; + } + } } private void putMouseEvent(byte button, byte state, int dz, long nanos) { diff --git a/src/native/common/org_lwjgl_opengl_AWTSurfaceLock.c b/src/native/common/org_lwjgl_opengl_AWTSurfaceLock.c index d4e5f1c2..b31e03d1 100644 --- a/src/native/common/org_lwjgl_opengl_AWTSurfaceLock.c +++ b/src/native/common/org_lwjgl_opengl_AWTSurfaceLock.c @@ -38,7 +38,11 @@ */ #include +#ifdef __MACH__ +#include +#else #include +#endif #include "org_lwjgl_opengl_AWTSurfaceLock.h" #include "awt_tools.h" #include "common_tools.h" @@ -49,29 +53,27 @@ JNIEXPORT jobject JNICALL Java_org_lwjgl_opengl_AWTSurfaceLock_createHandle } JNIEXPORT jboolean JNICALL Java_org_lwjgl_opengl_AWTSurfaceLock_lockAndInitHandle - (JNIEnv *env, jclass clazz, jobject lock_buffer_handle, jobject canvas, jboolean allowCALayer) { + (JNIEnv *env, jclass clazz, jobject lock_buffer_handle, jobject canvas) { JAWT awt; JAWT_DrawingSurface* ds; JAWT_DrawingSurfaceInfo *dsi; AWTSurfaceLock *awt_lock = (AWTSurfaceLock *)(*env)->GetDirectBufferAddress(env, lock_buffer_handle); - jboolean result = JNI_FALSE; + jboolean result = JNI_FALSE; #ifdef __MACH__ - if (allowCALayer) { - //first try CALAYER - awt.version = JAWT_VERSION_1_4 | 0x80000000;//JAWT_MACOSX_USE_CALAYER; - result = JAWT_GetAWT(env, &awt); - } + // try get JAWT with JAWT_MACOSX_USE_CALAYER Opt In + awt.version = JAWT_VERSION_1_4 | 0x80000000;//JAWT_MACOSX_USE_CALAYER; + result = JAWT_GetAWT(env, &awt); #endif - + if (result == JNI_FALSE) { - // now try without CALAYER - awt.version = JAWT_VERSION_1_4; - if (JAWT_GetAWT(env, &awt) == JNI_FALSE) { - throwException(env, "Could not get the JAWT interface"); - return JNI_FALSE; - } + // now try without CALAYER + awt.version = JAWT_VERSION_1_4; + if (JAWT_GetAWT(env, &awt) == JNI_FALSE) { + throwException(env, "Could not get the JAWT interface"); + return JNI_FALSE; + } } ds = awt.GetDrawingSurface(env, canvas); diff --git a/src/native/macosx/context.h b/src/native/macosx/context.h index 188a0b32..91932a33 100644 --- a/src/native/macosx/context.h +++ b/src/native/macosx/context.h @@ -46,16 +46,98 @@ #include #include #include "common_tools.h" +#include + +@class NSOpenGLContext, NSOpenGLPixelFormat, MacOSXOpenGLView, MacOSXKeyableWindow; typedef struct { + MacOSXKeyableWindow *window; + + NSRect display_rect; + + MacOSXOpenGLView *view; + NSOpenGLContext *context; + + // Native objects for Java callbacks + jobject jdisplay; + jobject jmouse; + jobject jkeyboard; + + jboolean fullscreen; + jboolean undecorated; + jboolean resizable; + jboolean parented; + + jboolean resized; + +} MacOSXWindowInfo; + +@interface MacOSXOpenGLView : NSView +{ + @public + MacOSXWindowInfo* _parent; + + @private + NSOpenGLContext* _openGLContext; + NSOpenGLPixelFormat* _pixelFormat; + NSTrackingArea * _trackingArea; +} + ++ (NSOpenGLPixelFormat*)defaultPixelFormat; +- (BOOL)windowShouldClose:(id)sender; +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender; +- (id)initWithFrame:(NSRect)frameRect pixelFormat:(NSOpenGLPixelFormat*)format; +- (void)setOpenGLContext:(NSOpenGLContext*)context; +- (NSOpenGLContext*)openGLContext; +- (void)clearGLContext; +- (void)prepareOpenGL; +- (void)update; +- (void)lockFocus; +- (void)setPixelFormat:(NSOpenGLPixelFormat*)pixelFormat; +- (NSOpenGLPixelFormat*)pixelFormat; +- (void)setParent:(MacOSXWindowInfo*)parent; +- (BOOL)acceptsFirstResponder; +@end + +@interface GLLayer : CAOpenGLLayer { + @public + JAWT_MacOSXDrawingSurfaceInfo *macosx_dsi; + JAWT_Rectangle canvasBounds; + MacOSXWindowInfo *window_info; + bool setViewport; + + @private + CGLContextObj contextObject; + int fboID; + int imageRenderBufferID; + int depthRenderBufferID; + int fboWidth; + int fboHeight; +} + +- (void) attachLayer; +- (void) removeLayer; +- (void) blitFrameBuffer; + +- (int) getWidth; +- (int) getHeight; +@end + +typedef struct { + bool isCALayer; + bool isWindowed; + MacOSXWindowInfo *window_info; NSOpenGLPixelFormat *pixel_format; - bool window; - bool canDrawGL; - union { - NSView *nsview; - NSOpenGLPixelBuffer *pbuffer; - }; + NSOpenGLPixelBuffer *pbuffer; + NSView *parent; + GLLayer *glLayer; } MacOSXPeerInfo; +@interface MacOSXKeyableWindow : NSWindow ++ (void)createWindow; ++ (void)destroyWindow; +- (BOOL)canBecomeKeyWindow; +@end + NSOpenGLPixelFormat *choosePixelFormat(JNIEnv *env, jobject pixel_format, bool gl32, bool use_display_bpp, bool support_window, bool support_pbuffer, bool double_buffered); #endif diff --git a/src/native/macosx/context.m b/src/native/macosx/context.m index 510eb2d4..0240b2f7 100644 --- a/src/native/macosx/context.m +++ b/src/native/macosx/context.m @@ -91,7 +91,16 @@ NSOpenGLPixelFormat *choosePixelFormat(JNIEnv *env, jobject pixel_format, bool g int bpp; jclass cls_pixel_format = (*env)->GetObjectClass(env, pixel_format); if (use_display_bpp) - bpp = CGDisplayBitsPerPixel(kCGDirectMainDisplay); + { + CGDisplayModeRef mode = CGDisplayCopyDisplayMode(kCGDirectMainDisplay); + CFStringRef pixEnc = CGDisplayModeCopyPixelEncoding(mode); + if (CFStringCompare(pixEnc, CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) + bpp = 32; + else if(CFStringCompare(pixEnc, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) + bpp = 16; + else if(CFStringCompare(pixEnc, CFSTR(IO8BitIndexedPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) + bpp = 8; + } else bpp = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "bpp", "I")); diff --git a/src/native/macosx/org_lwjgl_opengl_Display.m b/src/native/macosx/org_lwjgl_opengl_Display.m index 96002d83..1c31d883 100644 --- a/src/native/macosx/org_lwjgl_opengl_Display.m +++ b/src/native/macosx/org_lwjgl_opengl_Display.m @@ -39,20 +39,667 @@ * @version $Revision$ */ +#import #import #import -#import #import #import -//#import "display.h" #import "common_tools.h" #import "org_lwjgl_opengl_MacOSXDisplay.h" #import "org_lwjgl_MacOSXSysImplementation.h" +#import "context.h" -#define WAIT_DELAY 100 +static NSOpenGLPixelFormat *default_format = nil; -JNIEXPORT jint JNICALL Java_org_lwjgl_DefaultSysImplementation_getJNIVersion - (JNIEnv *env, jobject ignored) { +static NSAutoreleasePool *pool; + +static MacOSXPeerInfo *peer_info; + +@implementation MacOSXKeyableWindow + ++ (void) createWindow { + MacOSXWindowInfo *window_info = peer_info->window_info; + + int width = window_info->display_rect.size.width; + int height = window_info->display_rect.size.height; + + NSRect view_rect = NSMakeRect(0.0, 0.0, width, height); + window_info->view = [[MacOSXOpenGLView alloc] initWithFrame:view_rect pixelFormat:peer_info->pixel_format]; + [window_info->view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + + // set nsapp delegate for catching app quit events + [NSApp setDelegate:window_info->view]; + + if (window_info->context != nil) { + [window_info->view setOpenGLContext:window_info->context]; + } + + if (!window_info->fullscreen) { + + if (window_info->parented) { + window_info->window = [peer_info->parent window]; + [peer_info->parent addSubview:window_info->view]; + } + else { + + int default_window_mask = NSBorderlessWindowMask; // undecorated + + if (!window_info->undecorated) { + default_window_mask = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask; + } + + if (window_info->resizable) { + default_window_mask |= NSResizableWindowMask; + } + + window_info->window = [[MacOSXKeyableWindow alloc] initWithContentRect:window_info->display_rect styleMask:default_window_mask backing:NSBackingStoreBuffered defer:NO]; + + [window_info->window setContentView:window_info->view]; + [window_info->window setContentView:window_info->view]; // call twice to fix issue + + // set NSView as delegate of NSWindow to get windowShouldClose events + [window_info->window setDelegate:window_info->view]; + } + + // disable any fixed backbuffer size to allow resizing + CGLContextObj cgcontext = (CGLContextObj)[[window_info->view openGLContext] CGLContextObj]; + CGLDisable(cgcontext, kCGLCESurfaceBackingSize); + } + else { + // set a fixed backbuffer size for fullscreen + CGLContextObj cgcontext = (CGLContextObj)[window_info->context CGLContextObj]; + GLint dim[2] = {width, height}; + CGLSetParameter(cgcontext, kCGLCPSurfaceBackingSize, dim); + CGLEnable(cgcontext, kCGLCESurfaceBackingSize); + + // enter fullscreen mode + [window_info->view enterFullScreenMode: [NSScreen mainScreen] withOptions: nil ]; + window_info->window = [window_info->view window]; + + // adjust the NSView bounds to correct mouse coordinates in fullscreen + NSSize windowSize = [window_info->window frame].size; + NSSize newBounds = NSMakeSize(windowSize.width/width*windowSize.width, windowSize.height/height*windowSize.height); + [window_info->view setBoundsSize:newBounds]; + } + + // Inform the view of its parent window info; + [window_info->view setParent:window_info]; + + [window_info->window makeFirstResponder:window_info->view]; + [window_info->window setInitialFirstResponder:window_info->view]; + [window_info->window makeKeyAndOrderFront:[NSApplication sharedApplication]]; +} + ++ (void) destroyWindow { + MacOSXWindowInfo *window_info = peer_info->window_info; + + if (window_info->fullscreen) { + [window_info->view exitFullScreenModeWithOptions: nil]; + window_info->window = nil; + } + else { + if (peer_info->isCALayer) { + [peer_info->glLayer removeLayer]; + } + + if (window_info->window != nil) { + // if the nsview has no parent then close window + if ([window_info->window contentView] == window_info->view) { + // release the nsview and remove it from any parent nsview + [window_info->view removeFromSuperviewWithoutNeedingDisplay]; + [window_info->window close]; + window_info->window = nil; + } + else { + // release the nsview and remove it from any parent nsview + [window_info->view removeFromSuperviewWithoutNeedingDisplay]; + } + } + } +} + +- (BOOL)canBecomeKeyWindow; +{ + return YES; +} +@end + +@implementation MacOSXOpenGLView + ++ (NSOpenGLPixelFormat*)defaultPixelFormat { + NSOpenGLPixelFormatAttribute defaultAttribs[] = { + NSOpenGLPFADoubleBuffer, + NSOpenGLPFADepthSize, 16, + NSOpenGLPFAColorSize, 32, + 0 + }; + if (default_format == nil) { + default_format = [[NSOpenGLPixelFormat alloc] initWithAttributes:defaultAttribs]; + } + return default_format; +} + +- (BOOL)windowShouldClose:(id)sender { + if (_parent != nil) { + JNIEnv *env = attachCurrentThread(); + jclass display_class = (*env)->GetObjectClass(env, _parent->jdisplay); + jmethodID close_callback = (*env)->GetMethodID(env, display_class, "doHandleQuit", "()V"); + (*env)->CallVoidMethod(env, _parent->jdisplay, close_callback); + } + return NO; +} + +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { + [self windowShouldClose:nil]; + return NSTerminateCancel; +} + +- (id)initWithFrame:(NSRect)frameRect pixelFormat:(NSOpenGLPixelFormat*)format { + self = [super initWithFrame:frameRect]; + if (self != nil) { + _pixelFormat = [format retain]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(_surfaceNeedsUpdate:) + name:NSViewGlobalFrameDidChangeNotification + object:self]; + } + return self; +} + +- (void) _surfaceNeedsUpdate:(NSNotification*)notification { + [self update]; +} + +- (void)setOpenGLContext:(NSOpenGLContext*)context { + _openGLContext = context; +} + +- (NSOpenGLContext*)openGLContext { + return _openGLContext; +} + +- (void)clearGLContext { + [_openGLContext release]; + _openGLContext = nil; +} + +- (void)prepareOpenGL { + +} + +- (void)update { + [_openGLContext update]; +} + +- (void)lockFocus { + [super lockFocus]; + + NSOpenGLContext* context = [self openGLContext]; + + if (context == nil) return; + + if ([context view] != self) { + [context setView:self]; + } + + [context makeCurrentContext]; +} + +- (void)setPixelFormat:(NSOpenGLPixelFormat*)pixelFormat { + _pixelFormat = [pixelFormat retain]; +} + +- (NSOpenGLPixelFormat*)pixelFormat { + return _pixelFormat; +} + +- (BOOL)acceptsFirstResponder { + return YES; +} + +- (void)setParent:(MacOSXWindowInfo*)parent { + _parent = parent; +} + +- (void)keyDown:(NSEvent *)event { + JNIEnv *env = attachCurrentThread(); + if (env == nil || event == nil || _parent == nil || _parent->jkeyboard == nil) { + return; + } + long time = [event timestamp] * 1000000000; + jclass keyboard_class = (*env)->GetObjectClass(env, _parent->jkeyboard); + jmethodID keydown = (*env)->GetMethodID(env, keyboard_class, "keyPressed", "(IIJ)V"); + const char* charbuf = [[event characters] cStringUsingEncoding:NSASCIIStringEncoding]; + int charcode = (charbuf == nil) ? 0 : charbuf[0]; + (*env)->CallVoidMethod(env, _parent->jkeyboard, keydown, [event keyCode], charcode, time); +} + +- (void)keyUp:(NSEvent *)event { + JNIEnv *env = attachCurrentThread(); + if (env == nil || event == nil || _parent == nil || _parent->jkeyboard == nil) { + return; + } + long time = [event timestamp] * 1000000000; + jclass keyboard_class = (*env)->GetObjectClass(env, _parent->jkeyboard); + jmethodID keyup = (*env)->GetMethodID(env, keyboard_class, "keyReleased", "(IIJ)V"); + const char* charbuf = [[event characters] cStringUsingEncoding:NSASCIIStringEncoding]; + int charcode = (charbuf == nil) ? 0 : charbuf[0]; + (*env)->CallVoidMethod(env, _parent->jkeyboard, keyup, [event keyCode], charcode, time); +} + +- (void)flagsChanged:(NSEvent *)event { + JNIEnv *env = attachCurrentThread(); + if (env == nil || event == nil || _parent == nil || _parent->jkeyboard == nil) { + return; + } + long time = [event timestamp] * 1000000000; + + NSUInteger mask = ~0; + switch([event keyCode]) { + case kVK_Control : mask = 0x0001; break; + case kVK_Shift : mask = 0x0002; break; + case kVK_RightShift : mask = 0x0004; break; + case kVK_Command : mask = 0x0008; break; + case 0x36 : mask = 0x0010; break; // Should be: kVK_RightCommand -- missing O.o + case kVK_Option : mask = 0x0020; break; + case kVK_RightOption : mask = 0x0040; break; + case kVK_RightControl: mask = 0x2000; break; + case kVK_CapsLock : mask = NSAlphaShiftKeyMask; break; + case kVK_Function : mask = NSFunctionKeyMask; break; + // case 0x?? : mask = NSNumericPadKeyMask; break; // Didn't have the keycode for this one :( + default: + NSLog(@"Unknown modifier with keycode: %d\n", [event keyCode]); + return; + } + + jclass keyboard_class = (*env)->GetObjectClass(env, _parent->jkeyboard); + + jmethodID keyMethod; + if (([event modifierFlags] & mask) == mask) { + keyMethod = (*env)->GetMethodID(env, keyboard_class, "keyPressed", "(IIJ)V"); + } else { + keyMethod = (*env)->GetMethodID(env, keyboard_class, "keyReleased", "(IIJ)V"); + } + + (*env)->CallVoidMethod(env, _parent->jkeyboard, keyMethod, [event keyCode], 0, time); +} + +- (void)mouseButtonState:(NSEvent *)event :(int)button :(int)state { + JNIEnv *env = attachCurrentThread(); + if (env == nil || event == nil || _parent == nil || _parent->jkeyboard == nil) { + return; + } + long time = [event timestamp] * 1000000000; + jclass mouse_class = (*env)->GetObjectClass(env, _parent->jmouse); + jmethodID mousebutton = (*env)->GetMethodID(env, mouse_class, "setButton", "(IIJ)V"); + (*env)->CallVoidMethod(env, _parent->jmouse, mousebutton, button, state, time); +} + +- (void)mouseDown:(NSEvent *)event { + [self mouseButtonState:event :0 :1]; +} + +- (void)rightMouseDown:(NSEvent *)event { + [self mouseButtonState:event :1 :1]; +} + +- (void)otherMouseDown:(NSEvent *)event { + [self mouseButtonState:event :2 :1]; +} + +- (void)mouseUp:(NSEvent *)event { + [self mouseButtonState:event :0 :0]; +} + +- (void)rightMouseUp:(NSEvent *)event { + [self mouseButtonState:event :1 :0]; +} + +- (void)otherMouseUp:(NSEvent *)event { + [self mouseButtonState:event :2 :0]; +} + +- (void)mouseDragged:(NSEvent *)event { + JNIEnv *env = attachCurrentThread(); + if (env == nil || event == nil || _parent == nil || _parent->jmouse == nil) { + return; + } + long time = [event timestamp] * 1000000000; + jclass mouse_class = (*env)->GetObjectClass(env, _parent->jmouse); + jmethodID mousemove = (*env)->GetMethodID(env, mouse_class, "mouseMoved", "(FFFFFJ)V"); + NSPoint loc = [self convertPoint:[event locationInWindow] toView:nil]; + (*env)->CallVoidMethod(env, _parent->jmouse, mousemove, loc.x, loc.y, [event deltaX], [event deltaY], 0.0f, time); +} + +- (void)rightMouseDragged:(NSEvent *)event { + JNIEnv *env = attachCurrentThread(); + if (env == nil || event == nil || _parent == nil || _parent->jmouse == nil) { + return; + } + long time = [event timestamp] * 1000000000; + jclass mouse_class = (*env)->GetObjectClass(env, _parent->jmouse); + jmethodID mousemove = (*env)->GetMethodID(env, mouse_class, "mouseMoved", "(FFFFFJ)V"); + NSPoint loc = [self convertPoint:[event locationInWindow] toView:nil]; + (*env)->CallVoidMethod(env, _parent->jmouse, mousemove, loc.x, loc.y, [event deltaX], [event deltaY], 0.0f, time); +} + +- (void)otherMouseDragged:(NSEvent *)event { + JNIEnv *env = attachCurrentThread(); + if (env == nil || event == nil || _parent == nil || _parent->jmouse == nil) { + return; + } + long time = [event timestamp] * 1000000000; + jclass mouse_class = (*env)->GetObjectClass(env, _parent->jmouse); + jmethodID mousemove = (*env)->GetMethodID(env, mouse_class, "mouseMoved", "(FFFFFJ)V"); + NSPoint loc = [self convertPoint:[event locationInWindow] toView:nil]; + (*env)->CallVoidMethod(env, _parent->jmouse, mousemove, loc.x, loc.y, [event deltaX], [event deltaY], 0.0f, time); +} + +- (void)mouseMoved:(NSEvent *)event { + JNIEnv *env = attachCurrentThread(); + if (env == nil || event == nil || _parent == nil || _parent->jmouse == nil) { + return; + } + long time = [event timestamp] * 1000000000; + jclass mouse_class = (*env)->GetObjectClass(env, _parent->jmouse); + jmethodID mousemove = (*env)->GetMethodID(env, mouse_class, "mouseMoved", "(FFFFFJ)V"); + NSPoint loc = [self convertPoint:[event locationInWindow] toView:nil]; + (*env)->CallVoidMethod(env, _parent->jmouse, mousemove, loc.x, loc.y, [event deltaX], [event deltaY], 0.0f, time); +} + +- (void)scrollWheel:(NSEvent *)event { + JNIEnv *env = attachCurrentThread(); + if (env == nil || event == nil || _parent == nil) { + return; + } + long time = [event timestamp] * 1000000000; + jclass mouse_class = (*env)->GetObjectClass(env, _parent->jmouse); + jmethodID mousemove = (*env)->GetMethodID(env, mouse_class, "mouseMoved", "(FFFFFJ)V"); + NSPoint loc = [self convertPoint:[event locationInWindow] toView:nil]; + (*env)->CallVoidMethod(env, _parent->jmouse, mousemove, loc.x, loc.y, [event deltaX], [event deltaY], 1.0f, time); +} + +- (void)viewDidMoveToWindow { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(windowResized:) + name:NSWindowDidResizeNotification + object:[self window]]; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [super dealloc]; +} + +- (void)windowResized:(NSNotification *)notification; +{ + if (_parent != nil) { + _parent->display_rect = [self frame]; + _parent->resized = JNI_TRUE; + } +} + +-(void)updateTrackingAreas { + if(_trackingArea != nil) { + [self removeTrackingArea:_trackingArea]; + [_trackingArea release]; + } + + int options = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways); + _trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] + options:options + owner:self + userInfo:nil]; + [self addTrackingArea:_trackingArea]; + + // since nstrackingarea's don't know if mouse is inside or outside on creation + // manually detect this and send a fake mouse entered/exited message + NSPoint mouseLocation = [[self window] mouseLocationOutsideOfEventStream]; + mouseLocation = [self convertPoint:mouseLocation fromView:nil]; + + if (NSPointInRect(mouseLocation, [self bounds])) { + [self mouseEntered:nil]; + } + else { + [self mouseExited:nil]; + } +} + +-(void)mouseEntered:(NSEvent *)event { + JNIEnv *env = attachCurrentThread(); + if (env == nil || _parent == nil || _parent->jdisplay == nil) { + return; + } + + jclass display_class = (*env)->GetObjectClass(env, _parent->jdisplay); + jmethodID mouseInsideWindow_callback = (*env)->GetMethodID(env, display_class, "mouseInsideWindow", "(Z)V"); + (*env)->CallVoidMethod(env, _parent->jdisplay, mouseInsideWindow_callback, JNI_TRUE); +} + +-(void)mouseExited:(NSEvent *)event { + JNIEnv *env = attachCurrentThread(); + if (env == nil || _parent == nil || _parent->jdisplay == nil) { + return; + } + + jclass display_class = (*env)->GetObjectClass(env, _parent->jdisplay); + jmethodID mouseInsideWindow_callback = (*env)->GetMethodID(env, display_class, "mouseInsideWindow", "(Z)V"); + (*env)->CallVoidMethod(env, _parent->jdisplay, mouseInsideWindow_callback, JNI_FALSE); +} + +- (void) drawRect:(NSRect)rect { + // set black as the default background color + // for the nsview to avoid white flash on fullscreen + [[NSColor blackColor] setFill]; + NSRectFill(rect); +} +@end + +JNIEXPORT jboolean JNICALL Java_org_lwjgl_opengl_MacOSXDisplay_nIsMiniaturized(JNIEnv *env, jobject this, jobject window_handle) { + MacOSXWindowInfo *window_info = (MacOSXWindowInfo *)(*env)->GetDirectBufferAddress(env, window_handle); + return (jboolean)[window_info->window isMiniaturized]; +} + +JNIEXPORT jboolean JNICALL Java_org_lwjgl_opengl_MacOSXDisplay_nIsFocused(JNIEnv *env, jobject this, jobject window_handle) { + MacOSXWindowInfo *window_info = (MacOSXWindowInfo *)(*env)->GetDirectBufferAddress(env, window_handle); + // Display is focused if nswindow is key window and nsview is first responder in that nswindow + return (jboolean)([[window_info->view window] isKeyWindow] && [[window_info->view window] firstResponder] == window_info->view); +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXDisplay_nResizeWindow(JNIEnv *env, jobject this, jobject window_handle, jint x, jint y, jint width, jint height) { + MacOSXWindowInfo *window_info = (MacOSXWindowInfo *)(*env)->GetDirectBufferAddress(env, window_handle); + window_info->display_rect = NSMakeRect(x, y, width, height); + [window_info->window setFrame:window_info->display_rect display:false]; + [window_info->view update]; +} + +JNIEXPORT jboolean JNICALL Java_org_lwjgl_opengl_MacOSXDisplay_nWasResized(JNIEnv *env, jobject this, jobject window_handle) { + MacOSXWindowInfo *window_info = (MacOSXWindowInfo *)(*env)->GetDirectBufferAddress(env, window_handle); + jboolean was_resized = window_info->resized; + window_info->resized = JNI_FALSE; + return was_resized; +} + +JNIEXPORT jint JNICALL Java_org_lwjgl_opengl_MacOSXDisplay_nGetWidth(JNIEnv *env, jobject this, jobject window_handle) { + MacOSXWindowInfo *window_info = (MacOSXWindowInfo *)(*env)->GetDirectBufferAddress(env, window_handle); + jint width = window_info->display_rect.size.width; + return width; +} + +JNIEXPORT jint JNICALL Java_org_lwjgl_opengl_MacOSXDisplay_nGetHeight(JNIEnv *env, jobject this, jobject window_handle) { + MacOSXWindowInfo *window_info = (MacOSXWindowInfo *)(*env)->GetDirectBufferAddress(env, window_handle); + jint height = window_info->display_rect.size.height; + return height; +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXDisplay_nSetResizable(JNIEnv *env, jobject this, jobject window_handle, jboolean resizable) { + MacOSXWindowInfo *window_info = (MacOSXWindowInfo *)(*env)->GetDirectBufferAddress(env, window_handle); + NSUInteger style_mask = [window_info->window styleMask]; + if (resizable == true) { + style_mask |= NSResizableWindowMask; + } else { + style_mask &= ~NSResizableWindowMask; + } + [window_info->window setStyleMask:style_mask]; +} + +JNIEXPORT jint JNICALL Java_org_lwjgl_opengl_MacOSXDisplay_nGetX(JNIEnv *env, jobject this, jobject window_handle) { + MacOSXWindowInfo *window_info = (MacOSXWindowInfo *)(*env)->GetDirectBufferAddress(env, window_handle); + jint x = [[window_info->view window] frame].origin.x; + return x; +} + +JNIEXPORT jint JNICALL Java_org_lwjgl_opengl_MacOSXDisplay_nGetY(JNIEnv *env, jobject this, jobject window_handle) { + MacOSXWindowInfo *window_info = (MacOSXWindowInfo *)(*env)->GetDirectBufferAddress(env, window_handle); + + NSRect screenRect = [[window_info->window screen] frame]; + NSRect winRect = [[window_info->view window] frame]; + + // get top corner of window frame, also flip coords so origin is in top left + jint y = screenRect.size.height - (winRect.origin.y + winRect.size.height) - 1; + return y; +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXDisplay_nSetTitle(JNIEnv *env, jobject this, jobject window_handle, jobject title_buffer) { + MacOSXWindowInfo *window_info = (MacOSXWindowInfo *)(*env)->GetDirectBufferAddress(env, window_handle); + const char *title_cstr = (const char *)(*env)->GetDirectBufferAddress(env, title_buffer); + NSString *title = [[NSString alloc] initWithUTF8String:title_cstr]; + [window_info->window setTitle:title]; +} + +JNIEXPORT jobject JNICALL Java_org_lwjgl_opengl_MacOSXDisplay_nCreateWindow(JNIEnv *env, jobject this, jint x, jint y, jint width, jint height, jboolean fullscreen, jboolean undecorated, jboolean resizable, jboolean parented, jobject peer_info_handle, jobject window_handle) { + + pool = [[NSAutoreleasePool alloc] init]; + + peer_info = (MacOSXPeerInfo *)(*env)->GetDirectBufferAddress(env, peer_info_handle); + + if (peer_info->isCALayer && !fullscreen) { + MacOSXWindowInfo *window_info = (MacOSXWindowInfo *)(*env)->GetDirectBufferAddress(env, window_handle); + window_info->fullscreen = fullscreen; + window_info->undecorated = undecorated; + window_info->parented = parented; + + return window_handle; + } + + if (window_handle == NULL) { + window_handle = newJavaManagedByteBuffer(env, sizeof(MacOSXWindowInfo)); + if (window_handle == NULL) { + throwException(env, "Could not create handle buffer"); + return NULL; + } + } + + MacOSXWindowInfo *window_info = (MacOSXWindowInfo *)(*env)->GetDirectBufferAddress(env, window_handle); + + window_info->fullscreen = fullscreen; + window_info->undecorated = undecorated; + window_info->resizable = resizable; + window_info->parented = parented; + + peer_info->window_info = window_info; + peer_info->isWindowed = true; + + window_info->display_rect = NSMakeRect(x, y, width, height); + + // Cache the necessary info for window-close callbacks into the JVM + if (window_info->jdisplay == NULL) { + window_info->jdisplay = (*env)->NewGlobalRef(env, this); + } + + // create window on main thread + [MacOSXKeyableWindow performSelectorOnMainThread:@selector(createWindow) withObject:nil waitUntilDone:YES]; + + return window_handle; +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXDisplay_nDestroyWindow(JNIEnv *env, jobject this, jobject window_handle) { + + // destroy window on main thread + [MacOSXKeyableWindow performSelectorOnMainThread:@selector(destroyWindow) withObject:nil waitUntilDone:YES]; + + [pool drain]; +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXDisplay_nDestroyCALayer(JNIEnv *env, jobject this, jobject peer_info_handle) { + MacOSXPeerInfo *peer_info = (MacOSXPeerInfo *)(*env)->GetDirectBufferAddress(env, peer_info_handle); + if (peer_info->isCALayer) { + peer_info->isCALayer = false; + [peer_info->glLayer performSelectorOnMainThread:@selector(removeLayer) withObject:nil waitUntilDone:YES]; + [peer_info->glLayer release]; + } +} + +JNIEXPORT jboolean JNICALL Java_org_lwjgl_opengl_MacOSXDisplay_nIsNativeMode(JNIEnv *env, jobject this, jobject peer_info_handle) { + MacOSXPeerInfo *peer_info = (MacOSXPeerInfo *)(*env)->GetDirectBufferAddress(env, peer_info_handle); + if (peer_info->isCALayer) { + return JNI_FALSE; + } + else { + return JNI_TRUE; + } +} + +JNIEXPORT jobject JNICALL Java_org_lwjgl_opengl_MacOSXDisplay_nGetCurrentDisplayMode(JNIEnv *env, jobject this) { + + jclass displayClass = (*env)->GetObjectClass(env, this); + jmethodID createDisplayModeMethod = (*env)->GetMethodID(env, displayClass, "createDisplayMode", "(IIII)Ljava/lang/Object;"); + + CGDisplayModeRef mode = CGDisplayCopyDisplayMode(kCGDirectMainDisplay); + + int width = (int) CGDisplayModeGetWidth(mode); + int height = (int) CGDisplayModeGetHeight(mode); + int refreshRate = (int)CGDisplayModeGetRefreshRate(mode); + int bitsPerPixel; + + // get bitsPerPixel + CFStringRef pixelEncoding = CGDisplayModeCopyPixelEncoding(mode); + + if(CFStringCompare(pixelEncoding, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { + bitsPerPixel = 16; + } + else { + bitsPerPixel = 32; + } + + jobject displayMode = (*env)->CallObjectMethod(env, this, createDisplayModeMethod, width, height, bitsPerPixel, refreshRate); + + return displayMode; +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXDisplay_nGetDisplayModes(JNIEnv *env, jobject this, jobject modesList) { + CFArrayRef modes = CGDisplayCopyAllDisplayModes(kCGDirectMainDisplay, NULL); + + jclass displayClass = (*env)->GetObjectClass(env, this); + jmethodID addDisplayModeMethod = (*env)->GetMethodID(env, displayClass, "addDisplayMode", "(Ljava/lang/Object;IIII)V"); + + int i = 0; + + for (i = 0; i < CFArrayGetCount(modes); i++) { + CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i); + + int width = (int) CGDisplayModeGetWidth(mode); + int height = (int) CGDisplayModeGetHeight(mode); + int refreshRate = (int)CGDisplayModeGetRefreshRate(mode); + int bitsPerPixel; + + // get bitsPerPixel + CFStringRef pixelEncoding = CGDisplayModeCopyPixelEncoding(mode); + if(CFStringCompare(pixelEncoding, CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { + bitsPerPixel = 32; + } + else if(CFStringCompare(pixelEncoding, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { + bitsPerPixel = 16; + } + else { + continue; // ignore DisplayMode of other bitsPerPixel rates + } + + (*env)->CallVoidMethod(env, this, addDisplayModeMethod, modesList, width, height, bitsPerPixel, refreshRate); + } +} + +JNIEXPORT jint JNICALL Java_org_lwjgl_DefaultSysImplementation_getJNIVersion(JNIEnv *env, jobject ignored) { return org_lwjgl_MacOSXSysImplementation_JNI_VERSION; } @@ -62,17 +709,9 @@ JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXDisplay_restoreGamma(JNIEnv * JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXDisplay_setGammaRamp(JNIEnv *env, jobject this, jobject gamma_buffer) { const CGGammaValue *values = (*env)->GetDirectBufferAddress(env, gamma_buffer); - CGTableCount table_size = (*env)->GetDirectBufferCapacity(env, gamma_buffer); + uint32_t table_size = (*env)->GetDirectBufferCapacity(env, gamma_buffer); CGDisplayErr err = CGSetDisplayTransferByTable(kCGDirectMainDisplay, table_size, values, values, values); if (err != CGDisplayNoErr) { throwException(env, "Could not set display gamma"); } -} - -JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXDisplay_nHideUI(JNIEnv *env, jobject this, jboolean hide) { - if (hide == JNI_TRUE) { - SetSystemUIMode(kUIModeContentSuppressed, 0); - } else { - SetSystemUIMode(kUIModeNormal, 0); - } -} +} \ No newline at end of file diff --git a/src/native/macosx/org_lwjgl_input_Mouse.c b/src/native/macosx/org_lwjgl_opengl_MacOSXAWTMouse.m similarity index 99% rename from src/native/macosx/org_lwjgl_input_Mouse.c rename to src/native/macosx/org_lwjgl_opengl_MacOSXAWTMouse.m index a9840e6c..16122a82 100644 --- a/src/native/macosx/org_lwjgl_input_Mouse.c +++ b/src/native/macosx/org_lwjgl_opengl_MacOSXAWTMouse.m @@ -29,7 +29,7 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - + /** * $Id$ * @@ -70,4 +70,4 @@ JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXMouseEventQueue_getMouseDelta jint *buffer = (*env)->GetDirectBufferAddress(env, delta_buffer); buffer[0] = dx; buffer[1] = dy; -} +} \ No newline at end of file diff --git a/src/native/macosx/org_lwjgl_opengl_MacOSXCanvasPeerInfo.m b/src/native/macosx/org_lwjgl_opengl_MacOSXCanvasPeerInfo.m index 1593fb0d..b217509c 100644 --- a/src/native/macosx/org_lwjgl_opengl_MacOSXCanvasPeerInfo.m +++ b/src/native/macosx/org_lwjgl_opengl_MacOSXCanvasPeerInfo.m @@ -39,8 +39,6 @@ */ #import -#import - #include #include #include "awt_tools.h" @@ -48,211 +46,232 @@ #include "context.h" #include "common_tools.h" -@interface AttachLayerOnMainThread : NSObject { - MacOSXPeerInfo *peer_info; - JAWT_MacOSXDrawingSurfaceInfo *macosx_dsi; -} - -- (void) attachLayer; - -- (MacOSXPeerInfo*) peer_info; -- (JAWT_MacOSXDrawingSurfaceInfo) macosx_dsi; - -- (void) setPeer_info: (MacOSXPeerInfo*)input; -- (void) setMacosx_dsi: (JAWT_MacOSXDrawingSurfaceInfo*)input; - -@end - -JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXCanvasPeerInfo_nInitHandle -(JNIEnv *env, jclass clazz, jobject lock_buffer_handle, jobject peer_info_handle, jboolean allowCALayer) { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; +JNIEXPORT jobject JNICALL Java_org_lwjgl_opengl_MacOSXCanvasPeerInfo_nInitHandle +(JNIEnv *env, jclass clazz, jobject lock_buffer_handle, jobject peer_info_handle, jobject window_handle, jboolean forceCALayer) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; MacOSXPeerInfo *peer_info = (MacOSXPeerInfo *)(*env)->GetDirectBufferAddress(env, peer_info_handle); AWTSurfaceLock *surface = (AWTSurfaceLock *)(*env)->GetDirectBufferAddress(env, lock_buffer_handle); JAWT_MacOSXDrawingSurfaceInfo *macosx_dsi = (JAWT_MacOSXDrawingSurfaceInfo *)surface->dsi->platformInfo; - - if (allowCALayer) { - // check for CALayer support - if(surface->awt.version & 0x80000000) { //JAWT_MACOSX_USE_CALAYER) { - jint width = surface->dsi->bounds.width; - jint height = surface->dsi->bounds.height; + + // force CALayer usage or check if CALayer is supported (i.e. on Java 5 and Java 6) + if(forceCALayer || (surface->awt.version & 0x80000000)) { //JAWT_MACOSX_USE_CALAYER) { + + if (macosx_dsi != NULL) { - if(peer_info->pbuffer == NULL || peer_info->window || width != [peer_info->pbuffer pixelsWide] || height != [peer_info->pbuffer pixelsHigh]) { - if(peer_info->pbuffer != NULL) { - [peer_info->pbuffer release]; + if (window_handle == NULL) { + window_handle = newJavaManagedByteBuffer(env, sizeof(MacOSXWindowInfo)); + if (window_handle == NULL) { + throwException(env, "Could not create handle buffer"); } - - // make pbuffer - NSOpenGLPixelBuffer *pbuffer = nil; - NSLog(@"Make pbuffer: %d x %d", width, height); - pbuffer = [[NSOpenGLPixelBuffer alloc] initWithTextureTarget:GL_TEXTURE_RECTANGLE_EXT - textureInternalFormat:GL_RGBA - textureMaxMipMapLevel:0 - pixelsWide:width - pixelsHigh:height]; - - peer_info->pbuffer = pbuffer; - peer_info->window = false; - peer_info->canDrawGL = true; + } else if (peer_info->window_info->window != nil) { + return window_handle; } - - if (macosx_dsi != NULL) { - - AttachLayerOnMainThread *attachLayerOnMainThread = [[AttachLayerOnMainThread new] autorelease]; - attachLayerOnMainThread.peer_info = peer_info; - attachLayerOnMainThread.macosx_dsi = macosx_dsi; - - [JNFRunLoop performOnMainThread:@selector(attachLayer) - on:attachLayerOnMainThread - withObject:nil - waitUntilDone:YES]; + + if (peer_info->isCALayer) { + [peer_info->glLayer release]; } + peer_info->glLayer = [GLLayer new]; + + peer_info->glLayer->macosx_dsi = macosx_dsi; + peer_info->glLayer->canvasBounds = (JAWT_Rectangle)surface->dsi->bounds; + peer_info->window_info = (MacOSXWindowInfo *)(*env)->GetDirectBufferAddress(env, window_handle); + peer_info->glLayer->window_info = peer_info->window_info; + + [peer_info->glLayer performSelectorOnMainThread:@selector(createWindow:) withObject:peer_info->pixel_format waitUntilDone:YES]; + + peer_info->isCALayer = true; + peer_info->isWindowed = true; + peer_info->parent = nil; + [pool release]; - return; + return window_handle; } } - peer_info->nsview = macosx_dsi->cocoaViewRef; - peer_info->window = true; - + // no CALayer support, fallback to using legacy method of getting the NSView of an AWT Canvas + peer_info->parent = macosx_dsi->cocoaViewRef; + peer_info->isCALayer = false; + peer_info->isWindowed = true; + [pool release]; + return NULL; } -@interface PBufferGLLayer : NSOpenGLLayer { - MacOSXPeerInfo *peer_info; - GLuint textureID; -} - -- (MacOSXPeerInfo*) peer_info; -- (GLuint) textureID; - -- (void) setPeer_info: (MacOSXPeerInfo*)input; -- (void) setTextureID: (GLuint)input; - -@end - -// Object class to CALayer on AppKit Thread -@implementation AttachLayerOnMainThread +@implementation GLLayer - (void) attachLayer { - // attach the "root layer" to the AWT Canvas surface layers - id surfaceLayers = (id )macosx_dsi;//dsi->platformInfo; - if(surfaceLayers.layer == NULL) { - PBufferGLLayer *caGLLayer = [[PBufferGLLayer new] autorelease]; - caGLLayer.peer_info = peer_info; - caGLLayer.asynchronous = YES; - caGLLayer.needsDisplayOnBoundsChange = YES; - caGLLayer.opaque = YES; - surfaceLayers.layer = caGLLayer; + self.asynchronous = YES; + self.needsDisplayOnBoundsChange = YES; + self.opaque = NO; + self.contentsGravity = kCAGravityTopLeft; + self.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable; + + // get root layer of the AWT Canvas and add self to it + id surfaceLayers = (id )macosx_dsi; + surfaceLayers.layer = self; +} + +- (void) removeLayer { + // finish any pending blits before destroying the offscreen window to prevent crashes + glFinish(); + + // remove self from root layer + id surfaceLayers = (id )macosx_dsi; + surfaceLayers.layer = nil; +} + +- (int) getWidth { + return canvasBounds.width; +} + +- (int) getHeight { + return canvasBounds.height; +} + +- (void) createWindow:(NSOpenGLPixelFormat*)pixel_format { + if (window_info->window != nil) { + [window_info->window close]; } + + window_info->display_rect = [[NSScreen mainScreen] frame]; + + window_info->view = [[MacOSXOpenGLView alloc] initWithFrame:window_info->display_rect pixelFormat:pixel_format]; + + window_info->window = [[MacOSXKeyableWindow alloc] initWithContentRect:window_info->display_rect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; + [window_info->window setContentView:window_info->view]; + + [window_info->window orderOut:nil]; } -- (MacOSXPeerInfo*) peer_info { - return peer_info; +- (void) blitFrameBuffer { + + // get the size of the CALayer/AWT Canvas + int width = self.bounds.size.width; + int height = self.bounds.size.height; + + if (width != fboWidth || height != fboHeight) { + + // store current fbo/renderbuffers for later deletion + int oldFboID = fboID; + int oldImageRenderBufferID = imageRenderBufferID; + int oldDepthRenderBufferID = depthRenderBufferID; + + // create new fbo + int tempFBO; + glGenFramebuffersEXT(1, &tempFBO); + + // create new render buffers + glGenRenderbuffersEXT(1, &imageRenderBufferID); + glGenRenderbuffersEXT(1, &depthRenderBufferID); + + // switch to new fbo to attach render buffers + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, tempFBO); + + // initialize and attach image render buffer + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, imageRenderBufferID); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGB, width, height); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, imageRenderBufferID); + + // initialize and attach depth render buffer + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthRenderBufferID); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, width, height); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthRenderBufferID); + + // clear garbage background on new fbo + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + // blit frameBuffer to the new fbo + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0); + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, tempFBO); + glBlitFramebufferEXT(0, 0, width, height, + 0, 0, width, height, + GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, + GL_NEAREST); + + glFinish(); // finish before using new fbo and resizing the window + + // set new fbo and its sizes + fboID = tempFBO; + fboWidth = width; + fboHeight = height; + + // set the size of the offscreen frame buffer window + window_info->display_rect = NSMakeRect(0, 0, width, height); + //[window_info->window setFrame:window_info->display_rect display:false]; + + // clean up the old fbo and renderBuffers + glDeleteFramebuffersEXT(1, &oldFboID); + glDeleteRenderbuffersEXT(1, &oldImageRenderBufferID); + glDeleteRenderbuffersEXT(1, &oldDepthRenderBufferID); + } + else { + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0); + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, fboID); + + glBlitFramebufferEXT(0, 0, width, height, + 0, 0, width, height, + GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, + GL_NEAREST); + } + + // restore default framebuffer + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); } -- (JAWT_MacOSXDrawingSurfaceInfo*) macosx_dsi { - return macosx_dsi; -} - -- (void) setPeer_info: (MacOSXPeerInfo*)input { - peer_info = input; -} - -- (void) setMacosx_dsi: (JAWT_MacOSXDrawingSurfaceInfo*)input { - macosx_dsi = input; -} - -@end - -// rotates a red square when asked to draw -@implementation PBufferGLLayer - -// override to draw custom GL content -(void)drawInCGLContext:(CGLContextObj)glContext - pixelFormat:(CGLPixelFormatObj)pixelFormat - forLayerTime:(CFTimeInterval)timeInterval - displayTime:(const CVTimeStamp *)timeStamp { + pixelFormat:(CGLPixelFormatObj)pixelFormat + forLayerTime:(CFTimeInterval)timeInterval + displayTime:(const CVTimeStamp *)timeStamp { - if(!peer_info || !peer_info->pbuffer) { - return; - } - - peer_info->canDrawGL = false; - - NSOpenGLPixelBuffer *pbuffer = self.peer_info->pbuffer; - - // set the current context - CGLSetCurrentContext(glContext); + // set the current context + CGLSetCurrentContext(glContext); - GLsizei width = [pbuffer pixelsWide]; - GLsizei height = [pbuffer pixelsHigh]; + // get the size of the CALayer/AWT Canvas + int width = self.bounds.size.width; + int height = self.bounds.size.height; - if(textureID == 0) { - glGenTextures(1, &textureID); - } - glBindTexture(GL_TEXTURE_RECTANGLE_EXT, self.textureID); - CGLTexImagePBuffer(glContext,[pbuffer CGLPBufferObj], GL_FRONT); + if (width != fboWidth || height != fboHeight) { + // clear garbage background before lwjgl fbo blit + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + } - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + // read the LWJGL FBO and blit it into this CALayers FBO + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, fboID); + glBlitFramebufferEXT(0, 0, width, height, + 0, height - fboHeight, width, height, + GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, + GL_NEAREST); - glEnable(GL_TEXTURE_RECTANGLE_EXT); - - static GLfloat verts[] = { - -1.0, -1.0, - -1.0, 1.0, - 1.0, 1.0, - 1.0, -1.0 - }; - - GLfloat tex[] = { - 0.0, 0.0, - 0.0, height, - width, height, - width, 0.0 - }; - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glVertexPointer(2, GL_FLOAT, 0, verts); - glTexCoordPointer(2, GL_FLOAT, 0, tex); - - glDrawArrays(GL_QUADS, 0, 4); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - - glDisable(GL_TEXTURE_RECTANGLE_EXT); - - // call super to finalize the drawing - by default all it does is call glFlush() - [super drawInCGLContext:glContext pixelFormat:pixelFormat forLayerTime:timeInterval displayTime:timeStamp]; + // call super to finalize the drawing - by default all it does is call glFlush() + [super drawInCGLContext:glContext pixelFormat:pixelFormat forLayerTime:timeInterval displayTime:timeStamp]; } -(BOOL)canDrawInCGLContext:(CGLContextObj)glContext - pixelFormat:(CGLPixelFormatObj)pixelFormat - forLayerTime:(CFTimeInterval)timeInterval - displayTime:(const CVTimeStamp *)timeStamp { - return (peer_info->canDrawGL && !peer_info->window) ? YES : NO; + pixelFormat:(CGLPixelFormatObj)pixelFormat + forLayerTime:(CFTimeInterval)timeInterval + displayTime:(const CVTimeStamp *)timeStamp { + return YES; } -- (MacOSXPeerInfo*) peer_info { - return peer_info; +- (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat { + CGLCreateContext(pixelFormat, [window_info->context CGLContextObj], &contextObject); + return contextObject; } -- (GLuint) textureID { - return textureID; +- (void)releaseCGLContext:(CGLContextObj)glContext { + CGLDestroyContext(contextObject); } -- (void) setPeer_info: (MacOSXPeerInfo*)input { - peer_info = input; +- (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask { + return CGLGetPixelFormat([window_info->context CGLContextObj]); } -- (void) setTextureID: (GLuint)input { - textureID = input; +- (void)releaseCGLPixelFormat:(CGLPixelFormatObj)pixelFormat { + } @end \ No newline at end of file diff --git a/src/native/macosx/org_lwjgl_opengl_MacOSXContextImplementation.m b/src/native/macosx/org_lwjgl_opengl_MacOSXContextImplementation.m index aecebe1c..2b2f02b7 100644 --- a/src/native/macosx/org_lwjgl_opengl_MacOSXContextImplementation.m +++ b/src/native/macosx/org_lwjgl_opengl_MacOSXContextImplementation.m @@ -47,7 +47,7 @@ #import "common_tools.h" typedef struct { - NSOpenGLContext *context; + NSOpenGLContext *context; MacOSXPeerInfo *peer_info; } MacOSXContext; @@ -57,8 +57,9 @@ JNIEXPORT jobject JNICALL Java_org_lwjgl_opengl_MacOSXContextImplementation_nCre MacOSXPeerInfo *peer_info; MacOSXContext *shared_context_info; MacOSXContext *context_info; - NSOpenGLContext *context; - NSOpenGLContext *shared_context = NULL; + NSOpenGLContext *context; + NSOpenGLContext *shared_context = NULL; + jobject context_handle = newJavaManagedByteBuffer(env, sizeof(MacOSXContext)); if (context_handle == NULL) { throwException(env, "Could not create handle buffer"); @@ -67,16 +68,37 @@ JNIEXPORT jobject JNICALL Java_org_lwjgl_opengl_MacOSXContextImplementation_nCre peer_info = (MacOSXPeerInfo *)(*env)->GetDirectBufferAddress(env, peer_info_handle); if (shared_context_handle != NULL) { shared_context_info = (MacOSXContext *)(*env)->GetDirectBufferAddress(env, shared_context_handle); - shared_context = shared_context_info->context; + shared_context = shared_context_info->context; } - context = [[NSOpenGLContext alloc] initWithFormat:peer_info->pixel_format shareContext:shared_context]; + context = [[NSOpenGLContext alloc] initWithFormat:peer_info->pixel_format shareContext:shared_context]; if (context == NULL) { throwException(env, "Could not create context"); return NULL; } + + if (peer_info->isWindowed) { + if (peer_info->window_info->fullscreen) { + // set a fixed backbuffer size for fullscreen + CGLContextObj cgcontext = (CGLContextObj)[context CGLContextObj]; + NSSize displaySize = peer_info->window_info->display_rect.size; + GLint dim[2] = {displaySize.width, displaySize.height}; + CGLSetParameter(cgcontext, kCGLCPSurfaceBackingSize, dim); + CGLEnable(cgcontext, kCGLCESurfaceBackingSize); + } + else { + // disable any fixed backbuffer size to allow resizing + CGLContextObj cgcontext = (CGLContextObj)[context CGLContextObj]; + CGLDisable(cgcontext, kCGLCESurfaceBackingSize); + } + + [peer_info->window_info->view setOpenGLContext:context]; + peer_info->window_info->context = context; + } + context_info = (MacOSXContext *)(*env)->GetDirectBufferAddress(env, context_handle); context_info->context = context; - context_info->peer_info = peer_info; + context_info->peer_info = peer_info; + [pool release]; return context_handle; } @@ -88,7 +110,7 @@ JNIEXPORT jlong JNICALL Java_org_lwjgl_opengl_MacOSXContextImplementation_getCGL CGLContextObj cgl_context = [context_info->context CGLContextObj]; CGLShareGroupObj share_group = CGLGetShareGroup(cgl_context); [pool release]; - return share_group; + return (jlong)share_group; } JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXContextImplementation_nSwapBuffers @@ -96,22 +118,25 @@ JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXContextImplementation_nSwapBu NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; MacOSXContext *context_info = (MacOSXContext *)(*env)->GetDirectBufferAddress(env, context_handle); [context_info->context flushBuffer]; - context_info->peer_info->canDrawGL = true; - [pool release]; + + if (context_info->peer_info->isCALayer) { + // blit the contents of buffer to CALayer + [context_info->peer_info->glLayer blitFrameBuffer]; + } + + [pool release]; } - JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXContextImplementation_nUpdate (JNIEnv *env, jclass clazz, jobject context_handle) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; MacOSXContext *context_info = (MacOSXContext *)(*env)->GetDirectBufferAddress(env, context_handle); [context_info->context update]; - context_info->peer_info->canDrawGL = true; [pool release]; } JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXContextImplementation_clearDrawable - (JNIEnv *env, jclass clazz, jobject context_handle) { +(JNIEnv *env, jclass clazz, jobject context_handle) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; MacOSXContext *context_info = (MacOSXContext *)(*env)->GetDirectBufferAddress(env, context_handle); [context_info->context clearDrawable]; @@ -121,36 +146,50 @@ JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXContextImplementation_clearDr JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXContextImplementation_nReleaseCurrentContext (JNIEnv *env, jclass clazz) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - [NSOpenGLContext clearCurrentContext]; + [NSOpenGLContext clearCurrentContext]; [pool release]; } JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXContextImplementation_setView - (JNIEnv *env, jclass clazz, jobject peer_info_handle, jobject context_handle) { + (JNIEnv *env, jclass clazz, jobject peer_info_handle, jobject context_handle) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; MacOSXContext *context_info = (MacOSXContext *)(*env)->GetDirectBufferAddress(env, context_handle); MacOSXPeerInfo *peer_info = (MacOSXPeerInfo *)(*env)->GetDirectBufferAddress(env, peer_info_handle); - if (peer_info->window) { - [context_info->context setView: peer_info->nsview]; - } else { + + if (peer_info->isWindowed) { + [context_info->context setView: peer_info->window_info->view]; + } + else { [context_info->context setPixelBuffer:peer_info->pbuffer cubeMapFace:0 mipMapLevel:0 currentVirtualScreen:0]; } - peer_info->canDrawGL = true; + + if (peer_info->isCALayer) { + peer_info->glLayer->setViewport = YES; + // if using a CALayer, attach it to AWT Canvas and create a shared opengl context with current context + [peer_info->glLayer performSelectorOnMainThread:@selector(attachLayer) withObject:nil waitUntilDone:NO]; + } + [pool release]; } JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXContextImplementation_nMakeCurrent (JNIEnv *env, jclass clazz, jobject context_handle) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - MacOSXContext *context_info = (MacOSXContext *)(*env)->GetDirectBufferAddress(env, context_handle); + MacOSXContext *context_info = (MacOSXContext *)(*env)->GetDirectBufferAddress(env, context_handle); [context_info->context makeCurrentContext]; + + if (context_info->peer_info->isCALayer && context_info->peer_info->glLayer->setViewport) { + context_info->peer_info->glLayer->setViewport = NO; + glViewport(0, 0, [context_info->peer_info->glLayer getWidth], [context_info->peer_info->glLayer getHeight]); + } + [pool release]; } JNIEXPORT jboolean JNICALL Java_org_lwjgl_opengl_MacOSXContextImplementation_nIsCurrent (JNIEnv *env, jclass clazz, jobject context_handle) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - MacOSXContext *context_info = (MacOSXContext *)(*env)->GetDirectBufferAddress(env, context_handle); + MacOSXContext *context_info = (MacOSXContext *)(*env)->GetDirectBufferAddress(env, context_handle); bool result = context_info->context == [NSOpenGLContext currentContext]; [pool release]; return result ? JNI_TRUE : JNI_FALSE; @@ -168,8 +207,24 @@ JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXContextImplementation_nSetSwa JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXContextImplementation_nDestroy (JNIEnv *env, jclass clazz, jobject context_handle) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + MacOSXContext *context_info = (MacOSXContext *)(*env)->GetDirectBufferAddress(env, context_handle); - [context_info->context clearDrawable]; - [context_info->context release]; + + if (context_info->peer_info->isCALayer) { + context_info->peer_info->isCALayer = false; + [context_info->peer_info->glLayer performSelectorOnMainThread:@selector(removeLayer) withObject:nil waitUntilDone:YES]; + [context_info->peer_info->glLayer release]; + context_info->peer_info->glLayer = nil; + } + + // clearDrawable on main thread to ensure its not in use + [context_info->context performSelectorOnMainThread:@selector(clearDrawable) withObject:nil waitUntilDone:YES]; + + if (context_info->peer_info->isWindowed) { + [context_info->context release]; + context_info->context = nil; + context_info->peer_info->window_info->context = nil; + } + [pool release]; } diff --git a/src/native/macosx/org_lwjgl_opengl_MacOSXNativeKeyboard.m b/src/native/macosx/org_lwjgl_opengl_MacOSXNativeKeyboard.m new file mode 100644 index 00000000..aec26e84 --- /dev/null +++ b/src/native/macosx/org_lwjgl_opengl_MacOSXNativeKeyboard.m @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2002-2012 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * $Id: org_lwjgl_opengl_MacOSXNativeKeyboard.m 3055 2012-08-29 0:46:00Z mojang $ + * + * Mac OS X native keyboard functions. + * + * @author mojang + * @version $Revision: 3055 $ + */ + +#import +#import +#import +#import +#import "common_tools.h" +#import "org_lwjgl_opengl_MacOSXNativeKeyboard.h" +#import "context.h" + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXNativeKeyboard_nRegisterKeyListener(JNIEnv *env, jobject this, jobject window_handle) { + MacOSXWindowInfo *window_info = (MacOSXWindowInfo *)(*env)->GetDirectBufferAddress(env, window_handle); + window_info->jkeyboard = (*env)->NewGlobalRef(env, this); +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXNativeKeyboard_nUnregisterKeyListener(JNIEnv *env, jobject this, jobject window_handle) { + MacOSXWindowInfo *window_info = (MacOSXWindowInfo *)(*env)->GetDirectBufferAddress(env, window_handle); + window_info->jkeyboard = NULL; +} diff --git a/src/native/macosx/org_lwjgl_opengl_MacOSXNativeMouse.m b/src/native/macosx/org_lwjgl_opengl_MacOSXNativeMouse.m new file mode 100644 index 00000000..1d625ee1 --- /dev/null +++ b/src/native/macosx/org_lwjgl_opengl_MacOSXNativeMouse.m @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2002-2012 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * $Id: org_lwjgl_opengl_MacOSXNativeKeyboard.m 3055 2012-08-29 0:46:00Z mojang $ + * + * Mac OS X native keyboard functions. + * + * @author mojang + * @version $Revision: 3055 $ + */ + +#import +#import +#import +#import +#import "common_tools.h" +#import "org_lwjgl_opengl_MacOSXNativeMouse.h" +#import "context.h" + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXNativeMouse_nGrabMouse(JNIEnv *env, jclass this, jboolean grab) { + CGAssociateMouseAndMouseCursorPosition(grab == JNI_TRUE ? FALSE : TRUE); + if (grab) { + CGDisplayHideCursor(kCGDirectMainDisplay); + } + else { + CGDisplayShowCursor(kCGDirectMainDisplay); + } +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXNativeMouse_nSetCursorPosition(JNIEnv *env, jclass this, jobject window_handle, jint x, jint y) { + MacOSXWindowInfo *window_info = (MacOSXWindowInfo *)(*env)->GetDirectBufferAddress(env, window_handle); + + CGPoint p; + + if (window_info->fullscreen) { + NSPoint point = NSMakePoint(x, y); + + // convert point to window/screen coordinates + point = [window_info->view convertPoint:point fromView:nil]; + + p.x = point.x; + p.y = point.y; + } + else { + NSRect screenRect = [[window_info->window screen] frame]; + NSRect viewRect = [window_info->view frame]; + NSRect winRect = [window_info->window frame]; + + // get window coords of the view origin + NSPoint viewPoint = [window_info->view convertPoint:viewRect.origin fromView:nil]; + + // convert y to screen coordinates, origin bottom left + p.y = winRect.origin.y + viewPoint.y + (viewRect.size.height - y - 1); + + p.x = winRect.origin.x + viewPoint.x + x; + // flip y coordinates (origin top left) to allow use with CGDisplayMoveCursorToPoint + p.y = screenRect.size.height - p.y - 1; + } + + CGDisplayMoveCursorToPoint(CGMainDisplayID(), p); +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXNativeMouse_nRegisterMouseListener(JNIEnv *env, jobject _this, jobject window_handle) { + MacOSXWindowInfo *window_info = (MacOSXWindowInfo *)(*env)->GetDirectBufferAddress(env, window_handle); + [window_info->window setAcceptsMouseMovedEvents:YES]; + window_info->jmouse = (*env)->NewGlobalRef(env, _this); + + // since initial mouse location is not reported until mouse is moved + // manually get the mouse location and report it with a fake event + NSPoint mouseLocation = [window_info->window mouseLocationOutsideOfEventStream]; + mouseLocation = [window_info->view convertPoint:mouseLocation fromView:nil]; + + NSEvent *mouseLocationEvent = [NSEvent + mouseEventWithType:NSMouseMoved + location:mouseLocation + modifierFlags:NSMouseMovedMask + timestamp:0 + windowNumber:[window_info->window windowNumber] + context:nil + eventNumber:0 + clickCount:0 + pressure:0]; + + [window_info->view mouseMoved:mouseLocationEvent]; +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXNativeMouse_nUnregisterMouseListener(JNIEnv *env, jobject this, jobject window_handle) { + MacOSXWindowInfo *window_info = (MacOSXWindowInfo *)(*env)->GetDirectBufferAddress(env, window_handle); + [window_info->window setAcceptsMouseMovedEvents:NO]; + window_info->jmouse = nil; +} + +JNIEXPORT jlong JNICALL Java_org_lwjgl_opengl_MacOSXNativeMouse_nCreateCursor(JNIEnv *env, jobject _this, jint width, jint height, jint x_hotspot, jint y_hotspot, jint num_images, jobject image_buffer, jint images_offset, jobject delay_buffer, jint delays_offset) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + jlong *bytes = (jint *)(*env)->GetDirectBufferAddress(env, image_buffer) + images_offset; + + NSBitmapImageRep *bitmap = [[[NSBitmapImageRep alloc] + initWithBitmapDataPlanes:(jlong *)&bytes + pixelsWide:width pixelsHigh:height + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSDeviceRGBColorSpace + bitmapFormat:NSAlphaNonpremultipliedBitmapFormat + bytesPerRow:width*4 + bitsPerPixel:32] autorelease]; + + NSImage *image = [[[NSImage alloc] initWithSize:NSMakeSize(width, height)] autorelease]; + + [image addRepresentation:bitmap]; + + + NSCursor *cursor = [[NSCursor alloc] initWithImage:image hotSpot:NSMakePoint(x_hotspot, y_hotspot)]; + + [pool release]; + + return (jlong)cursor; +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXNativeMouse_nDestroyCursor(JNIEnv *env, jobject _this, jlong cursor_pointer) { + if (cursor_pointer != 0) { + NSCursor *cursor = (NSCursor *)cursor_pointer; + [cursor release]; + } +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXNativeMouse_nSetCursor(JNIEnv *env, jobject _this, jlong cursor_pointer) { + if (cursor_pointer == 0) { + // restore default cursor + [[NSCursor arrowCursor] performSelectorOnMainThread:@selector(set) withObject:nil waitUntilDone:NO]; + } + else { + NSCursor *cursor = (NSCursor *)cursor_pointer; + [cursor performSelectorOnMainThread:@selector(set) withObject:nil waitUntilDone:NO]; + } +} diff --git a/src/native/macosx/org_lwjgl_opengl_MacOSXPbufferPeerInfo.m b/src/native/macosx/org_lwjgl_opengl_MacOSXPbufferPeerInfo.m index 46fa5655..e1e41dff 100644 --- a/src/native/macosx/org_lwjgl_opengl_MacOSXPbufferPeerInfo.m +++ b/src/native/macosx/org_lwjgl_opengl_MacOSXPbufferPeerInfo.m @@ -65,7 +65,7 @@ JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXPbufferPeerInfo_nCreate(JNIEn } MacOSXPeerInfo *peer_info = (MacOSXPeerInfo *)(*env)->GetDirectBufferAddress(env, peer_info_handle); peer_info->pbuffer = pbuffer; - peer_info->window = false; + peer_info->isWindowed = false; [pool release]; }