diff --git a/msi.gama.core/src/msi/gama/common/interfaces/IDisplaySynchronizer.java b/msi.gama.core/src/msi/gama/common/interfaces/IDisplaySynchronizer.java index 431810a0e0..5a8d15a0a2 100644 --- a/msi.gama.core/src/msi/gama/common/interfaces/IDisplaySynchronizer.java +++ b/msi.gama.core/src/msi/gama/common/interfaces/IDisplaySynchronizer.java @@ -1,12 +1,12 @@ /******************************************************************************************************* * - * IDisplaySynchronizer.java, in msi.gama.core, is part of the source code of the - * GAMA modeling and simulation platform (v.1.8.2). + * IDisplaySynchronizer.java, in msi.gama.core, is part of the source code of the GAMA modeling and simulation platform + * (v.1.8.2). * * (c) 2007-2022 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, TLU, CTU) * * Visit https://github.com/gama-platform/gama for license information and contacts. - * + * ********************************************************************************************************/ package msi.gama.common.interfaces; @@ -20,7 +20,7 @@ public interface IDisplaySynchronizer { /** * Allows any object calling this method to release the thread waiting for the scene to be rendered (called by the - * rendering processes or when this surface is disposed) + * rendering processes or when this surface is disposed). Nothing to do by default */ void signalRenderingIsFinished(); @@ -49,4 +49,12 @@ public interface IDisplaySynchronizer { */ void signalSurfaceIsRealized(); + /** + * Sets the surface. + * + * @param displaySurface + * the new surface + */ + void setSurface(IDisplaySurface displaySurface); + } diff --git a/ummisco.gama.annotations/src/ummisco/gama/dev/utils/FLAGS.java b/ummisco.gama.annotations/src/ummisco/gama/dev/utils/FLAGS.java index be493c3f9b..6f78c6f62f 100644 --- a/ummisco.gama.annotations/src/ummisco/gama/dev/utils/FLAGS.java +++ b/ummisco.gama.annotations/src/ummisco/gama/dev/utils/FLAGS.java @@ -1,12 +1,12 @@ /******************************************************************************************************* * - * FLAGS.java, in ummisco.gama.annotations, is part of the source code of the - * GAMA modeling and simulation platform (v.1.8.2). + * FLAGS.java, in ummisco.gama.annotations, is part of the source code of the GAMA modeling and simulation platform + * (v.1.8.2). * * (c) 2007-2022 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, TLU, CTU) * * Visit https://github.com/gama-platform/gama for license information and contacts. - * + * ********************************************************************************************************/ package ummisco.gama.dev.utils; @@ -63,16 +63,15 @@ private static boolean get(final String name, final boolean def) { /** * For debugging purposes, see #3164. True by default until bugs on Linux regarding the use of multiple threads in - * UI processes are solved. + * UI processes are solved. Update 12/03/22: now false by default, tested on Ubuntu */ - public static final boolean USE_OLD_ANIMATOR = - get("use_old_animator", true /* System.getProperty("os.name").contains("Linux") */); + // public static final boolean USE_OLD_ANIMATOR = get("use_old_animator", false); /** * Used in LayeredDisplayView. True to use a combination of wait(), notify() and Thread.sleep() for synchronizing * displays with the simulation or false to use semaphores (reduces the time spent between frames). False by default */ - public static final boolean USE_OLD_SYNC_STRATEGY = get("use_old_sync_strategy", false); + // public static final boolean USE_OLD_SYNC_STRATEGY = get("use_old_sync_strategy", false); /** * Used in GamaPreferences, true to save the preferences in the global (managed by the JRE) preference store or @@ -105,18 +104,17 @@ private static boolean get(final String name, final boolean def) { public static final boolean USE_LEGACY_DRAWERS = get("use_legacy_drawers", false); /** - * Used in msi.gama.application.workbench.ApplicationWorkbenchWindowAdvisor to work around issue #3195. If true, - * makes the workbench window resize its views asynchronously. True by default on macOS. Could prove useful also in - * other environments, for instance in the presence of slow graphic cards/computers. + * Originally used in msi.gama.application.workbench.ApplicationWorkbenchWindowAdvisor to work around issue #3195. + * If true, makes the workbench window resize its views asynchronously. Could prove useful in all environments, for + * instance in the presence of slow graphic cards/computers. False by default */ - public static final boolean USE_DELAYED_RESIZE = - get("use_delayed_resize", System.getProperty("os.name").contains("Mac")); + public static final boolean USE_DELAYED_RESIZE = get("use_delayed_resize", false); /** * Used in JOGL displays, esp. ummisco.gama.opengl.view.SWTOpenGLDisplaySurface to create a NEWT window instead of a - * GLCanvas. Advantages are multiple (smaller memory footprint, immediate opening and resizing, etc.) but so are - * incompatibilities. + * GLCanvas. Advantages are multiple (smaller memory footprint, immediate opening and resizing, etc.), and only a + * few glitches remain (esp. on macOS). True by defautl */ - public static final boolean USE_NATIVE_OPENGL_WINDOW = get("use_native_opengl_window", false); + public static final boolean USE_NATIVE_OPENGL_WINDOW = get("use_native_opengl_window", true); } diff --git a/ummisco.gama.java2d/src/ummisco/gama/java2d/AWTDisplayView.java b/ummisco.gama.java2d/src/ummisco/gama/java2d/AWTDisplayView.java index 5de50dbdde..4d33c2b96c 100644 --- a/ummisco.gama.java2d/src/ummisco/gama/java2d/AWTDisplayView.java +++ b/ummisco.gama.java2d/src/ummisco/gama/java2d/AWTDisplayView.java @@ -1,12 +1,12 @@ /******************************************************************************************************* * - * AWTDisplayView.java, in ummisco.gama.java2d, is part of the source code of the - * GAMA modeling and simulation platform (v.1.8.2). + * AWTDisplayView.java, in ummisco.gama.java2d, is part of the source code of the GAMA modeling and simulation platform + * (v.1.8.2). * * (c) 2007-2022 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, TLU, CTU) * * Visit https://github.com/gama-platform/gama for license information and contacts. - * + * ********************************************************************************************************/ package ummisco.gama.java2d; @@ -47,4 +47,9 @@ public void focusCanvas() { WorkbenchHelper.asyncRun(() -> centralPanel.forceFocus()); } + @Override + protected boolean canBeSynchronized() { + return true; + } + } \ No newline at end of file diff --git a/ummisco.gama.java2d/src/ummisco/gama/java2d/swing/SwingControl.java b/ummisco.gama.java2d/src/ummisco/gama/java2d/swing/SwingControl.java index cfeb28293a..501111f434 100644 --- a/ummisco.gama.java2d/src/ummisco/gama/java2d/swing/SwingControl.java +++ b/ummisco.gama.java2d/src/ummisco/gama/java2d/swing/SwingControl.java @@ -1,17 +1,20 @@ /******************************************************************************************************* * - * SwingControl.java, in ummisco.gama.java2d, is part of the source code of the - * GAMA modeling and simulation platform (v.1.8.2). + * SwingControl.java, in ummisco.gama.java2d, is part of the source code of the GAMA modeling and simulation platform + * (v.1.8.2). * * (c) 2007-2022 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, TLU, CTU) * * Visit https://github.com/gama-platform/gama for license information and contacts. - * + * ********************************************************************************************************/ package ummisco.gama.java2d.swing; import java.awt.EventQueue; import java.awt.Frame; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; import javax.swing.JApplet; import javax.swing.LayoutFocusTraversalPolicy; @@ -89,14 +92,35 @@ protected void populate() { // WorkbenchHelper.runInUI("Opening Java2D display", 400, m -> { WorkbenchHelper.asyncRun(() -> { frame = SWT_AWT.new_Frame(SwingControl.this); - // EventQueue.invokeLater(() -> { + + frame.setAlwaysOnTop(false); + frame.setAutoRequestFocus(false); + // frame.setFocusable(false); + // frame.setFocusableWindowState(false); applet = new JApplet(); + // applet.setFocusable(false); if (PlatformHelper.isWindows()) { applet.setFocusTraversalPolicy(new LayoutFocusTraversalPolicy()); } final Java2DDisplaySurface surface = createSwingComponent(); applet.getRootPane().getContentPane().add(surface); WorkaroundForIssue2476.installOn(applet, surface); frame.add(applet); - // }); + if (PlatformHelper.isMac()) { + MouseListener ml = new MouseAdapter() { + + @Override + public void mouseExited(final MouseEvent e) { + if (surface.isFocusOwner() && !surface.contains(e.getPoint())) { + frame.setVisible(false); + frame.setVisible(true); + WorkbenchHelper.asyncRun(() -> getShell().forceActive()); + } + + } + + }; + applet.addMouseListener(ml); + surface.addMouseListener(ml); + } }); // SwingControl.this.getParent().layout(true, true); diff --git a/ummisco.gama.opengl/src/ummisco/gama/opengl/view/GamaGLCanvas.java b/ummisco.gama.opengl/src/ummisco/gama/opengl/view/GamaGLCanvas.java index 64aa2d1762..8edd31e3b9 100644 --- a/ummisco.gama.opengl/src/ummisco/gama/opengl/view/GamaGLCanvas.java +++ b/ummisco.gama.opengl/src/ummisco/gama/opengl/view/GamaGLCanvas.java @@ -114,8 +114,8 @@ public void controlResized(final ControlEvent e) { * @return the GL animator control */ private GLAnimatorControl defineAnimator() { - GLAnimatorControl animator = - FLAGS.USE_OLD_ANIMATOR ? new SingleThreadGLAnimator(drawable) : new MultithreadGLAnimator(drawable); + GLAnimatorControl animator = new MultithreadGLAnimator(drawable); + // FLAGS.USE_OLD_ANIMATOR ? new SingleThreadGLAnimator(drawable) : new MultithreadGLAnimator(drawable); animator.setUpdateFPSFrames(FPSCounter.DEFAULT_FRAMES_PER_INTERVAL, null); return animator; } diff --git a/ummisco.gama.opengl/src/ummisco/gama/opengl/view/NEWTOverlay.java b/ummisco.gama.opengl/src/ummisco/gama/opengl/view/NEWTOverlay.java deleted file mode 100644 index 7c47921b76..0000000000 --- a/ummisco.gama.opengl/src/ummisco/gama/opengl/view/NEWTOverlay.java +++ /dev/null @@ -1,320 +0,0 @@ -/******************************************************************************************************* - * - * NEWTOverlay.java, in ummisco.gama.opengl, is part of the source code of the - * GAMA modeling and simulation platform (v.1.8.2). - * - * (c) 2007-2022 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, TLU, CTU) - * - * Visit https://github.com/gama-platform/gama for license information and contacts. - * - ********************************************************************************************************/ -package ummisco.gama.opengl.view; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ControlEvent; -import org.eclipse.swt.events.ControlListener; -import org.eclipse.swt.events.DisposeListener; -import org.eclipse.swt.events.PaintListener; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Scrollable; -import org.eclipse.swt.widgets.Shell; - -/** - * A customizable overlay over a control. - * - * @author Loris Securo - */ -public class NEWTOverlay { - - /** The parents. */ - private final List parents; - - /** The object to overlay. */ - private final Control objectToOverlay; - - /** The overlay. */ - private final Shell overlay; - - /** The label. */ - private final Label label; - - /** The control listener. */ - private final ControlListener controlListener; - - /** The dispose listener. */ - private final DisposeListener disposeListener; - - /** The paint listener. */ - private final PaintListener paintListener; - - /** The showing. */ - private boolean showing; - - /** The has client area. */ - private boolean hasClientArea; - - /** The scrollable to overlay. */ - private Scrollable scrollableToOverlay; - - /** - * Instantiates a new NEWT overlay. - * - * @param objectToOverlay - * the object to overlay - */ - public NEWTOverlay(final Control objectToOverlay) { - - Objects.requireNonNull(objectToOverlay); - - this.objectToOverlay = objectToOverlay; - - // if the object to overlay is an instance of Scrollable (e.g. Shell) then it has - // the getClientArea method, which is preferable over Control.getSize - if (objectToOverlay instanceof Scrollable) { - hasClientArea = true; - scrollableToOverlay = (Scrollable) objectToOverlay; - } else { - hasClientArea = false; - scrollableToOverlay = null; - } - - // save the parents of the object, so we can add/remove listeners to them - parents = new ArrayList<>(); - Composite parent = objectToOverlay.getParent(); - while (parent != null) { - parents.add(parent); - parent = parent.getParent(); - } - - // listener to track position and size changes in order to modify the overlay bounds as well - controlListener = new ControlListener() { - @Override - public void controlMoved(final ControlEvent e) { - reposition(); - } - - @Override - public void controlResized(final ControlEvent e) { - reposition(); - } - }; - - // listener to track paint changes, like when the object or its parents become not visible (for example changing - // tab in a TabFolder) - paintListener = arg0 -> reposition(); - - // listener to remove the overlay if the object to overlay is disposed - disposeListener = e -> remove(); - - // create the overlay shell - overlay = new Shell(objectToOverlay.getShell(), SWT.NO_TRIM | SWT.ON_TOP); - - // default values of the overlay - overlay.setBackground(objectToOverlay.getDisplay().getSystemColor(SWT.COLOR_GRAY)); - overlay.setAlpha(200); - - // so the label can inherit the background of the overlay - overlay.setBackgroundMode(SWT.INHERIT_DEFAULT); - - // label to display a text - // style WRAP so if it is too long the text get wrapped - label = new Label(overlay, SWT.WRAP); - - // to center the label - overlay.setLayout(new GridLayout()); - label.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); - - showing = false; - overlay.open(); - overlay.setVisible(showing); - } - - /** - * Show. - */ - public void show() { - - // if it's already visible we just exit - if (showing) return; - - // set the overlay position over the object - reposition(); - - // show the overlay - overlay.setVisible(true); - - // add listeners to the object to overlay - objectToOverlay.addControlListener(controlListener); - objectToOverlay.addDisposeListener(disposeListener); - objectToOverlay.addPaintListener(paintListener); - - // add listeners also to the parents because if they change then also the visibility of our object could change - for (Composite parent : parents) { - parent.addControlListener(controlListener); - parent.addPaintListener(paintListener); - } - - showing = true; - } - - /** - * Removes the. - */ - public void remove() { - - // if it's already not visible we just exit - if (!showing) return; - - // remove the listeners - if (!objectToOverlay.isDisposed()) { - objectToOverlay.removeControlListener(controlListener); - objectToOverlay.removeDisposeListener(disposeListener); - objectToOverlay.removePaintListener(paintListener); - } - - // remove the parents listeners - for (Composite parent : parents) { - if (!parent.isDisposed()) { - parent.removeControlListener(controlListener); - parent.removePaintListener(paintListener); - } - } - - // remove the overlay shell - if (!overlay.isDisposed()) { overlay.setVisible(false); } - - showing = false; - } - - /** - * Sets the background. - * - * @param background - * the new background - */ - public void setBackground(final Color background) { - overlay.setBackground(background); - } - - /** - * Gets the background. - * - * @return the background - */ - public Color getBackground() { return overlay.getBackground(); } - - /** - * Sets the alpha. - * - * @param alpha - * the new alpha - */ - public void setAlpha(final int alpha) { - overlay.setAlpha(alpha); - } - - /** - * Gets the alpha. - * - * @return the alpha - */ - public int getAlpha() { return overlay.getAlpha(); } - - /** - * Checks if is showing. - * - * @return true, if is showing - */ - public boolean isShowing() { return showing; } - - /** - * Sets the text. - * - * @param text - * the new text - */ - public void setText(final String text) { - label.setText(text); - - // to adjust the label size accordingly - overlay.layout(); - } - - /** - * Gets the text. - * - * @return the text - */ - public String getText() { return label.getText(); } - - /** - * Reposition. - */ - private void reposition() { - - if (objectToOverlay == null || objectToOverlay.isDisposed()) { - remove(); - return; - } - // if the object is not visible, we hide the overlay and exit - if (!objectToOverlay.isVisible()) { - overlay.setBounds(new Rectangle(0, 0, 0, 0)); - return; - } - - // if the object is visible we need to find the visible region in order to correctly place the overlay - - // get the display bounds of the object to overlay - Point objectToOverlayDisplayLocation = objectToOverlay.toDisplay(0, 0); - - Point objectToOverlaySize; - - // if it has a client area, we prefer that instead of the size - if (hasClientArea) { - Rectangle clientArea = scrollableToOverlay.getClientArea(); - objectToOverlaySize = new Point(clientArea.width, clientArea.height); - } else { - objectToOverlaySize = objectToOverlay.getSize(); - } - - Rectangle objectToOverlayBounds = new Rectangle(objectToOverlayDisplayLocation.x, - objectToOverlayDisplayLocation.y, objectToOverlaySize.x, objectToOverlaySize.y); - - Rectangle intersection = objectToOverlayBounds; - - // intersect the bounds of the object with its parents bounds so we get only the visible bounds - for (Composite parent : parents) { - - Rectangle parentClientArea = parent.getClientArea(); - Point parentLocation = parent.toDisplay(parentClientArea.x, parentClientArea.y); - Rectangle parentBounds = - new Rectangle(parentLocation.x, parentLocation.y, parentClientArea.width, parentClientArea.height); - - intersection = intersection.intersection(parentBounds); - - // if intersection has no size then it would be a waste of time to continue - if (intersection.width == 0 || intersection.height == 0) { break; } - } - - overlay.setBounds(intersection); - } - - /** - * Gets the shell. - * - * @return the shell - */ - public Control getShell() { return overlay; } - -} \ No newline at end of file diff --git a/ummisco.gama.opengl/src/ummisco/gama/opengl/view/OpenGLDisplayView.java b/ummisco.gama.opengl/src/ummisco/gama/opengl/view/OpenGLDisplayView.java index 280ecdeef8..2e05edd9cf 100644 --- a/ummisco.gama.opengl/src/ummisco/gama/opengl/view/OpenGLDisplayView.java +++ b/ummisco.gama.opengl/src/ummisco/gama/opengl/view/OpenGLDisplayView.java @@ -145,4 +145,9 @@ public void toggleCamera() { getOutput().getData().setCameraLocked(!getOutput().getData().isCameraLocked()); } + @Override + protected boolean canBeSynchronized() { + return getGLCanvas().getNEWTWindow().isVisible(); + } + } diff --git a/ummisco.gama.ui.experiment/src/ummisco/gama/ui/views/displays/LayeredDisplayDecorator.java b/ummisco.gama.ui.experiment/src/ummisco/gama/ui/views/displays/LayeredDisplayDecorator.java index 978a527f32..b93c11b79e 100644 --- a/ummisco.gama.ui.experiment/src/ummisco/gama/ui/views/displays/LayeredDisplayDecorator.java +++ b/ummisco.gama.ui.experiment/src/ummisco/gama/ui/views/displays/LayeredDisplayDecorator.java @@ -29,9 +29,11 @@ import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.ToolItem; import org.eclipse.ui.IPartListener2; +import org.eclipse.ui.IPartService; import org.eclipse.ui.IPerspectiveDescriptor; import org.eclipse.ui.IPerspectiveListener; import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchPartReference; import msi.gama.application.workbench.PerspectiveHelper; @@ -114,8 +116,8 @@ public class LayeredDisplayDecorator implements DisplayDataListener { LayeredDisplayDecorator(final LayeredDisplayView view) { this.view = view; createCommands(); - // final IPartService ps = ((IWorkbenchPart) view).getSite().getService(IPartService.class); - // ps.addPartListener(overlayListener); + final IPartService ps = ((IWorkbenchPart) view).getSite().getService(IPartService.class); + ps.addPartListener(overlayListener); } @@ -163,9 +165,9 @@ private boolean ok(final IWorkbenchPartReference partRef) { @Override public void partActivated(final IWorkbenchPartReference partRef) { - if (ok(partRef)) { DEBUG.OUT("Part Activated:" + partRef.getTitle()); + DEBUG.STACK(); WorkbenchHelper.asyncRun(() -> { if (overlay != null) { overlay.display(); } view.showCanvas(); @@ -180,21 +182,24 @@ public void partClosed(final IWorkbenchPartReference partRef) { @Override public void partDeactivated(final IWorkbenchPartReference partRef) { - - if (ok(partRef) && !view.surfaceComposite.isVisible()) { - DEBUG.OUT("Part Deactivated:" + partRef.getTitle()); - WorkbenchHelper.asyncRun(() -> { - if (overlay != null) { overlay.hide(); } - view.hideCanvas(); - }); - } + // On macOS, this event is wrongly sent when tabs are not displayed for the views and another display is + // selected + // if (PlatformHelper.isMac() && !PerspectiveHelper.keepTabs()) return; + // if (ok(partRef)) { + // DEBUG.OUT("Part Deactivated:" + partRef.getTitle()); + // WorkbenchHelper.asyncRun(() -> { + // if (overlay != null) { overlay.hide(); } + // view.hideCanvas(); + // }); + // } } @Override public void partHidden(final IWorkbenchPartReference partRef) { - // This event is wrongly sent when tabs are not displayed for the views - - if (ok(partRef) && !view.surfaceComposite.isVisible()) { + // On macOS, this event is wrongly sent when tabs are not displayed for the views and another display is + // selected + if (PlatformHelper.isMac() && !PerspectiveHelper.keepTabs()) return; + if (ok(partRef)) { DEBUG.OUT("Part hidden:" + partRef.getTitle()); WorkbenchHelper.asyncRun(() -> { if (overlay != null) { overlay.hide(); } @@ -205,7 +210,6 @@ public void partHidden(final IWorkbenchPartReference partRef) { @Override public void partVisible(final IWorkbenchPartReference partRef) { - if (ok(partRef)) { DEBUG.OUT("Part Visible:" + partRef.getTitle()); WorkbenchHelper.asyncRun(() -> { @@ -582,8 +586,8 @@ public void dispose() { // FIXME Remove the listeners try { WorkbenchHelper.getWindow().removePerspectiveListener(perspectiveListener); - // final IPartService ps = ((IWorkbenchPart) view).getSite().getService(IPartService.class); - // if (ps != null) { ps.removePartListener(overlayListener); } + final IPartService ps = ((IWorkbenchPart) view).getSite().getService(IPartService.class); + if (ps != null) { ps.removePartListener(overlayListener); } } catch (final Exception e) { } diff --git a/ummisco.gama.ui.experiment/src/ummisco/gama/ui/views/displays/LayeredDisplaySynchronizer.java b/ummisco.gama.ui.experiment/src/ummisco/gama/ui/views/displays/LayeredDisplaySynchronizer.java index 7c87923814..79af890ecf 100644 --- a/ummisco.gama.ui.experiment/src/ummisco/gama/ui/views/displays/LayeredDisplaySynchronizer.java +++ b/ummisco.gama.ui.experiment/src/ummisco/gama/ui/views/displays/LayeredDisplaySynchronizer.java @@ -1,18 +1,16 @@ /******************************************************************************************************* * - * LayeredDisplaySynchronizer.java, in ummisco.gama.ui.experiment, is part of the source code of the - * GAMA modeling and simulation platform (v.1.8.2). + * LayeredDisplaySynchronizer.java, in ummisco.gama.ui.experiment, is part of the source code of the GAMA modeling and + * simulation platform (v.1.8.2). * * (c) 2007-2022 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, TLU, CTU) * * Visit https://github.com/gama-platform/gama for license information and contacts. - * + * ********************************************************************************************************/ package ummisco.gama.ui.views.displays; -import static ummisco.gama.dev.utils.FLAGS.USE_OLD_SYNC_STRATEGY; - -import java.util.concurrent.Semaphore; +import java.util.concurrent.ArrayBlockingQueue; import msi.gama.common.interfaces.IDisplaySurface; import msi.gama.common.interfaces.IDisplaySynchronizer; @@ -23,123 +21,73 @@ */ public class LayeredDisplaySynchronizer implements IDisplaySynchronizer { + /** The Constant TOKEN. */ + static final Integer TOKEN = 0; + static { DEBUG.OFF(); } - /** The view update lock. */ - Semaphore viewUpdateLock = new Semaphore(0); - - /** The surface render lock. */ - Semaphore surfaceRenderLock = new Semaphore(1); - - /** The surface realisation lock. */ - Semaphore surfaceRealisationLock = new Semaphore(0); + /** The queues. */ + private final ArrayBlockingQueue realisationQueue = new ArrayBlockingQueue<>(1), + updateQueue = new ArrayBlockingQueue<>(1), renderQueue = new ArrayBlockingQueue<>(1); /** The surface. */ IDisplaySurface surface; /** - * Acquire view lock. - * - * @throws InterruptedException - * the interrupted exception - */ - private void acquireViewLock() throws InterruptedException { - if (viewUpdateLock.availablePermits() > 0) { viewUpdateLock.drainPermits(); } - viewUpdateLock.acquire(); - } - - /** - * Release view lock. - */ - private void releaseViewLock() { - viewUpdateLock.release(); - } - - /** - * Acquire lock. + * Sets the surface. * - * @throws InterruptedException - * the interrupted exception + * @param surface + * the new surface */ - private synchronized void acquireLock() throws InterruptedException { - wait(); - } + @Override + public void setSurface(final IDisplaySurface surface) { + this.surface = surface; + if (surface != null) { surface.setDisplaySynchronizer(this); } - /** - * Release lock. - */ - private synchronized void releaseLock() { - notify(); } @Override - public void waitForViewUpdateAuthorisation() { - DEBUG.OUT("Waiting for view to update: " + Thread.currentThread().getName()); + public void waitForSurfaceToBeRealized() { + // DEBUG.OUT("Waiting for surface to realize: " + Thread.currentThread().getName()); try { - if (USE_OLD_SYNC_STRATEGY) { - acquireLock(); - } else { - acquireViewLock(); - } + realisationQueue.take(); } catch (InterruptedException e) {} } @Override - public void authorizeViewUpdate() { - DEBUG.OUT("Signalling that view can be updated: " + Thread.currentThread().getName()); - if (USE_OLD_SYNC_STRATEGY) { - releaseLock(); - } else { - releaseViewLock(); - } - } - - @Override - public void waitForRenderingToBeFinished() { - DEBUG.OUT("Waiting for surface to be rendered: " + Thread.currentThread().getName()); - try { - if (USE_OLD_SYNC_STRATEGY) { - while (!surface.isRendered() && !surface.isDisposed()) { Thread.sleep(1); } - } else { - if (surfaceRenderLock.availablePermits() > 0) { surfaceRenderLock.drainPermits(); } - surfaceRenderLock.acquire(); - } - } catch (final InterruptedException e) {} - + public void signalSurfaceIsRealized() { + // DEBUG.OUT("Signalling that surface is realized: " + Thread.currentThread().getName()); + realisationQueue.offer(TOKEN); } @Override public void signalRenderingIsFinished() { - DEBUG.OUT("Signalling that surface is rendered: " + Thread.currentThread().getName()); - surfaceRenderLock.release(); + // DEBUG.OUT("Signalling that surface is rendered: " + Thread.currentThread().getName()); + renderQueue.offer(TOKEN); } - /** - * Sets the surface. - * - * @param surface - * the new surface - */ - public void setSurface(final IDisplaySurface surface) { - this.surface = surface; - if (surface != null) { surface.setDisplaySynchronizer(this); } - + @Override + public void waitForRenderingToBeFinished() { + // DEBUG.OUT("Waiting for surface to be rendered: " + Thread.currentThread().getName()); + try { + renderQueue.take(); + } catch (final InterruptedException e) {} } @Override - public void waitForSurfaceToBeRealized() { - DEBUG.OUT("Waiting for surface to realize: " + Thread.currentThread().getName()); + public void waitForViewUpdateAuthorisation() { + // DEBUG.OUT("Waiting for view to update: " + Thread.currentThread().getName()); try { - surfaceRealisationLock.acquire(); + updateQueue.take(); } catch (InterruptedException e) {} } @Override - public void signalSurfaceIsRealized() { - DEBUG.OUT("Signalling that surface is realized: " + Thread.currentThread().getName()); - surfaceRealisationLock.release(); + public void authorizeViewUpdate() { + // DEBUG.OUT("Signalling that view can be updated: " + Thread.currentThread().getName()); + updateQueue.offer(TOKEN); } } diff --git a/ummisco.gama.ui.experiment/src/ummisco/gama/ui/views/displays/LayeredDisplayView.java b/ummisco.gama.ui.experiment/src/ummisco/gama/ui/views/displays/LayeredDisplayView.java index a3b02310e6..cf3369b565 100644 --- a/ummisco.gama.ui.experiment/src/ummisco/gama/ui/views/displays/LayeredDisplayView.java +++ b/ummisco.gama.ui.experiment/src/ummisco/gama/ui/views/displays/LayeredDisplayView.java @@ -32,6 +32,7 @@ import org.eclipse.ui.PartInitException; import msi.gama.common.interfaces.IDisplaySurface; +import msi.gama.common.interfaces.IDisplaySynchronizer; import msi.gama.common.interfaces.IDisposable; import msi.gama.common.interfaces.IGamaView; import msi.gama.common.interfaces.ILayerManager; @@ -74,7 +75,8 @@ public abstract class LayeredDisplayView extends GamaViewPart public LayeredDisplayDecorator decorator; /** The synchronizer. */ - public final LayeredDisplaySynchronizer synchronizer = new LayeredDisplaySynchronizer(); + public final IDisplaySynchronizer synchronizer = new LayeredDisplaySynchronizer(); + // FLAGS.USE_OLD_SYNC_STRATEGY?new LayeredDisplayLegacySynchronizer():new LayeredDisplayNewSynchronizer(); /** The disposed. */ public volatile boolean disposed = false; @@ -395,9 +397,19 @@ public void update(final IDisplayOutput out) { updateThread.start(); } synchronizer.authorizeViewUpdate(); - if (!inInitPhase && out.isSynchronized()) { synchronizer.waitForRenderingToBeFinished(); } + if (!inInitPhase && out.isSynchronized() && canBeSynchronized()) { + synchronizer.waitForRenderingToBeFinished(); + } } + /** + * Can be synchronized. Returns true if this can be synchronized: for instance, on macOS, hiding an OpenGL view will + * prevent it from rendering, and the simulation should not wait for this invisible visualisation. + * + * @return true, if successful + */ + protected abstract boolean canBeSynchronized(); + @Override public boolean zoomWhenScrolling() { return true; diff --git a/ummisco.gama.ui.experiment/src/ummisco/gama/ui/views/displays/SWTLayeredDisplayMultiListener.java b/ummisco.gama.ui.experiment/src/ummisco/gama/ui/views/displays/SWTLayeredDisplayMultiListener.java index 00e066c46f..9721876a06 100644 --- a/ummisco.gama.ui.experiment/src/ummisco/gama/ui/views/displays/SWTLayeredDisplayMultiListener.java +++ b/ummisco.gama.ui.experiment/src/ummisco/gama/ui/views/displays/SWTLayeredDisplayMultiListener.java @@ -1,16 +1,15 @@ /******************************************************************************************************* * - * SWTLayeredDisplayMultiListener.java, in ummisco.gama.ui.experiment, is part of the source code of the - * GAMA modeling and simulation platform (v.1.8.2). + * SWTLayeredDisplayMultiListener.java, in ummisco.gama.ui.experiment, is part of the source code of the GAMA modeling + * and simulation platform (v.1.8.2). * * (c) 2007-2022 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, TLU, CTU) * * Visit https://github.com/gama-platform/gama for license information and contacts. - * + * ********************************************************************************************************/ package ummisco.gama.ui.views.displays; -import java.util.Objects; import java.util.function.Supplier; import org.eclipse.swt.SWT; @@ -34,7 +33,6 @@ import msi.gama.common.interfaces.IDisposable; import ummisco.gama.dev.utils.DEBUG; import ummisco.gama.ui.bindings.GamaKeyBindings; -import ummisco.gama.ui.utils.WorkbenchHelper; /** * The listener interface for receiving SWTLayeredDisplayMulti events. The class that is interested in processing a @@ -78,12 +76,12 @@ public SWTLayeredDisplayMultiListener(final LayeredDisplayDecorator deco, final if (!viewOk) return false; final boolean controlOk = control != null && !control.isDisposed(); if (!controlOk) return false; - final boolean surfaceOk = surface != null && !surface.isDisposed(); - if (!control.isFocusControl()) { control.forceFocus(); } - if (!Objects.equals(WorkbenchHelper.getActivePart(), deco.view)) { - WorkbenchHelper.getPage().activate(deco.view); - } - return surfaceOk; + // Removed to prevent views from stealing the focus w/o control + // if (!control.isFocusControl()) { control.forceFocus(); } + // if (!Objects.equals(WorkbenchHelper.getActivePart(), deco.view)) { + // WorkbenchHelper.getPage().activate(deco.view); + // } + return surface != null && !surface.isDisposed(); }; control.addKeyListener(this); @@ -113,14 +111,14 @@ public void dispose() { @Override public void keyPressed(final KeyEvent e) { if (!ok.get()) return; - //DEBUG.OUT("Key pressed " + e); + // DEBUG.OUT("Key pressed " + e); delegate.keyPressed(e.character); } @Override public void keyReleased(final KeyEvent e) { if (!ok.get()) return; - //DEBUG.OUT("Key released " + e); + // DEBUG.OUT("Key released " + e); delegate.keyReleased(e.keyCode, GamaKeyBindings.ctrl(e)); } @@ -150,7 +148,7 @@ public void mouseHover(final MouseEvent e) { @Override public void mouseMove(final MouseEvent e) { if (!ok.get()) return; - //DEBUG.OUT("Mouse move " + e); + // DEBUG.OUT("Mouse move " + e); delegate.mouseMove(e.x, e.y, (e.stateMask & SWT.MODIFIER_MASK) != 0); } @@ -162,14 +160,14 @@ public void mouseDoubleClick(final MouseEvent e) { @Override public void mouseDown(final MouseEvent e) { if (!ok.get()) return; - //DEBUG.OUT("Mouse down " + e); + // DEBUG.OUT("Mouse down " + e); delegate.mouseDown(e.x, e.y, e.button, (e.stateMask & SWT.MODIFIER_MASK) != 0); } @Override public void mouseUp(final MouseEvent e) { if (!ok.get()) return; - //DEBUG.OUT("Mouse up " + e); + // DEBUG.OUT("Mouse up " + e); delegate.mouseUp(e.x, e.y, e.button, (e.stateMask & SWT.MODIFIER_MASK) != 0); } diff --git a/ummisco.gama.ui.shared/src/ummisco/gama/ui/views/ExpandableItemsView.java b/ummisco.gama.ui.shared/src/ummisco/gama/ui/views/ExpandableItemsView.java index 9d635315f6..8e0078e082 100644 --- a/ummisco.gama.ui.shared/src/ummisco/gama/ui/views/ExpandableItemsView.java +++ b/ummisco.gama.ui.shared/src/ummisco/gama/ui/views/ExpandableItemsView.java @@ -1,12 +1,12 @@ /******************************************************************************************************* * - * ExpandableItemsView.java, in ummisco.gama.ui.shared, is part of the source code of the - * GAMA modeling and simulation platform (v.1.8.2). + * ExpandableItemsView.java, in ummisco.gama.ui.shared, is part of the source code of the GAMA modeling and simulation + * platform (v.1.8.2). * * (c) 2007-2022 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, TLU, CTU) * * Visit https://github.com/gama-platform/gama for license information and contacts. - * + * ********************************************************************************************************/ package ummisco.gama.ui.views; @@ -242,7 +242,7 @@ public void reset() { @Override public void setFocus() { - if (viewer != null) { viewer.setFocus(); } + if (viewer != null && viewer.isVisible()) { viewer.setFocus(); } } @Override diff --git a/ummisco.gama.ui.shared/src/ummisco/gama/ui/views/OutputPartsManager.java b/ummisco.gama.ui.shared/src/ummisco/gama/ui/views/OutputPartsManager.java index 1999973567..961830c794 100644 --- a/ummisco.gama.ui.shared/src/ummisco/gama/ui/views/OutputPartsManager.java +++ b/ummisco.gama.ui.shared/src/ummisco/gama/ui/views/OutputPartsManager.java @@ -1,12 +1,12 @@ /******************************************************************************************************* * - * OutputPartsManager.java, in ummisco.gama.ui.shared, is part of the source code of the - * GAMA modeling and simulation platform (v.1.8.2). + * OutputPartsManager.java, in ummisco.gama.ui.shared, is part of the source code of the GAMA modeling and simulation + * platform (v.1.8.2). * * (c) 2007-2022 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, TLU, CTU) * * Visit https://github.com/gama-platform/gama for license information and contacts. - * + * ********************************************************************************************************/ package ummisco.gama.ui.views; @@ -16,6 +16,7 @@ import msi.gama.common.interfaces.IGamaView; import msi.gama.outputs.IDisplayOutput; +import ummisco.gama.dev.utils.DEBUG; import ummisco.gama.ui.utils.WorkbenchHelper; /** @@ -27,27 +28,31 @@ */ public class OutputPartsManager { + static { + DEBUG.OFF(); + } + /** - * The listener interface for receiving part events. - * The class that is interested in processing a part - * event implements this interface, and the object created - * with that class is registered with a component using the - * component's addPartListener method. When - * the part event occurs, that object's appropriate - * method is invoked. + * The listener interface for receiving part events. The class that is interested in processing a part event + * implements this interface, and the object created with that class is registered with a component using the + * component's addPartListener method. When the part event occurs, that object's appropriate method is + * invoked. * * @see PartEvent */ public static class PartListener implements IPartListener2 { @Override - public void partActivated(final IWorkbenchPartReference partRef) {} + public void partActivated(final IWorkbenchPartReference partRef) { + final IWorkbenchPart part = partRef.getPart(false); + //if (part instanceof IGamaView.Display) { DEBUG.LOG("Part Activated: " + part.getTitle()); } + } @Override public void partClosed(final IWorkbenchPartReference partRef) { - // DEBUG.LOG("Closed:" + partRef.getPartName()); final IWorkbenchPart part = partRef.getPart(false); if (part instanceof IGamaView) { + // DEBUG.LOG("Part Closed:" + part.getTitle()); final IDisplayOutput output = ((IGamaView) part).getOutput(); if (output != null) { output.setPaused(true); @@ -61,28 +66,47 @@ public void partDeactivated(final IWorkbenchPartReference partRef) {} @Override public void partOpened(final IWorkbenchPartReference partRef) { - // DEBUG.LOG("Opened:" + partRef.getPartName()); final IWorkbenchPart part = partRef.getPart(false); if (part instanceof IGamaView) { + DEBUG.LOG("Part Opened:" + part.getTitle()); final IDisplayOutput output = ((IGamaView) part).getOutput(); - if (output != null) { - if (!output.isOpen()) { - output.open(); - output.setPaused(false); - } + if (output != null && !output.isOpen()) { + output.open(); + output.setPaused(false); } } } @Override - public void partBroughtToTop(final IWorkbenchPartReference part) {} + public void partBroughtToTop(final IWorkbenchPartReference partRef) { + final IWorkbenchPart part = partRef.getPart(false); + //if (part instanceof IGamaView.Display) { DEBUG.LOG("Part Brought to top: " + part.getTitle()); } + } @Override - public void partHidden(final IWorkbenchPartReference partRef) {} + public void partHidden(final IWorkbenchPartReference partRef) { + //final IWorkbenchPart part = partRef.getPart(false); + // if (part instanceof IGamaView.Display display) { + // // See issue #3318 + // if (PlatformHelper.isMac() && display.hasTab()) { + // WorkbenchHelper.asyncRun(() -> display.hideCanvas()); + // } + // DEBUG.LOG("Part Hidden: " + part.getTitle()); + // } + } @Override - public void partVisible(final IWorkbenchPartReference partRef) {} + public void partVisible(final IWorkbenchPartReference partRef) { + //final IWorkbenchPart part = partRef.getPart(false); + // See issue #3318 + // if (part instanceof IGamaView.Display display) { + // if (PlatformHelper.isMac() && display.hasTab()) { + // WorkbenchHelper.asyncRun(() -> display.showCanvas()); + // } + // DEBUG.LOG("Part Visible: " + part.getTitle()); + // } + } @Override public void partInputChanged(final IWorkbenchPartReference partRef) {}