From 0fb264180e3a76e9bdf061f8508dccbe4e832170 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Fri, 12 Nov 2004 13:23:20 +0000 Subject: [PATCH] Mac OS X: Made AWT access thread safe --- src/java/org/lwjgl/opengl/MacOSXDisplay.java | 94 ++---- src/java/org/lwjgl/opengl/MacOSXFrame.java | 276 ++++++++++++++++++ src/java/org/lwjgl/opengl/MacOSXGLCanvas.java | 52 +++- 3 files changed, 344 insertions(+), 78 deletions(-) create mode 100644 src/java/org/lwjgl/opengl/MacOSXFrame.java diff --git a/src/java/org/lwjgl/opengl/MacOSXDisplay.java b/src/java/org/lwjgl/opengl/MacOSXDisplay.java index d2d61b74..ef378d93 100644 --- a/src/java/org/lwjgl/opengl/MacOSXDisplay.java +++ b/src/java/org/lwjgl/opengl/MacOSXDisplay.java @@ -51,30 +51,26 @@ import java.util.List; import java.util.ArrayList; import java.awt.BorderLayout; import java.awt.Frame; -import javax.swing.JFrame; import java.awt.Insets; import java.awt.Cursor; import java.awt.Rectangle; import java.awt.Dimension; import java.awt.Point; import java.awt.Toolkit; -import java.awt.GraphicsEnvironment; -import java.awt.GraphicsDevice; import java.awt.image.BufferedImage; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; -final class MacOSXDisplay extends WindowAdapter implements DisplayImplementation { - private JFrame frame; - private MacOSXGLCanvas canvas; - private boolean close_requested; +final class MacOSXDisplay implements DisplayImplementation { + private MacOSXFrame frame; private MouseEventQueue mouse_queue; private KeyboardEventQueue keyboard_queue; private java.awt.DisplayMode requested_mode; + + /* States */ + private boolean close_requested; public MacOSXDisplay() { new MacOSXApplicationListener(); @@ -82,33 +78,12 @@ final class MacOSXDisplay extends WindowAdapter implements DisplayImplementation public void createWindow(DisplayMode mode, boolean fullscreen, int x, int y) throws LWJGLException { close_requested = false; - frame = new JFrame(); - frame.setResizable(false); - frame.addWindowListener(this); - canvas = new MacOSXGLCanvas(); - frame.getContentPane().add(canvas, BorderLayout.CENTER); - frame.setUndecorated(fullscreen); - if (fullscreen) { - getDevice().setFullScreenWindow(frame); - getDevice().setDisplayMode(requested_mode); - /** For some strange reason, the display mode is sometimes silently capped even though the mode is reported as supported */ - if (requested_mode.getWidth() != getDevice().getDisplayMode().getWidth() || requested_mode.getHeight() != getDevice().getDisplayMode().getHeight()) { - destroyWindow(); - throw new LWJGLException("AWT capped mode"); - } + try { + frame = new MacOSXFrame(mode, requested_mode, fullscreen, x, y); + } catch (LWJGLException e) { + destroyWindow(); + throw e; } - frame.pack(); - reshape(x, y, mode.getWidth(), mode.getHeight()); - frame.setVisible(true); - frame.requestFocus(); - canvas.requestFocus(); - canvas.waitForCanvasCreated(); - } - - private GraphicsDevice getDevice() { - GraphicsEnvironment g_env = GraphicsEnvironment.getLocalGraphicsEnvironment(); - GraphicsDevice device = g_env.getDefaultScreenDevice(); - return device; } private void handleQuit() { @@ -117,21 +92,12 @@ final class MacOSXDisplay extends WindowAdapter implements DisplayImplementation } } - public void windowClosing(WindowEvent e) { - handleQuit(); - } - - public void windowActivated(WindowEvent e) { - warpCursor(); - } - public void destroyWindow() { - if (getDevice().getFullScreenWindow() != null) - getDevice().setFullScreenWindow(null); + if (MacOSXFrame.getDevice().getFullScreenWindow() != null) + MacOSXFrame.getDevice().setFullScreenWindow(null); setView(null); - frame.dispose(); + frame.syncDispose(); frame = null; - canvas = null; } public int getGammaRampLength() { @@ -156,7 +122,7 @@ final class MacOSXDisplay extends WindowAdapter implements DisplayImplementation } public void switchDisplayMode(DisplayMode mode) throws LWJGLException { - java.awt.DisplayMode[] awt_modes = getDevice().getDisplayModes(); + java.awt.DisplayMode[] awt_modes = MacOSXFrame.getDevice().getDisplayModes(); for (int i = 0; i < awt_modes.length; i++) if (equals(awt_modes[i], mode)) { requested_mode = awt_modes[i]; @@ -166,8 +132,8 @@ final class MacOSXDisplay extends WindowAdapter implements DisplayImplementation } public void resetDisplayMode() { - if (getDevice().getFullScreenWindow() != null) - getDevice().setFullScreenWindow(null); + if (MacOSXFrame.getDevice().getFullScreenWindow() != null) + MacOSXFrame.getDevice().setFullScreenWindow(null); requested_mode = null; } @@ -188,11 +154,11 @@ final class MacOSXDisplay extends WindowAdapter implements DisplayImplementation } public DisplayMode init() { - return createLWJGLDisplayMode(getDevice().getDisplayMode()); + return createLWJGLDisplayMode(MacOSXFrame.getDevice().getDisplayMode()); } public DisplayMode[] getAvailableDisplayModes() { - java.awt.DisplayMode[] awt_modes = getDevice().getDisplayModes(); + java.awt.DisplayMode[] awt_modes = MacOSXFrame.getDevice().getDisplayModes(); List modes = new ArrayList(); for (int i = 0; i < awt_modes.length; i++) if (awt_modes[i].getBitDepth() >= 16) @@ -203,7 +169,7 @@ final class MacOSXDisplay extends WindowAdapter implements DisplayImplementation } public void setTitle(String title) { - frame.setTitle(title); + frame.syncSetTitle(title); } public boolean isCloseRequested() { @@ -216,15 +182,15 @@ final class MacOSXDisplay extends WindowAdapter implements DisplayImplementation } public boolean isVisible() { - return frame.isShowing(); + return frame.syncIsVisible(); } public boolean isActive() { - return frame.isFocused(); + return frame.syncIsActive(); } public boolean isDirty() { - return canvas.isDirty(); + return frame.getCanvas().syncIsDirty(); } public native void setView(MacOSXGLCanvas canvas); @@ -238,10 +204,10 @@ final class MacOSXDisplay extends WindowAdapter implements DisplayImplementation public native void destroyContext(); public void update() { - if (canvas.shouldUpdateContext()) { + if (frame.syncShouldUpdateContext()) { updateContext(); /* This is necessary to make sure the context won't "forget" about the view size */ - GL11.glViewport(0, 0, canvas.getWidth(), canvas.getHeight()); + GL11.glViewport(0, 0, frame.getCanvas().syncGetWidth(), frame.getCanvas().syncGetHeight()); warpCursor(); } mouse_queue.updateDeltas(); @@ -249,7 +215,7 @@ final class MacOSXDisplay extends WindowAdapter implements DisplayImplementation private void warpCursor() { if (mouse_queue != null && mouse_queue.isGrabbed()) { - Rectangle bounds = frame.getBounds(); + Rectangle bounds = frame.syncGetBounds(); int x = bounds.x + bounds.width/2; int y = bounds.y + bounds.height/2; nWarpCursor(x, y); @@ -263,8 +229,7 @@ final class MacOSXDisplay extends WindowAdapter implements DisplayImplementation public native void setVSyncEnabled(boolean sync); public void reshape(int x, int y, int width, int height) { - Insets insets = frame.getInsets(); - frame.setBounds(x, y, width + insets.left + insets.right, height + insets.top + insets.bottom); + frame.syncReshape(x, y, width, height); } /* Mouse */ @@ -277,6 +242,7 @@ final class MacOSXDisplay extends WindowAdapter implements DisplayImplementation } public void createMouse() { + MacOSXGLCanvas canvas = frame.getCanvas(); this.mouse_queue = new MouseEventQueue(canvas.getWidth(), canvas.getHeight()); canvas.addMouseListener(mouse_queue); canvas.addMouseMotionListener(mouse_queue); @@ -284,6 +250,7 @@ final class MacOSXDisplay extends WindowAdapter implements DisplayImplementation } public void destroyMouse() { + MacOSXGLCanvas canvas = frame.getCanvas(); canvas.removeMouseListener(mouse_queue); canvas.removeMouseWheelListener(mouse_queue); canvas.removeMouseMotionListener(mouse_queue); @@ -321,7 +288,7 @@ final class MacOSXDisplay extends WindowAdapter implements DisplayImplementation public void setNativeCursor(Object handle) throws LWJGLException { Cursor awt_cursor = (Cursor)handle; - canvas.setCursor(awt_cursor); + frame.syncSetCursor(awt_cursor); } public int getMinCursorSize() { @@ -336,6 +303,7 @@ final class MacOSXDisplay extends WindowAdapter implements DisplayImplementation /* Keyboard */ public void createKeyboard() throws LWJGLException { + MacOSXGLCanvas canvas = frame.getCanvas(); this.keyboard_queue = new KeyboardEventQueue(); canvas.addKeyListener(keyboard_queue); } @@ -345,7 +313,7 @@ final class MacOSXDisplay extends WindowAdapter implements DisplayImplementation * This line is commented out to work around AWT bug 4867453: * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4867453 */ -// canvas.removeKeyListener(keyboard_queue); +// frame.getCanvas().removeKeyListener(keyboard_queue); this.keyboard_queue = null; } diff --git a/src/java/org/lwjgl/opengl/MacOSXFrame.java b/src/java/org/lwjgl/opengl/MacOSXFrame.java new file mode 100644 index 00000000..c638eab6 --- /dev/null +++ b/src/java/org/lwjgl/opengl/MacOSXFrame.java @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2002-2004 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; + +/** + * This is the Mac OS X AWT Frame. It contains thread safe + * methods to manipulateit from non-AWT threads + * @author elias_naur + */ + +import java.awt.BorderLayout; +import java.awt.Frame; +import java.awt.Insets; +import java.awt.Cursor; +import java.awt.Rectangle; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Toolkit; +import java.awt.GraphicsEnvironment; +import java.awt.GraphicsDevice; +import java.awt.image.BufferedImage; +import java.awt.event.WindowListener; +import java.awt.event.ComponentListener; +import java.awt.event.ComponentEvent; +import java.awt.event.WindowEvent; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; + +import org.lwjgl.LWJGLException; + +final class MacOSXFrame extends Frame implements WindowListener, ComponentListener { + private final MacOSXGLCanvas canvas; + private boolean close_requested; + + /* States */ + private Rectangle bounds; + private boolean should_update; + private boolean active; + private boolean visible; + private boolean minimized; + + public MacOSXFrame(DisplayMode mode, java.awt.DisplayMode requested_mode, boolean fullscreen, int x, int y) throws LWJGLException { + setResizable(false); + addWindowListener(this); + addComponentListener(this); + canvas = new MacOSXGLCanvas(); + add(canvas, BorderLayout.CENTER); + setUndecorated(fullscreen); + if (fullscreen) { + getDevice().setFullScreenWindow(this); + getDevice().setDisplayMode(requested_mode); + /** For some strange reason, the display mode is sometimes silently capped even though the mode is reported as supported */ + if (requested_mode.getWidth() != getDevice().getDisplayMode().getWidth() || requested_mode.getHeight() != getDevice().getDisplayMode().getHeight()) { + syncDispose(); + throw new LWJGLException("AWT capped mode"); + } + } + pack(); + syncReshape(x, y, mode.getWidth(), mode.getHeight()); + invokeAWT(new Runnable() { + public void run() { + setVisible(true); + requestFocus(); + canvas.requestFocus(); + } + }); + canvas.waitForCanvasCreated(); + } + + 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 final 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; + } + } + + public void windowActivated(WindowEvent e) { + synchronized (this) { + should_update = true; + active = true; + } + } + + public void syncDispose() { + invokeAWT(new Runnable() { + public void run() { + dispose(); + } + }); + } + + private class TitleSetter implements Runnable { + private final String title; + + public TitleSetter(String title) { + this.title = title; + } + + public void run() { + setTitle(title); + } + } + + public void syncSetTitle(String title) { + invokeAWT(new TitleSetter(title)); + } + + 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 syncShouldUpdateContext() { + boolean result; + synchronized (this) { + result = canvas.syncShouldUpdateContext() || should_update; + should_update = false; + } + return result; + } + + private class Reshaper implements Runnable { + private final int x; + private final int y; + private final int width; + private final int height; + + public Reshaper(int x, int y, int width, int height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + public void run() { + Insets insets = getInsets(); + setBounds(x, y, width + insets.left + insets.right, height + insets.top + insets.bottom); + } + } + + private void invokeAWT(Runnable r) { + try { + java.awt.EventQueue.invokeAndWait(r); + } catch (InterruptedException e) { + // ignore + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + public void syncReshape(int x, int y, int width, int height) { + invokeAWT(new Reshaper(x, y, width, height)); + } + + private class CursorSetter implements Runnable { + private final java.awt.Cursor awt_cursor; + + public CursorSetter(java.awt.Cursor awt_cursor) { + this.awt_cursor = awt_cursor; + } + + public void run() { + canvas.setCursor(awt_cursor); + } + } + + public void syncSetCursor(java.awt.Cursor awt_cursor) { + invokeAWT(new CursorSetter(awt_cursor)); + } +} diff --git a/src/java/org/lwjgl/opengl/MacOSXGLCanvas.java b/src/java/org/lwjgl/opengl/MacOSXGLCanvas.java index b7995fc0..e1f5770b 100644 --- a/src/java/org/lwjgl/opengl/MacOSXGLCanvas.java +++ b/src/java/org/lwjgl/opengl/MacOSXGLCanvas.java @@ -39,22 +39,18 @@ package org.lwjgl.opengl; import java.awt.Canvas; import java.awt.Dimension; import java.awt.Point; +import java.awt.Rectangle; import java.awt.Graphics; -import java.awt.event.HierarchyBoundsListener; -import java.awt.event.HierarchyEvent; +import java.awt.event.ComponentListener; +import java.awt.event.ComponentEvent; -final class MacOSXGLCanvas extends Canvas implements HierarchyBoundsListener { +final class MacOSXGLCanvas extends Canvas implements ComponentListener { + private int width; + private int height; private boolean context_update; private boolean canvas_created; private boolean dirty; - public MacOSXGLCanvas() { - setFocusTraversalKeysEnabled(false); - /* Input methods are not enabled in fullscreen anyway, so disable always */ - enableInputMethods(false); - addHierarchyBoundsListener(this); - } - public void update(Graphics g) { paint(g); } @@ -63,6 +59,10 @@ final class MacOSXGLCanvas extends Canvas implements HierarchyBoundsListener { synchronized (this) { dirty = true; if (!canvas_created) { + setFocusTraversalKeysEnabled(false); + /* Input methods are not enabled in fullscreen anyway, so disable always */ + enableInputMethods(false); + addComponentListener(this); ((MacOSXDisplay)Display.getImplementation()).setView(this); canvas_created = true; setUpdate(); @@ -71,7 +71,7 @@ final class MacOSXGLCanvas extends Canvas implements HierarchyBoundsListener { } } - public boolean isDirty() { + public boolean syncIsDirty() { boolean result; synchronized (this) { result = dirty; @@ -92,7 +92,7 @@ final class MacOSXGLCanvas extends Canvas implements HierarchyBoundsListener { } } - public boolean shouldUpdateContext() { + public boolean syncShouldUpdateContext() { boolean should_update; synchronized (this) { should_update = context_update; @@ -102,14 +102,36 @@ final class MacOSXGLCanvas extends Canvas implements HierarchyBoundsListener { } private synchronized void setUpdate() { - context_update = true; + synchronized (this) { + width = getWidth(); + height = getHeight(); + context_update = true; + } } - public void ancestorResized(HierarchyEvent e) { + public int syncGetWidth() { + synchronized (this) { + return width; + } + } + + public int syncGetHeight() { + synchronized (this) { + return height; + } + } + + public void componentShown(ComponentEvent e) { + } + + public void componentHidden(ComponentEvent e) { + } + + public void componentResized(ComponentEvent e) { setUpdate(); } - public void ancestorMoved(HierarchyEvent e) { + public void componentMoved(ComponentEvent e) { setUpdate(); }