Jogamp
Bug 741 HiDPI: Add ScalableSurface interface to get/set pixelScale w/ full OSX impl.
authorSven Gothel <sgothel@jausoft.com>
Sun, 8 Jun 2014 06:11:57 +0000 (08:11 +0200)
committerSven Gothel <sgothel@jausoft.com>
Sun, 8 Jun 2014 06:11:57 +0000 (08:11 +0200)
Add ScalableSurface interface
  - To set pixelScale before and after realization
  - To get pixelScale

  - Implemented on:
    - NEWT Window
      - Generic impl. in WindowImpl
      - OSX WindowDriver impl.
        - Also propagetes pixelScale to parent JAWTWindow if offscreen (NewtCanvasAWT)
      - AWT WindowDriver impl.

    - JAWTWindow / OSXCalayer
      - AWT GLCanvas
      - AWT GLJPanel
      - NEWTCanvasAWT:
        - Propagates NEWT Window's pixelScale to underlying JAWTWindow

    - WrappedSurface for pixelScale propagation
      using offscreen drawables, i.e. GLJPanel

    - Generic helper in SurfaceScaleUtils (nativewindow package)

  - Fully implemented on OSX

  - Capable to switch pixelScale before realization,
    i.e. native-creation, as well as on-the-fly.

  - Impl. uses int[2] for pixelScale to support
    non-uniform scale.

Test cases:
  - com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT
  - com.jogamp.opengl.test.junit.jogl.demos.es2.awt.TestGearsES2AWT
  - com.jogamp.opengl.test.junit.jogl.demos.es2.awt.TestGearsES2GLJPanelAWT
  - com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NewtCanvasAWT

  - Press 'x' to toggle HiDPI
  - Commandline '-pixelScale <value>'
  - Added basic auto unit test (setting pre-realization)

25 files changed:
.gitignore
make/scripts/tests.sh
src/jogl/classes/javax/media/opengl/awt/GLCanvas.java
src/jogl/classes/javax/media/opengl/awt/GLJPanel.java
src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m
src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java
src/nativewindow/classes/javax/media/nativewindow/ScalableSurface.java [new file with mode: 0644]
src/nativewindow/classes/jogamp/nativewindow/SurfaceScaleUtils.java [new file with mode: 0644]
src/nativewindow/classes/jogamp/nativewindow/WrappedSurface.java
src/nativewindow/classes/jogamp/nativewindow/jawt/macosx/MacOSXJAWTWindow.java
src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java
src/nativewindow/native/macosx/OSXmisc.m
src/newt/classes/com/jogamp/newt/Window.java
src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java
src/newt/classes/com/jogamp/newt/opengl/GLWindow.java
src/newt/classes/jogamp/newt/WindowImpl.java
src/newt/classes/jogamp/newt/driver/awt/AWTCanvas.java
src/newt/classes/jogamp/newt/driver/awt/WindowDriver.java
src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java
src/newt/native/MacWindow.m
src/newt/native/NewtMacWindow.m
src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/awt/TestGearsES2AWT.java
src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/awt/TestGearsES2GLJPanelAWT.java
src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java
src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NewtCanvasAWT.java

index 1cd3375..71e3b7e 100644 (file)
@@ -2,6 +2,7 @@ build
 javadoc_public
 build-temp
 make/temp*
+temp/*
 .idea/workspace.xml
 .settings/*
 **~
index 1d2234a..3c79ebb 100644 (file)
@@ -206,7 +206,7 @@ function jrun() {
     #D_ARGS="-Dnewt.debug.EDT"
     #D_ARGS="-Dnewt.debug.Window -Dnewt.debug.Display -Dnewt.debug.EDT -Djogl.debug.GLContext"
     #D_ARGS="-Dnewt.debug.Window -Djogl.debug.Animator -Dnewt.debug.Screen"
-    #D_ARGS="-Dnativewindow.debug.JAWT -Dnewt.debug.Window"
+    #D_ARGS="-Dnativewindow.debug.JAWT -Dnewt.debug.Window -Djogl.debug.GLJPanel -Djogl.debug.GLCanvas"
     #D_ARGS="-Dnewt.debug.Window.KeyEvent"
     #D_ARGS="-Dnewt.debug.Window.MouseEvent"
     #D_ARGS="-Dnewt.debug.Window.MouseEvent -Dnewt.debug.Window.KeyEvent"
@@ -358,6 +358,15 @@ function testawtswt() {
 #testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestVersionSemanticsNOUI $*
 
 #
+# HiDPI
+#
+#testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT $*
+#testawt com.jogamp.opengl.test.junit.jogl.demos.es2.awt.TestGearsES2AWT $*
+#testawt com.jogamp.opengl.test.junit.jogl.demos.es2.awt.TestGearsES2GLJPanelAWT $*
+testawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NewtCanvasAWT $*
+#testawt com.jogamp.opengl.test.junit.jogl.awt.ManualHiDPIBufferedImage01AWT $*
+
+#
 # demos (any TK, more user driven tests)
 #
 #testnoawt com.jogamp.opengl.test.junit.jogl.demos.es1.newt.TestGearsES1NEWT $*
@@ -365,8 +374,7 @@ function testawtswt() {
 #testnoawt com.jogamp.opengl.test.junit.jogl.demos.es1.newt.TestRedSquareES1NEWT $*
 #testawt com.jogamp.opengl.test.junit.jogl.demos.es2.awt.TestGearsES2AWT $*
 #testawt com.jogamp.opengl.test.junit.jogl.demos.es2.awt.TestGearsES2GLJPanelAWT $*
-#testawt com.jogamp.opengl.test.junit.jogl.awt.TestHiDPIBufferedImage01AWT $*
-#testawt com.jogamp.opengl.test.junit.jogl.awt.TestHiDPIBufferedImage02AWT $*
+#testawt com.jogamp.opengl.test.junit.jogl.awt.ManualHiDPIBufferedImage01AWT $*
 #testawt com.jogamp.opengl.test.junit.jogl.demos.es2.awt.TestGearsES2GLJPanelsAWT $*
 #testawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NewtCanvasAWT $*
 #testawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestLandscapeES2NewtCanvasAWT $*
@@ -763,7 +771,7 @@ function testawtswt() {
 #testnoawt com.jogamp.opengl.test.junit.graph.demos.GPUTextNewtDemo $*
 #testnoawt com.jogamp.opengl.test.junit.graph.demos.GPURegionNewtDemo $*
 #testnoawt com.jogamp.opengl.test.junit.graph.demos.GPUUISceneNewtDemo $*
-testawt com.jogamp.opengl.test.junit.graph.demos.GPUUISceneNewtCanvasAWTDemo $*
+#testawt com.jogamp.opengl.test.junit.graph.demos.GPUUISceneNewtCanvasAWTDemo $*
 
 #testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.av.MovieCube $*
 #testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.av.MovieSimple $*
index d06b616..bdd12df 100644 (file)
@@ -63,6 +63,7 @@ import java.util.List;
 
 import javax.media.nativewindow.AbstractGraphicsConfiguration;
 import javax.media.nativewindow.OffscreenLayerOption;
+import javax.media.nativewindow.ScalableSurface;
 import javax.media.nativewindow.VisualIDHolder;
 import javax.media.nativewindow.WindowClosingProtocol;
 import javax.media.nativewindow.AbstractGraphicsDevice;
@@ -101,6 +102,7 @@ import com.jogamp.opengl.JoglVersion;
 import com.jogamp.opengl.util.GLDrawableUtil;
 import com.jogamp.opengl.util.TileRenderer;
 
+import jogamp.nativewindow.SurfaceScaleUtils;
 import jogamp.opengl.Debug;
 import jogamp.opengl.GLContextImpl;
 import jogamp.opengl.GLDrawableHelper;
@@ -156,7 +158,8 @@ import jogamp.opengl.awt.AWTTilePainter;
  */
 
 @SuppressWarnings("serial")
-public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosingProtocol, OffscreenLayerOption, AWTPrintLifecycle, GLSharedContextSetter {
+public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosingProtocol, OffscreenLayerOption,
+                                                AWTPrintLifecycle, GLSharedContextSetter, ScalableSurface {
 
   private static final boolean DEBUG = Debug.debug("GLCanvas");
 
@@ -167,7 +170,8 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
   private volatile JAWTWindow jawtWindow; // the JAWTWindow presentation of this AWT Canvas, bound to the 'drawable' lifecycle
   private volatile GLContextImpl context; // volatile: avoid locking for read-only access
   private volatile boolean sendReshape = false; // volatile: maybe written by EDT w/o locking
-  private volatile int pixelScale;
+  private volatile int[] hasPixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE };
+  final int[] reqPixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE };
 
   // copy of the cstr args, mainly for recreation
   private final GLCapabilitiesImmutable capsReqUser;
@@ -308,7 +312,6 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
 
     this.addHierarchyListener(hierarchyListener);
     this.isShowing = isShowing();
-    this.pixelScale = 1;
   }
 
   @Override
@@ -659,15 +662,45 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
     }
   }
 
+  @Override
+  public final void setSurfaceScale(final int[] pixelScale) {
+      SurfaceScaleUtils.validateReqPixelScale(reqPixelScale, pixelScale, DEBUG ? getClass().getSimpleName() : null);
+      if( isRealized() ) {
+          final ScalableSurface ns = jawtWindow;
+          if( null != ns ) {
+              ns.setSurfaceScale(reqPixelScale);
+              final int hadPixelScaleX = hasPixelScale[0];
+              final int hadPixelScaleY = hasPixelScale[1];
+              ns.getSurfaceScale(hasPixelScale);
+              if( hadPixelScaleX != hasPixelScale[0] || hadPixelScaleY != hasPixelScale[1] ) {
+                  reshapeImpl(getWidth(), getHeight());
+                  display();
+              }
+          }
+      }
+  }
+
+  @Override
+  public final int[] getSurfaceScale(final int[] result) {
+      final ScalableSurface ns = jawtWindow;
+      if( null != ns ) {
+          return ns.getSurfaceScale(result);
+      } else {
+          System.arraycopy(reqPixelScale, 0, result, 0, 2);
+          return result;
+      }
+  }
+
   private void createJAWTDrawableAndContext() {
     if ( !Beans.isDesignTime() ) {
         jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(this, awtConfig);
         jawtWindow.setShallUseOffscreenLayer(shallUseOffscreenLayer);
+        jawtWindow.setSurfaceScale(reqPixelScale);
         jawtWindow.lockSurface();
         try {
             drawable = (GLDrawableImpl) GLDrawableFactory.getFactory(capsReqUser.getGLProfile()).createGLDrawable(jawtWindow);
             createContextImpl(drawable);
-            pixelScale = jawtWindow.getPixelScale();
+            jawtWindow.getSurfaceScale(hasPixelScale);
         } finally {
             jawtWindow.unlockSurface();
         }
@@ -762,37 +795,39 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
   public void reshape(int x, int y, int width, int height) {
     synchronized (getTreeLock()) { // super.reshape(..) claims tree lock, so we do extend it's lock over reshape
         super.reshape(x, y, width, height);
+        reshapeImpl(width, height);
+    }
+  }
 
-        final int scale = getPixelScale();
-        final int scaledWidth = scale * width;
-        final int scaledHeight = scale * height;
+  private void reshapeImpl(final int width, final int height) {
+    final int scaledWidth = width * hasPixelScale[0];
+    final int scaledHeight = height * hasPixelScale[1];
 
-        if(DEBUG) {
-            final NativeSurface ns = getNativeSurface();
-            final long nsH = null != ns ? ns.getSurfaceHandle() : 0;
-            System.err.println(getThreadName()+": GLCanvas.reshape.0 "+this.getName()+" resize"+(printActive?"WithinPrint":"")+
-                    " [ this "+getWidth()+"x"+getHeight()+", pixelScale "+scale+
-                    "] -> "+(printActive?"[skipped] ":"") + width+"x"+height+" * "+scale+" -> "+scaledWidth+"x"+scaledHeight+
-                    " - surfaceHandle 0x"+Long.toHexString(nsH));
-            // Thread.dumpStack();
-        }
-        if( validateGLDrawable() && !printActive ) {
-            final GLDrawableImpl _drawable = drawable;
-            if( ! _drawable.getChosenGLCapabilities().isOnscreen() ) {
-                final RecursiveLock _lock = lock;
-                _lock.lock();
-                try {
-                    final GLDrawableImpl _drawableNew = GLDrawableHelper.resizeOffscreenDrawable(_drawable, context, scaledWidth, scaledHeight);
-                    if(_drawable != _drawableNew) {
-                        // write back
-                        drawable = _drawableNew;
-                    }
-                } finally {
-                   _lock.unlock();
+    if(DEBUG) {
+        final NativeSurface ns = getNativeSurface();
+        final long nsH = null != ns ? ns.getSurfaceHandle() : 0;
+        System.err.println(getThreadName()+": GLCanvas.reshape.0 "+this.getName()+" resize"+(printActive?"WithinPrint":"")+
+                " [ this "+getWidth()+"x"+getHeight()+", pixelScale "+getPixelScaleStr()+
+                "] -> "+(printActive?"[skipped] ":"") + width+"x"+height+" * "+getPixelScaleStr()+" -> "+scaledWidth+"x"+scaledHeight+
+                " - surfaceHandle 0x"+Long.toHexString(nsH));
+        // Thread.dumpStack();
+    }
+    if( validateGLDrawable() && !printActive ) {
+        final GLDrawableImpl _drawable = drawable;
+        if( ! _drawable.getChosenGLCapabilities().isOnscreen() ) {
+            final RecursiveLock _lock = lock;
+            _lock.lock();
+            try {
+                final GLDrawableImpl _drawableNew = GLDrawableHelper.resizeOffscreenDrawable(_drawable, context, scaledWidth, scaledHeight);
+                if(_drawable != _drawableNew) {
+                    // write back
+                    drawable = _drawableNew;
                 }
+            } finally {
+               _lock.unlock();
             }
-            sendReshape = true; // async if display() doesn't get called below, but avoiding deadlock
         }
+        sendReshape = true; // async if display() doesn't get called below, but avoiding deadlock
     }
   }
 
@@ -1133,12 +1168,12 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
 
   @Override
   public int getSurfaceWidth() {
-      return getWidth() * getPixelScale();
+      return getWidth() * hasPixelScale[0];
   }
 
   @Override
   public int getSurfaceHeight() {
-      return getHeight() * getPixelScale();
+      return getHeight() * hasPixelScale[1];
   }
 
   @Override
@@ -1185,7 +1220,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
   // Internals only below this point
   //
 
-  private final int getPixelScale() { return pixelScale; }
+  private final String getPixelScaleStr() { return hasPixelScale[0]+"x"+hasPixelScale[1]; }
 
   private final Runnable destroyOnEDTAction = new Runnable() {
     @Override
@@ -1269,7 +1304,8 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
             }
             jawtWindow=null;
         }
-        pixelScale = 1;
+        hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE;
+        hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE;
 
         if(null != awtConfig) {
             final AbstractGraphicsConfiguration aconfig = awtConfig.getNativeGraphicsConfiguration();
index e712f2f..18de5a7 100644 (file)
@@ -60,6 +60,7 @@ import java.util.List;
 
 import javax.media.nativewindow.AbstractGraphicsDevice;
 import javax.media.nativewindow.NativeSurface;
+import javax.media.nativewindow.ScalableSurface;
 import javax.media.nativewindow.SurfaceUpdatedListener;
 import javax.media.nativewindow.WindowClosingProtocol;
 import javax.media.opengl.GL;
@@ -83,6 +84,8 @@ import javax.media.opengl.GLSharedContextSetter;
 import javax.media.opengl.Threading;
 import javax.swing.JPanel;
 
+import jogamp.nativewindow.SurfaceScaleUtils;
+import jogamp.nativewindow.WrappedSurface;
 import jogamp.nativewindow.jawt.JAWTUtil;
 import jogamp.opengl.Debug;
 import jogamp.opengl.GLContextImpl;
@@ -169,7 +172,7 @@ import com.jogamp.opengl.util.texture.TextureState;
 */
 
 @SuppressWarnings("serial")
-public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosingProtocol, AWTPrintLifecycle, GLSharedContextSetter {
+public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosingProtocol, AWTPrintLifecycle, GLSharedContextSetter, ScalableSurface {
   private static final boolean DEBUG;
   private static final boolean DEBUG_VIEWPORT;
   private static final boolean USE_GLSL_TEXTURE_RASTERIZER;
@@ -244,7 +247,8 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
   private boolean handleReshape = false;
   private boolean sendReshape = true;
 
-  private volatile int pixelScale;
+  private volatile int[] hasPixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE };
+  private volatile int[] reqPixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE };
 
   // For handling reshape events lazily: reshapeWidth -> panelWidth -> backend.width
   private int reshapeWidth;
@@ -368,7 +372,6 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
     this.setFocusable(true); // allow keyboard input!
     this.addHierarchyListener(hierarchyListener);
     this.isShowing = isShowing();
-    this.pixelScale = 1;
   }
 
   /**
@@ -483,7 +486,8 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
         animator.resume();
       }
     }
-    pixelScale = 1;
+    hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE;
+    hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE;
 
     if(DEBUG) {
         System.err.println(getThreadName()+": GLJPanel.dispose() - stop");
@@ -552,6 +556,40 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
     }
   }
 
+  private final void updateWrappedSurfaceScale(final GLDrawable d) {
+      final NativeSurface s = d.getNativeSurface();
+      if( s instanceof WrappedSurface ) {
+          ((WrappedSurface)s).setSurfaceScale( hasPixelScale );
+      }
+  }
+
+  @Override
+  public final void setSurfaceScale(final int[] pixelScale) { // HiDPI support
+      SurfaceScaleUtils.validateReqPixelScale(reqPixelScale, pixelScale, DEBUG ? getClass().getSimpleName() : null);
+      final Backend b = backend;
+      if ( isInitialized && null != b ) {
+          final int[] pixelScaleInt;
+          {
+              final int ps = JAWTUtil.getPixelScale(getGraphicsConfiguration());
+              pixelScaleInt = new int[] { ps, ps };
+          }
+          final int hadPixelScaleX = hasPixelScale[0];
+          final int hadPixelScaleY = hasPixelScale[1];
+          SurfaceScaleUtils.computePixelScale(hasPixelScale, hasPixelScale, reqPixelScale, pixelScaleInt, DEBUG ? getClass().getSimpleName() : null);
+          if( hadPixelScaleX != hasPixelScale[0] || hadPixelScaleY != hasPixelScale[1] ) {
+              updateWrappedSurfaceScale(b.getDrawable());
+              reshapeImpl(getWidth(), getHeight());
+              display();
+          }
+      }
+  }
+
+  @Override
+  public final int[] getSurfaceScale(final int[] result) {
+      System.arraycopy(isInitialized ? hasPixelScale : reqPixelScale, 0, result, 0, 2);
+      return result;
+  }
+
   /** Overridden to track when this component is added to a container.
       Subclasses which override this method must call
       super.addNotify() in their addNotify() method in order to
@@ -562,10 +600,15 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
   public void addNotify() {
     super.addNotify();
     awtWindowClosingProtocol.addClosingListener();
+
+    // HiDPI support
+    final int[] pixelScaleInt;
     {
-        final int s = JAWTUtil.getPixelScale(getGraphicsConfiguration());
-        pixelScale = 0 < s ? s : 1;
+        final int ps = JAWTUtil.getPixelScale(getGraphicsConfiguration());
+        pixelScaleInt = new int[] { ps, ps };
     }
+    SurfaceScaleUtils.computePixelScale(hasPixelScale, hasPixelScale, reqPixelScale, pixelScaleInt, DEBUG ? getClass().getSimpleName() : null);
+
     if (DEBUG) {
         System.err.println(getThreadName()+": GLJPanel.addNotify()");
     }
@@ -596,16 +639,18 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
   @Override
   public void reshape(int x, int y, int width, int height) {
     super.reshape(x, y, width, height);
+    reshapeImpl(width, height);
+  }
 
-    final int scale = getPixelScale();
-    final int scaledWidth = scale * width;
-    final int scaledHeight = scale * height;
+  private void reshapeImpl(final int width, final int height) {
+    final int scaledWidth = width * hasPixelScale[0];
+    final int scaledHeight = height * hasPixelScale[1];
     if( DEBUG ) {
         System.err.println(getThreadName()+": GLJPanel.reshape.0 "+this.getName()+" resize"+(printActive?"WithinPrint":"")+
-                " [ this "+getWidth()+"x"+getHeight()+", pixelScale "+scale+
+                " [ this "+getWidth()+"x"+getHeight()+", pixelScale "+getPixelScaleStr()+
                 ", panel "+panelWidth+"x"+panelHeight +
                 ", reshape: " +reshapeWidth+"x"+reshapeHeight +
-                "] -> "+(printActive?"[skipped] ":"") + width+"x"+height+" * "+scale+" -> "+scaledWidth+"x"+scaledHeight);
+                "] -> "+(printActive?"[skipped] ":"") + width+"x"+height+" * "+getPixelScaleStr()+" -> "+scaledWidth+"x"+scaledHeight);
     }
     if( !printActive ) {
         reshapeWidth = scaledWidth;
@@ -724,17 +769,16 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
           // trigger reshape, i.e. gl-viewport and -listener - this component might got resized!
           final int awtWidth = GLJPanel.this.getWidth();
           final int awtHeight= GLJPanel.this.getHeight();
-          final int scale = getPixelScale();
-          final int scaledAWTWidth = scale * awtWidth;
-          final int scaledAWTHeight= scale * awtHeight;
+          final int scaledAWTWidth = awtWidth * hasPixelScale[0];
+          final int scaledAWTHeight= awtHeight * hasPixelScale[1];
           final GLDrawable drawable = GLJPanel.this.getDelegatedDrawable();
           if( scaledAWTWidth != panelWidth || scaledAWTHeight != panelHeight ||
               drawable.getSurfaceWidth() != panelWidth || drawable.getSurfaceHeight() != panelHeight ) {
               // -> !( awtSize == panelSize == drawableSize )
               if ( DEBUG ) {
-                  System.err.println(getThreadName()+": GLJPanel.releasePrintOnEDT.0: resizeWithinPrint panel " +panelWidth+"x"+panelHeight + " @ scale "+getPixelScale()+
+                  System.err.println(getThreadName()+": GLJPanel.releasePrintOnEDT.0: resizeWithinPrint panel " +panelWidth+"x"+panelHeight + " @ scale "+getPixelScaleStr()+
                           ", draw "+drawable.getSurfaceWidth()+"x"+drawable.getSurfaceHeight()+
-                          " -> " + awtWidth+"x"+awtHeight+" * "+scale+" -> "+scaledAWTWidth+"x"+scaledAWTHeight);
+                          " -> " + awtWidth+"x"+awtHeight+" * "+getPixelScaleStr()+" -> "+scaledAWTWidth+"x"+scaledAWTHeight);
               }
               reshapeWidth = scaledAWTWidth;
               reshapeHeight = scaledAWTHeight;
@@ -1020,12 +1064,12 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
 
   @Override
   public int getSurfaceWidth() {
-      return getWidth() * getPixelScale();
+      return panelWidth; // FIXME HiDPI: Accurate or: getWidth() * hasPixelScale[0];
   }
 
   @Override
   public int getSurfaceHeight() {
-      return getHeight() * getPixelScale();
+      return panelHeight; // FIXME HiDPI: Accurate or: getHeight() * hasPixelScale[1];
   }
 
   /**
@@ -1153,8 +1197,8 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
 
               if (DEBUG) {
                   System.err.println(getThreadName()+": GLJPanel.createAndInitializeBackend: " +
-                          panelWidth+"x"+panelHeight+" @ scale "+getPixelScale() + " -> " +
-                          reshapeWidth+"x"+reshapeHeight+" @ scale "+getPixelScale());
+                          panelWidth+"x"+panelHeight+" @ scale "+getPixelScaleStr() + " -> " +
+                          reshapeWidth+"x"+reshapeHeight+" @ scale "+getPixelScaleStr());
               }
               // Pull down reshapeWidth and reshapeHeight into panelWidth and
               // panelHeight eagerly in order to complete initialization, and
@@ -1182,7 +1226,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
     }
   }
 
-  private final int getPixelScale() { return pixelScale; }
+  private final String getPixelScaleStr() { return hasPixelScale[0]+"x"+hasPixelScale[1]; }
 
   @Override
   public WindowClosingMode getDefaultCloseOperation() {
@@ -1197,8 +1241,8 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
   private boolean handleReshape() {
     if (DEBUG) {
       System.err.println(getThreadName()+": GLJPanel.handleReshape: "+
-                         panelWidth+"x"+panelHeight+" @ scale "+getPixelScale() + " -> " +
-                         reshapeWidth+"x"+reshapeHeight+" @ scale "+getPixelScale());
+                         panelWidth+"x"+panelHeight+" @ scale "+getPixelScaleStr() + " -> " +
+                         reshapeWidth+"x"+reshapeHeight+" @ scale "+getPixelScaleStr());
     }
     panelWidth  = reshapeWidth;
     panelHeight = reshapeHeight;
@@ -1236,7 +1280,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
       }
       if (sendReshape) {
         if (DEBUG) {
-          System.err.println(getThreadName()+": GLJPanel.display: reshape(" + viewportX + "," + viewportY + " " + panelWidth + "x" + panelHeight + " @ scale "+getPixelScale()+")");
+          System.err.println(getThreadName()+": GLJPanel.display: reshape(" + viewportX + "," + viewportY + " " + panelWidth + "x" + panelHeight + " @ scale "+getPixelScaleStr()+")");
         }
         helper.reshape(GLJPanel.this, viewportX, viewportY, panelWidth, panelHeight);
         sendReshape = false;
@@ -1484,6 +1528,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
                                                     offscreenCaps,
                                                     chooser,
                                                     panelWidth, panelHeight);
+          updateWrappedSurfaceScale(offscreenDrawable);
           offscreenDrawable.setRealized(true);
           if( DEBUG ) {
               offscreenDrawable.getNativeSurface().addSurfaceUpdatedListener(new SurfaceUpdatedListener() {
@@ -1675,7 +1720,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
               System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" pixelBufferProvider isSingletonBufferProvider "+useSingletonBuffer+", 0x"+Integer.toHexString(pixelBufferProvider.hashCode())+", "+pixelBufferProvider.getClass().getSimpleName());
               System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" pixelBuffer 0x"+Integer.toHexString(pixelBuffer.hashCode())+", "+pixelBuffer+", alignment "+alignment);
               System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" flippedVertical "+flipVertical+", glslTextureRaster "+(null!=glslTextureRaster));
-              System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" panelSize "+panelWidth+"x"+panelHeight+" @ scale "+getPixelScale());
+              System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" panelSize "+panelWidth+"x"+panelHeight+" @ scale "+getPixelScaleStr());
           }
         }
         if( offscreenDrawable.getSurfaceWidth() != panelWidth || offscreenDrawable.getSurfaceHeight() != panelHeight ) {
@@ -1686,7 +1731,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
             !pixelBuffer.isDataBufferSource(alignedImage) ) {
             alignedImage = pixelBuffer.getAlignedImage(panelWidth, panelHeight);
             if(DEBUG) {
-                System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" new alignedImage "+alignedImage.getWidth()+"x"+alignedImage.getHeight()+" @ scale "+getPixelScale()+", "+alignedImage+", pixelBuffer "+pixelBuffer.width+"x"+pixelBuffer.height+", "+pixelBuffer);
+                System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" new alignedImage "+alignedImage.getWidth()+"x"+alignedImage.getHeight()+" @ scale "+getPixelScaleStr()+", "+alignedImage+", pixelBuffer "+pixelBuffer.width+"x"+pixelBuffer.height+", "+pixelBuffer);
             }
         }
         final IntBuffer readBackInts;
@@ -1813,7 +1858,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
             System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.doPaintComponent.drawImage: - frameCount "+frameCount);
         }
         // Draw resulting image in one shot
-        g.drawImage(alignedImage, 0, 0, alignedImage.getWidth()/getPixelScale(), alignedImage.getHeight()/getPixelScale(), null); // Null ImageObserver since image data is ready.
+        g.drawImage(alignedImage, 0, 0, alignedImage.getWidth()/hasPixelScale[0], alignedImage.getHeight()/hasPixelScale[1], null); // Null ImageObserver since image data is ready.
       }
       frameCount++;
     }
@@ -1832,10 +1877,11 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
                 // write back
                 _drawable = _drawableNew;
                 offscreenDrawable = _drawableNew;
+                updateWrappedSurfaceScale(offscreenDrawable);
             }
         }
         if (DEBUG) {
-            System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.handleReshape: " +panelWidth+"x"+panelHeight + " @ scale "+getPixelScale() + " -> " + _drawable.getSurfaceWidth()+"x"+_drawable.getSurfaceHeight());
+            System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.handleReshape: " +panelWidth+"x"+panelHeight + " @ scale "+getPixelScaleStr() + " -> " + _drawable.getSurfaceWidth()+"x"+_drawable.getSurfaceHeight());
         }
         panelWidth = _drawable.getSurfaceWidth();
         panelHeight = _drawable.getSurfaceHeight();
index fb6fd18..652b054 100644 (file)
@@ -292,13 +292,17 @@ static const GLfloat gl_verts[] = {
             gl_texCoords[i] = 0.0f;
         }
     }
-    if( _texWidth != _winWidth ) {
+    /** 
+     * Set via 
+     *   - OSXUtil_SetCALayerPixelScale0
+     *   - OSXUtil_AddCASublayer0 
 NS_DURING
-        // Available >= 10.7
-        [self setContentsScale: (CGFloat)_texWidth/(CGFloat)_winWidth];
+    // Available >= 10.7
+    [self setContentsScale: (CGFloat)_texWidth/(CGFloat)_winWidth];
 NS_HANDLER
 NS_ENDHANDLER
-    }
+    */
+
     parentPixelFmt = [_parentPixelFmt retain]; // until destruction
     glContext = [[MyNSOpenGLContext alloc] initWithFormat:parentPixelFmt shareContext:parentCtx];
     gl3ShaderProgramName = _gl3ShaderProgramName;
index 690d779..efe9409 100644 (file)
@@ -61,6 +61,7 @@ import javax.media.nativewindow.NativeWindow;
 import javax.media.nativewindow.NativeWindowException;
 import javax.media.nativewindow.OffscreenLayerOption;
 import javax.media.nativewindow.OffscreenLayerSurface;
+import javax.media.nativewindow.ScalableSurface;
 import javax.media.nativewindow.SurfaceUpdatedListener;
 import javax.media.nativewindow.util.Insets;
 import javax.media.nativewindow.util.InsetsImmutable;
@@ -70,13 +71,14 @@ import javax.media.nativewindow.util.PointImmutable;
 import javax.media.nativewindow.util.Rectangle;
 import javax.media.nativewindow.util.RectangleImmutable;
 
+import jogamp.nativewindow.SurfaceScaleUtils;
 import jogamp.nativewindow.SurfaceUpdatedHelper;
 import jogamp.nativewindow.awt.AWTMisc;
 import jogamp.nativewindow.jawt.JAWT;
 import jogamp.nativewindow.jawt.JAWTUtil;
 import jogamp.nativewindow.jawt.JAWT_Rectangle;
 
-public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, OffscreenLayerOption {
+public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, OffscreenLayerOption, ScalableSurface {
   protected static final boolean DEBUG = JAWTUtil.DEBUG;
 
   // user properties
@@ -98,7 +100,10 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface,
   protected Rectangle bounds;
   protected Insets insets;
   private volatile long offscreenSurfaceLayer;
-  private volatile int pixelScale;
+
+  private volatile int[] hasPixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE };
+  protected final int[] reqPixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE };
+
   private long drawable_old;
 
   /**
@@ -122,7 +127,6 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface,
     invalidate();
     this.isApplet = false;
     this.offscreenSurfaceLayer = 0;
-    this.pixelScale = 1;
   }
   private static String id(Object obj) { return ( null!=obj ? toHexString(obj.hashCode()) : "nil" ); }
   private String jawtStr() { return "JAWTWindow["+id(JAWTWindow.this)+"]"; }
@@ -260,22 +264,37 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface,
     drawable_old = 0;
     bounds = new Rectangle();
     insets = new Insets();
-    pixelScale = 1;
+    hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE;
+    hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE;
   }
   protected abstract void invalidateNative();
 
   /**
+   * {@inheritDoc}
+   * <p>
+   * Per default impl. only works for not yet {@link #isRealized() realized} instances,
+   * current exception OSX.
+   * </p>
+   */
+  @Override
+  public void setSurfaceScale(final int[] pixelScale) {
+      SurfaceScaleUtils.validateReqPixelScale(reqPixelScale, pixelScale, DEBUG ? getClass().getSimpleName() : null);
+  }
+
+  @Override
+  public final int[] getSurfaceScale(final int[] result) {
+      // 0 != drawable -> locked at least once !
+      System.arraycopy(0 != drawable ? hasPixelScale : reqPixelScale, 0, result, 0, 2);
+      return result;
+  }
+
+  /**
    * Updates bounds and pixelScale
+   * @return true if bounds or pixelScale has changed, otherwise false
    */
   protected final boolean updateLockedData(JAWT_Rectangle jawtBounds) {
     final Rectangle jb = new Rectangle(jawtBounds.getX(), jawtBounds.getY(), jawtBounds.getWidth(), jawtBounds.getHeight());
-    final int newPixelScale;
-    {
-        final int s = JAWTUtil.getPixelScale(config.getAWTGraphicsConfiguration());
-        newPixelScale = 0 < s ? s : 1;
-    }
     final boolean changedBounds = !bounds.equals(jb);
-    final boolean changedPixelScale = newPixelScale != pixelScale;
 
     if( changedBounds ) {
         if( DEBUG ) {
@@ -288,20 +307,31 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface,
             insets.set(contInsets.left, contInsets.right, contInsets.top, contInsets.bottom);
         }
     }
-    if( changedPixelScale ) {
-        if( DEBUG ) {
-            System.err.println("JAWTWindow.updatePixelScale: "+pixelScale+" -> "+newPixelScale);
-        }
-        pixelScale = newPixelScale;
+
+    return updatePixelScale() || changedBounds;
+  }
+
+  /**
+   * Update pixelScale
+   * @return true if pixelScale has changed, otherwise false
+   */
+  protected final boolean updatePixelScale() {
+    final int[] pixelScaleInt;
+    {
+        final int ps = JAWTUtil.getPixelScale(config.getAWTGraphicsConfiguration());
+        pixelScaleInt = new int[] { ps, ps };
     }
-    return changedBounds || changedPixelScale;
+    return SurfaceScaleUtils.computePixelScale(hasPixelScale, hasPixelScale, reqPixelScale, pixelScaleInt, DEBUG ? getClass().getSimpleName() : null);
   }
 
   /** @return the JAWT_DrawingSurfaceInfo's (JAWT_Rectangle) bounds, updated with lock */
   public final RectangleImmutable getBounds() { return bounds; }
 
-  /** @return the safe pixelScale value, i.e. never negative or zero. Updated with lock. */
-  public final int getPixelScale() { return pixelScale; }
+  /** @return the safe pixelScale value for x-direction, i.e. never negative or zero. Updated with lock. */
+  protected final int getPixelScaleX() { return hasPixelScale[0]; }
+
+  /** @return the safe pixelScale value for y-direction, i.e. never negative or zero. Updated with lock. */
+  protected final int getPixelScaleY() { return hasPixelScale[1]; }
 
   @Override
   public final InsetsImmutable getInsets() { return insets; }
@@ -625,12 +655,26 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface,
 
   @Override
   public final int getSurfaceWidth() {
-    return getWidth() * getPixelScale();
+    return getWidth() * getPixelScaleX();
   }
 
   @Override
   public final int getSurfaceHeight() {
-    return getHeight() * getPixelScale();
+    return getHeight() * getPixelScaleY();
+  }
+
+  @Override
+  public final int[] convertToWindowUnits(final int[] pixelUnitsAndResult) {
+      pixelUnitsAndResult[0] /= getPixelScaleX();
+      pixelUnitsAndResult[1] /= getPixelScaleY();
+      return pixelUnitsAndResult;
+  }
+
+  @Override
+  public final int[] convertToPixelUnits(final int[] windowUnitsAndResult) {
+      windowUnitsAndResult[0] *= getPixelScaleX();
+      windowUnitsAndResult[1] *= getPixelScaleY();
+      return windowUnitsAndResult;
   }
 
   @Override
@@ -651,22 +695,6 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface,
   }
 
   @Override
-  public final int[] convertToWindowUnits(final int[] pixelUnitsAndResult) {
-      final int scale = getPixelScale();
-      pixelUnitsAndResult[0] /= scale;
-      pixelUnitsAndResult[1] /= scale;
-      return pixelUnitsAndResult;
-  }
-
-  @Override
-  public final int[] convertToPixelUnits(final int[] windowUnitsAndResult) {
-      final int scale = getPixelScale();
-      windowUnitsAndResult[0] *= scale;
-      windowUnitsAndResult[1] *= scale;
-      return windowUnitsAndResult;
-  }
-
-  @Override
   public void destroy() {
     surfaceLock.lock();
     try {
@@ -810,7 +838,7 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface,
           append(", CA_LAYER: ").append(JAWTUtil.isJAWTUsingOffscreenLayer(jawt)).
           append(", isLayeredSurface ").append(isOffscreenLayerSurfaceEnabled()).
           append(", bounds ").append(bounds).append(", insets ").append(insets).
-          append(", pixelScale ").append(getPixelScale());
+          append(", pixelScale ").append(getPixelScaleX()).append("x").append(getPixelScaleY());
       } else {
           sb.append("JAWT n/a, bounds ").append(bounds).append(", insets ").append(insets);
       }
@@ -830,7 +858,7 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface,
                 ", bounds "+bounds+", insets "+insets
                 );
     sb.append(", window ["+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()+
-             "], pixels[x"+getPixelScale()+" -> "+getSurfaceWidth()+"x"+getSurfaceHeight()+"]"+
+             "], pixels[s "+getPixelScaleX()+"x"+getPixelScaleY()+" -> "+getSurfaceWidth()+"x"+getSurfaceHeight()+"]"+
               ", visible "+component.isVisible());
     sb.append(", lockedExt "+isSurfaceLockedByOtherThread()+
               ",\n\tconfig "+config+
diff --git a/src/nativewindow/classes/javax/media/nativewindow/ScalableSurface.java b/src/nativewindow/classes/javax/media/nativewindow/ScalableSurface.java
new file mode 100644 (file)
index 0000000..024f22b
--- /dev/null
@@ -0,0 +1,80 @@
+/**
+ * Copyright 2014 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ *    1. Redistributions of source code must retain the above copyright notice, this list of
+ *       conditions and the following disclaimer.
+ *
+ *    2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``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 JogAmp Community 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.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+
+package javax.media.nativewindow;
+
+/**
+ * Adding mutable surface pixel scale property to implementing class, usually to a {@link NativeSurface} implementation,
+ * see {@link #setSurfaceScale(int[])}.
+ */
+public interface ScalableSurface {
+  /** Setting surface-pixel-scale of {@value}, results in same pixel- and window-units. */
+  public static final int IDENTITY_PIXELSCALE = 1;
+  /** Setting surface-pixel-scale of {@value}, results in maximum platform dependent pixel-scale, i.e. pixel-units >> window-units where available. */
+  public static final int AUTOMAX_PIXELSCALE = 0;
+
+  /**
+   * Request a pixel scale in x- and y-direction for the associated {@link NativeSurface}.
+   * <p>
+   * Default pixel scale request for both directions is {@link #AUTOMAX_PIXELSCALE}.
+   * </p>
+   * <p>
+   * In case platform only supports uniform pixel scale, i.e. one scale for both directions,
+   * either {@link #AUTOMAX_PIXELSCALE} or the maximum requested pixel scale component is used.
+   * </p>
+   * <p>
+   * The <i>requested</i> pixel scale will be validated against platform limits before native scale-setup,
+   * i.e. clipped to {@link #IDENTITY_PIXELSCALE} if not supported or the platform maximum.
+   * </p>
+   * <p>
+   * The actual <i>realized</i> pixel scale values of the {@link NativeSurface}
+   * can be queried via {@link #getSurfaceScale(int[])} or
+   * computed via <code>surface.{@link NativeSurface#convertToPixelUnits(int[]) convertToPixelUnits}(new int[] { 1, 1 })</code>
+   * </p>
+   *
+   * @param pixelScale <i>requested</i> surface pixel scale int[2] values for x- and y-direction.
+   */
+  public void setSurfaceScale(final int[] pixelScale);
+
+  /**
+   * Returns the pixel scale of the associated {@link NativeSurface}.
+   * <p>
+   * In case the {@link NativeSurface} is not yet realized, method returns the
+   * requested pixel scale as validated via {@link #setSurfaceScale(int[])}
+   * if called earlier or the implementation default, usually {@link #AUTOMAX_PIXELSCALE}.
+   * </p>
+   * <p>
+   * In case the {@link NativeSurface} is already realized, method returns the
+   * actual used pixel scale.
+   * </p>
+   * @param result int[2] storage for the result
+   * @return the passed storage for chaining
+   */
+  public int[] getSurfaceScale(final int[] result);
+}
+
diff --git a/src/nativewindow/classes/jogamp/nativewindow/SurfaceScaleUtils.java b/src/nativewindow/classes/jogamp/nativewindow/SurfaceScaleUtils.java
new file mode 100644 (file)
index 0000000..22e67ec
--- /dev/null
@@ -0,0 +1,175 @@
+/**
+ * Copyright 2014 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ *    1. Redistributions of source code must retain the above copyright notice, this list of
+ *       conditions and the following disclaimer.
+ *
+ *    2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``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 JogAmp Community 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.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package jogamp.nativewindow;
+
+import javax.media.nativewindow.NativeWindowFactory;
+import javax.media.nativewindow.ScalableSurface;
+
+/**
+ * Basic {@link ScalableSurface} utility to validate and compute pixel-scale values.
+ */
+public class SurfaceScaleUtils {
+
+    private static final int[] PlatformMaxPixelScale;
+    private static final boolean PlatformUniformPixelScale;
+    private static final boolean PlatformPixelScaleSupported;
+
+    static {
+      if( NativeWindowFactory.TYPE_MACOSX == NativeWindowFactory.getNativeWindowType(true) ) {
+          PlatformMaxPixelScale = new int[] { jogamp.nativewindow.macosx.OSXUtil.MAX_PIXELSCALE, jogamp.nativewindow.macosx.OSXUtil.MAX_PIXELSCALE };
+          PlatformUniformPixelScale = true;
+          PlatformPixelScaleSupported = true;
+      } else {
+          PlatformMaxPixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE };
+          PlatformUniformPixelScale = false;
+          PlatformPixelScaleSupported = false;
+      }
+    }
+
+    /**
+     * Compute a new valid pixelScale to be used by {@link NativeSurface} implementations,
+     * based on the given request and surface's pixelScale
+     *
+     * @param result int[2] storage for result, maybe same as <code>prePixelScale</code> for in-place
+     * @param prePixelScale previous pixelScale
+     * @param reqPixelScale requested pixelScale, validated via {@link #validateReqPixelScale(int[], int, String)}.
+     * @param surfPixelScaleRaw raw surface pixelScale
+     * @param DEBUG_PREFIX if set, dumps debug info on stderr using this prefix
+     * @return true if pixelScale has changed, otherwise false
+     */
+    public static boolean computePixelScale(int[] result, final int[] prePixelScale, final int[] reqPixelScale, final int[] surfPixelScaleRaw, final String DEBUG_PREFIX) {
+        final int surfPixelScaleSafeX = 0 < surfPixelScaleRaw[0] ? surfPixelScaleRaw[0] : ScalableSurface.IDENTITY_PIXELSCALE;
+        final int surfPixelScaleSafeY = 0 < surfPixelScaleRaw[1] ? surfPixelScaleRaw[1] : ScalableSurface.IDENTITY_PIXELSCALE;
+        final boolean useHiDPI = ScalableSurface.IDENTITY_PIXELSCALE != reqPixelScale[0] || ScalableSurface.IDENTITY_PIXELSCALE != reqPixelScale[1];
+        final int prePixelScaleX = prePixelScale[0];
+        final int prePixelScaleY = prePixelScale[1];
+
+        if( useHiDPI ) {
+            result[0] = surfPixelScaleSafeX;
+            result[1] = surfPixelScaleSafeY;
+        } else {
+            result[0] = ScalableSurface.IDENTITY_PIXELSCALE;
+            result[1] = ScalableSurface.IDENTITY_PIXELSCALE;
+        }
+
+        if( result[0] != prePixelScaleX || result[1] != prePixelScaleY ) {
+            if( null != DEBUG_PREFIX ) {
+                System.err.println(DEBUG_PREFIX+".computePixelScale: useHiDPI "+useHiDPI+", ["+prePixelScaleX+"x"+prePixelScaleY+" (pre), "+
+                        reqPixelScale[0]+"x"+reqPixelScale[1]+" (req)] -> "+
+                        surfPixelScaleRaw[0]+"x"+surfPixelScaleRaw[1]+" (raw) -> "+
+                        surfPixelScaleSafeX+"x"+surfPixelScaleSafeY+" (safe) -> "+
+                        result[0]+"x"+result[1]+" (use)");
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Validate the given requested pixelScale value pair, i.e. clip it to the
+     * limits of {@link ScalableSurface#AUTOMAX_PIXELSCALE} and {@link #getPlatformMaxPixelScale(int[])}
+     * <p>
+     * To be used by {@link ScalableSurface#setSurfaceScale(int[])} implementations.
+     * </p>
+     *
+     * @param result int[2] storage for result
+     * @param reqPixelScale requested pixelScale
+     * @param DEBUG_PREFIX if set, dumps debug info on stderr using this prefix
+     */
+    public static void validateReqPixelScale(final int[] result, final int[] reqPixelScale, final String DEBUG_PREFIX) {
+        final int minPS = Math.min(reqPixelScale[0], reqPixelScale[1]);
+        if( ScalableSurface.AUTOMAX_PIXELSCALE >= minPS ) {
+            result[0] = ScalableSurface.AUTOMAX_PIXELSCALE;
+            result[1] = ScalableSurface.AUTOMAX_PIXELSCALE;
+        } else if( PlatformUniformPixelScale ) {
+            final int maxPS = Math.max(reqPixelScale[0], reqPixelScale[1]);
+            if( maxPS >= PlatformMaxPixelScale[0] ) {
+                result[0] = PlatformMaxPixelScale[0];
+                result[1] = PlatformMaxPixelScale[1];
+            } else {
+                result[0] = maxPS;
+                result[1] = maxPS;
+            }
+        } else {
+            if( reqPixelScale[0] >= PlatformMaxPixelScale[0] ) {
+                result[0] = PlatformMaxPixelScale[0];
+            } else {
+                result[0] = reqPixelScale[0];
+            }
+            if( reqPixelScale[1] >= PlatformMaxPixelScale[1] ) {
+                result[1] = PlatformMaxPixelScale[1];
+            } else {
+                result[1] = reqPixelScale[1];
+            }
+        }
+        if( null != DEBUG_PREFIX ) {
+            System.err.println(DEBUG_PREFIX+".validateReqPixelScale: ["+reqPixelScale[0]+"x"+reqPixelScale[1]+" (req), "+
+                    PlatformMaxPixelScale[0]+"x"+PlatformMaxPixelScale[1]+" (max)] -> "+
+                    result[0]+"x"+result[1]+" (valid)");
+        }
+    }
+
+    /**
+     * Replaces {@link ScalableSurface#AUTOMAX_PIXELSCALE} with {@link #getPlatformMaxPixelScale(int[])},
+     * for each component.
+     *
+     * @param pixelScale int[2] value array to be tested and replaced
+     */
+    public static void replaceAutoMaxWithPlatformMax(final int[] pixelScale) {
+        if( ScalableSurface.AUTOMAX_PIXELSCALE == pixelScale[0] ) {
+            pixelScale[0] = PlatformMaxPixelScale[0];
+        }
+        if( ScalableSurface.AUTOMAX_PIXELSCALE == pixelScale[1] ) {
+            pixelScale[1] = PlatformMaxPixelScale[1];
+        }
+    }
+
+    /**
+     * Returns the maximum platform pixelScale
+     */
+    public static int[] getPlatformMaxPixelScale(final int[] result) {
+        System.arraycopy(PlatformMaxPixelScale, 0, result, 0, 2);
+        return result;
+    }
+
+    /**
+     * Returns true if platform pixelScale is uniform, i.e. same scale factor for x- and y-direction, otherwise false.
+     */
+    public static boolean isPlatformPixelScaleUniform() {
+        return PlatformUniformPixelScale;
+    }
+
+    /**
+     * Returns whether the platform supports pixelScale
+     */
+    public static boolean isPlatformPixelScaleSupported() {
+        return PlatformPixelScaleSupported;
+    }
+
+}
index 902223c..1a87729 100644 (file)
@@ -31,6 +31,7 @@ package jogamp.nativewindow;
 import javax.media.nativewindow.AbstractGraphicsConfiguration;
 import javax.media.nativewindow.AbstractGraphicsDevice;
 import javax.media.nativewindow.ProxySurface;
+import javax.media.nativewindow.ScalableSurface;
 import javax.media.nativewindow.UpstreamSurfaceHook;
 
 import com.jogamp.nativewindow.UpstreamSurfaceHookMutableSize;
@@ -40,8 +41,9 @@ import com.jogamp.nativewindow.UpstreamSurfaceHookMutableSize;
  *
  * @see ProxySurface
  */
-public class WrappedSurface extends ProxySurfaceImpl {
-  protected long surfaceHandle;
+public class WrappedSurface extends ProxySurfaceImpl implements ScalableSurface {
+  private final int[] hasPixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE };
+  private long surfaceHandle;
 
   /**
    * Utilizes a {@link UpstreamSurfaceHook.MutableSize} to hold the size information,
@@ -76,6 +78,8 @@ public class WrappedSurface extends ProxySurfaceImpl {
   @Override
   protected void invalidateImpl() {
     surfaceHandle = 0;
+    hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE;
+    hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE;
   }
 
   @Override
@@ -97,14 +101,61 @@ public class WrappedSurface extends ProxySurfaceImpl {
   protected final void unlockSurfaceImpl() {
   }
 
+  /**
+   * {@inheritDoc}
+   * <p>
+   * {@link WrappedSurface}'s implementation uses the {@link #setSurfaceScale(int[]) given pixelScale} directly.
+   * </p>
+   */
   @Override
   public final int[] convertToWindowUnits(final int[] pixelUnitsAndResult) {
-      return pixelUnitsAndResult; // FIXME HiDPI: use 'pixelScale'
+      pixelUnitsAndResult[0] /= hasPixelScale[0];
+      pixelUnitsAndResult[1] /= hasPixelScale[1];
+      return pixelUnitsAndResult;
   }
 
+  /**
+   * {@inheritDoc}
+   * <p>
+   * {@link WrappedSurface}'s implementation uses the {@link #setSurfaceScale(int[]) given pixelScale} directly.
+   * </p>
+   */
   @Override
   public final int[] convertToPixelUnits(final int[] windowUnitsAndResult) {
-      return windowUnitsAndResult; // FIXME HiDPI: use 'pixelScale'
+      windowUnitsAndResult[0] *= hasPixelScale[0];
+      windowUnitsAndResult[1] *= hasPixelScale[1];
+      return windowUnitsAndResult;
+  }
+
+  /**
+   * {@inheritDoc}
+   * <p>
+   * {@link WrappedSurface}'s implementation is to simply pass the given pixelScale
+   * from the caller <i>down</i> to this instance without validation to be applied in the {@link #convertToPixelUnits(int[]) conversion} {@link #convertToWindowUnits(int[]) methods} <b>only</b>.<br/>
+   * This allows the caller to pass down knowledge about window- and pixel-unit conversion and utilize mentioned conversion methods.
+   * </p>
+   * <p>
+   * The given pixelScale will not impact the actual {@link #getSurfaceWidth()} and {@link #getSurfaceHeight()},
+   * which is determinated by this instances {@link #getUpstreamSurface() upstream surface}.
+   * </p>
+   * <p>
+   * Implementation uses the default pixelScale {@link ScalableSurface#IDENTITY_PIXELSCALE}
+   * and resets to default values on {@link #invalidateImpl()}, i.e. {@link #destroyNotify()}.
+   * </p>
+   * <p>
+   * Implementation returns the given pixelScale array.
+   * </p>
+   */
+  @Override
+  public final void setSurfaceScale(final int[] pixelScale) {
+      hasPixelScale[0] = pixelScale[0];
+      hasPixelScale[1] = pixelScale[1];
+  }
+
+  @Override
+  public final int[] getSurfaceScale(final int[] result) {
+      System.arraycopy(hasPixelScale, 0, result, 0, 2);
+      return result;
   }
 
 }
\ No newline at end of file
index 3b91fc8..3d88049 100644 (file)
@@ -114,6 +114,27 @@ public class MacOSXJAWTWindow extends JAWTWindow implements MutableSurface {
   }
 
   @Override
+  public void setSurfaceScale(final int[] pixelScale) {
+      super.setSurfaceScale(pixelScale);
+      if( 0 != drawable ) { // locked at least once !
+          final int hadPixelScaleX = getPixelScaleX();
+          updatePixelScale();
+
+          if( hadPixelScaleX != getPixelScaleX() && 0 != getAttachedSurfaceLayer() ) {
+              OSXUtil.RunOnMainThread(false, new Runnable() {
+                  @Override
+                  public void run() {
+                      final long osl = getAttachedSurfaceLayer();
+                      if( 0 != osl ) {
+                          OSXUtil.SetCALayerPixelScale(rootSurfaceLayer, osl, getPixelScaleX());
+                      }
+                  }
+              });
+          }
+      }
+  }
+
+  @Override
   protected void attachSurfaceLayerImpl(final long layerHandle) {
       OSXUtil.RunOnMainThread(false, new Runnable() {
           @Override
@@ -143,7 +164,8 @@ public class MacOSXJAWTWindow extends JAWTWindow implements MutableSurface {
               } else if( DEBUG ) {
                   System.err.println("JAWTWindow.attachSurfaceLayerImpl: "+toHexString(layerHandle) + ", [ins "+outterInsets+"], p0 "+p0+" -> "+p1+", bounds "+bounds);
               }
-              OSXUtil.AddCASublayer(rootSurfaceLayer, layerHandle, p1.getX(), p1.getY(), getWidth(), getHeight(), getPixelScale(), JAWTUtil.getOSXCALayerQuirks());
+              // HiDPI: uniform pixel scale
+              OSXUtil.AddCASublayer(rootSurfaceLayer, layerHandle, p1.getX(), p1.getY(), getWidth(), getHeight(), getPixelScaleX(), JAWTUtil.getOSXCALayerQuirks());
           } } );
   }
 
@@ -309,7 +331,7 @@ public class MacOSXJAWTWindow extends JAWTWindow implements MutableSurface {
                 public void run() {
                     String errMsg = null;
                     if(0 == rootSurfaceLayer && 0 != jawtSurfaceLayersHandle) {
-                        rootSurfaceLayer = OSXUtil.CreateCALayer(bounds.getWidth(), bounds.getHeight(), getPixelScale());
+                        rootSurfaceLayer = OSXUtil.CreateCALayer(bounds.getWidth(), bounds.getHeight(), getPixelScaleX()); // HiDPI: uniform pixel scale
                         if(0 == rootSurfaceLayer) {
                           errMsg = "Could not create root CALayer";
                         } else {
index 12e574c..a91698f 100644 (file)
@@ -44,6 +44,9 @@ public class OSXUtil implements ToolkitProperties {
     private static boolean isInit = false;
     private static final boolean DEBUG = Debug.debug("OSXUtil");
 
+    /** FIXME HiDPI: OSX unique and maximum value {@value} */
+    public static final int MAX_PIXELSCALE = 2;
+
     /**
      * Called by {@link NativeWindowFactory#initSingleton()}
      * @see ToolkitProperties
@@ -201,6 +204,20 @@ public class OSXUtil implements ToolkitProperties {
     }
 
     /**
+     * Set root and sub CALayer pixelScale / contentScale for HiDPI
+     *
+     * @param rootCALayer the root surface layer, maybe null.
+     * @param subCALayer the client surface layer, maybe null.
+     * @param contentsScale scale for HiDPI support: pixel-dim = window-dim x scale
+     */
+    public static void SetCALayerPixelScale(final long rootCALayer, final long subCALayer, final float contentsScale) {
+        if( 0==rootCALayer && 0==subCALayer ) {
+            return;
+        }
+        SetCALayerPixelScale0(rootCALayer, subCALayer, contentsScale);
+    }
+
+    /**
      * Detach a sub CALayer from the root CALayer.
      */
     public static void RemoveCASublayer(final long rootCALayer, final long subCALayer) {
@@ -364,6 +381,7 @@ public class OSXUtil implements ToolkitProperties {
     private static native long CreateCALayer0(int width, int height, float contentsScale);
     private static native void AddCASublayer0(long rootCALayer, long subCALayer, int x, int y, int width, int height, float contentsScale, int caLayerQuirks);
     private static native void FixCALayerLayout0(long rootCALayer, long subCALayer, boolean visible, int x, int y, int width, int height, int caLayerQuirks);
+    private static native void SetCALayerPixelScale0(long rootCALayer, long subCALayer, float contentsScale);
     private static native void RemoveCASublayer0(long rootCALayer, long subCALayer);
     private static native void DestroyCALayer0(long caLayer);
     private static native void RunOnMainThread0(Runnable runnable);
index 62d3d67..fa3bf02 100644 (file)
@@ -639,13 +639,11 @@ JNIEXPORT jlong JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_CreateCALayer0
     if(0 == width) { width = 32; }
     if(0 == height) { height = 32; }
 
-    if( 1.0 != contentsScale ) {
 NS_DURING
-        // Available >= 10.7
-        [layer setContentsScale: (CGFloat)contentsScale];
+    // Available >= 10.7
+    [layer setContentsScale: (CGFloat)contentsScale];
 NS_HANDLER
 NS_ENDHANDLER
-    }
 
     // initial dummy size !
     CGRect lFrame = [layer frame];
@@ -692,12 +690,10 @@ JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_AddCASublayer0
         caLayerQuirks, rootLayer, (int)[rootLayer retainCount], subLayer, (int)[subLayer retainCount],
         lRectRoot.origin.x, lRectRoot.origin.y, lRectRoot.size.width, lRectRoot.size.height, (float)contentsScale);
 
-    if( 1.0 != contentsScale ) {
 NS_DURING
-        [subLayer setContentsScale: (CGFloat)contentsScale];
+    [subLayer setContentsScale: (CGFloat)contentsScale];
 NS_HANDLER
 NS_ENDHANDLER
-    }
 
     [subLayer setFrame:lRectRoot];
     [rootLayer addSublayer:subLayer];
@@ -753,6 +749,37 @@ JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_FixCALayerLayout0
 
 /*
  * Class:     Java_jogamp_nativewindow_macosx_OSXUtil
+ * Method:    SetCALayerPixelScale0
+ * Signature: (JJF)V
+ */
+JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_SetCALayerPixelScale0
+  (JNIEnv *env, jclass unused, jlong rootCALayer, jlong subCALayer, jfloat contentsScale)
+{
+    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+    MyCALayer* rootLayer = (MyCALayer*) ((intptr_t) rootCALayer);
+    if( NULL == rootLayer ) {
+        NativewindowCommon_throwNewRuntimeException(env, "Argument \"rootLayer\" is null");
+    }
+    CALayer* subLayer = (CALayer*) ((intptr_t) subCALayer);
+
+    [CATransaction begin];
+    [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
+
+NS_DURING
+    [rootLayer setContentsScale: (CGFloat)contentsScale];
+    if( NULL != subLayer ) {
+        [subLayer setContentsScale: (CGFloat)contentsScale];
+    }
+NS_HANDLER
+NS_ENDHANDLER
+
+    [CATransaction commit];
+
+    [pool release];
+}
+
+/*
+ * Class:     Java_jogamp_nativewindow_macosx_OSXUtil
  * Method:    RemoveCASublayer0
  * Signature: (JJ)V
  */
index 82199e9..88134f3 100644 (file)
@@ -45,6 +45,7 @@ import jogamp.newt.WindowImpl;
 import javax.media.nativewindow.CapabilitiesChooser;
 import javax.media.nativewindow.CapabilitiesImmutable;
 import javax.media.nativewindow.NativeWindow;
+import javax.media.nativewindow.ScalableSurface;
 import javax.media.nativewindow.WindowClosingProtocol;
 import javax.media.nativewindow.util.Rectangle;
 import javax.media.nativewindow.util.RectangleImmutable;
@@ -94,7 +95,7 @@ import javax.media.nativewindow.util.RectangleImmutable;
  * </pre>
  * </p>
  */
-public interface Window extends NativeWindow, WindowClosingProtocol {
+public interface Window extends NativeWindow, WindowClosingProtocol, ScalableSurface {
     public static final boolean DEBUG_MOUSE_EVENT = Debug.debug("Window.MouseEvent");
     public static final boolean DEBUG_KEY_EVENT = Debug.debug("Window.KeyEvent");
     public static final boolean DEBUG_IMPLEMENTATION = Debug.debug("Window");
index 37e9f98..baaa69e 100644 (file)
@@ -865,6 +865,7 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto
       }
       newtChild.setVisible(false);
       newtChild.setSize(w, h);
+      jawtWindow.setSurfaceScale(newtChild.getSurfaceScale(new int[2]));
       newtChild.reparentWindow(jawtWindow, -1, -1, Window.REPARENT_HINT_BECOMES_VISIBLE);
       newtChild.addSurfaceUpdatedListener(jawtWindow);
       if( jawtWindow.isOffscreenLayerSurfaceEnabled() &&
index a61085f..6610bd7 100644 (file)
@@ -404,6 +404,16 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind
     }
 
     @Override
+    public final void setSurfaceScale(final int[] pixelScale) {
+        window.setSurfaceScale(pixelScale);
+    }
+
+    @Override
+    public final int[] getSurfaceScale(final int[] result) {
+        return window.getSurfaceScale(result);
+    }
+
+    @Override
     public final void setPosition(int x, int y) {
         window.setPosition(x, y);
     }
index 5288bfc..f02b974 100644 (file)
@@ -48,6 +48,7 @@ import javax.media.nativewindow.NativeWindow;
 import javax.media.nativewindow.NativeWindowException;
 import javax.media.nativewindow.NativeWindowFactory;
 import javax.media.nativewindow.OffscreenLayerSurface;
+import javax.media.nativewindow.ScalableSurface;
 import javax.media.nativewindow.SurfaceUpdatedListener;
 import javax.media.nativewindow.WindowClosingProtocol;
 import javax.media.nativewindow.util.DimensionImmutable;
@@ -59,6 +60,7 @@ import javax.media.nativewindow.util.PointImmutable;
 import javax.media.nativewindow.util.Rectangle;
 import javax.media.nativewindow.util.RectangleImmutable;
 
+import jogamp.nativewindow.SurfaceScaleUtils;
 import jogamp.nativewindow.SurfaceUpdatedHelper;
 
 import com.jogamp.common.util.ArrayHashSet;
@@ -150,6 +152,9 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer
     private volatile boolean hasFocus = false;
     private volatile int pixWidth = 128, pixHeight = 128; // client-area size w/o insets in pixel units, default: may be overwritten by user
     private volatile int winWidth = 128, winHeight = 128; // client-area size w/o insets in window units, default: may be overwritten by user
+    protected int[] hasPixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE };
+    protected int[] reqPixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE };
+
     private volatile int x = 64, y = 64; // client-area pos w/o insets in window units
     private volatile Insets insets = new Insets(); // insets of decoration (if top-level && decorated)
     private boolean blockInsetsChange = false; // block insets change (from same thread)
@@ -1953,9 +1958,25 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer
     }
 
     /** HiDPI: We currently base scaling of window units to pixel units on an integer scale factor per component. */
-    protected int getPixelScaleX() { return 1; }
+    protected final int getPixelScaleX() {
+        return hasPixelScale[0];
+    }
+
     /** HiDPI: We currently base scaling of window units to pixel units on an integer scale factor per component. */
-    protected int getPixelScaleY() { return 1; }
+    protected final int getPixelScaleY() {
+        return hasPixelScale[1];
+    }
+
+    @Override
+    public void setSurfaceScale(final int[] pixelScale) {
+        SurfaceScaleUtils.validateReqPixelScale(reqPixelScale, pixelScale, DEBUG_IMPLEMENTATION ? getClass().getSimpleName() : null);
+    }
+
+    @Override
+    public final int[] getSurfaceScale(final int[] result) {
+        System.arraycopy(isNativeValid() ? hasPixelScale : reqPixelScale, 0, result, 0, 2);
+        return result;
+    }
 
     protected final boolean autoPosition() { return autoPosition; }
 
index 5dab64e..d01a2f2 100644 (file)
@@ -66,10 +66,16 @@ public class AWTCanvas extends Canvas {
   private volatile JAWTWindow jawtWindow=null; // the JAWTWindow presentation of this AWT Canvas, bound to the 'drawable' lifecycle
   private CapabilitiesChooser chooser=null;
   private final CapabilitiesImmutable capabilities;
+  private final UpstreamScalable upstreamScale;
+
+  public static interface UpstreamScalable {
+      int[] getReqPixelScale();
+      void setHasPixelScale(final int[] pixelScale);
+  }
 
   private boolean displayConfigChanged=false;
 
-  public AWTCanvas(CapabilitiesImmutable capabilities, CapabilitiesChooser chooser) {
+  public AWTCanvas(CapabilitiesImmutable capabilities, CapabilitiesChooser chooser, UpstreamScalable upstreamScale) {
     super();
 
     if(null==capabilities) {
@@ -77,6 +83,7 @@ public class AWTCanvas extends Canvas {
     }
     this.capabilities=capabilities;
     this.chooser=chooser;
+    this.upstreamScale = upstreamScale;
   }
 
   public AWTGraphicsConfiguration getAWTGraphicsConfiguration() {
@@ -139,7 +146,9 @@ public class AWTCanvas extends Canvas {
     {
         jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(this, awtConfig);
         // trigger initialization cycle
+        jawtWindow.setSurfaceScale( upstreamScale.getReqPixelScale() );
         jawtWindow.lockSurface();
+        upstreamScale.setHasPixelScale(jawtWindow.getSurfaceScale(new int[2]));
         jawtWindow.unlockSurface();
     }
 
@@ -152,10 +161,6 @@ public class AWTCanvas extends Canvas {
     }
   }
 
-  public int getPixelScale() {
-    final JAWTWindow _jawtWindow = jawtWindow;
-    return (null != _jawtWindow) ? _jawtWindow.getPixelScale() : 1;
-  }
   public NativeWindow getNativeWindow() {
     final JAWTWindow _jawtWindow = jawtWindow;
     return (null != _jawtWindow) ? _jawtWindow : null;
index cc92c49..4064fdb 100644 (file)
@@ -98,16 +98,17 @@ public class WindowDriver extends WindowImpl {
         }
     }
 
-    @Override
-    protected final int getPixelScaleX() {
-        final AWTCanvas _awtCanvas = awtCanvas;
-        return null != _awtCanvas ? _awtCanvas.getPixelScale() : 1;
-    }
+    private final AWTCanvas.UpstreamScalable upstreamScalable = new AWTCanvas.UpstreamScalable() {
+        @Override
+        public int[] getReqPixelScale() {
+            return WindowDriver.this.reqPixelScale;
+        }
 
-    @Override
-    protected final int getPixelScaleY() {
-        return getPixelScaleX();
-    }
+        @Override
+        public void setHasPixelScale(final int[] pixelScale) {
+            System.arraycopy(pixelScale, 0, WindowDriver.this.hasPixelScale, 0, 2);
+        }
+    };
 
     @Override
     protected void createNativeImpl() {
@@ -130,7 +131,7 @@ public class WindowDriver extends WindowImpl {
         awtContainer.setLayout(new BorderLayout());
 
         if( null == awtCanvas ) {
-            awtCanvas = new AWTCanvas(capsRequested, WindowDriver.this.capabilitiesChooser);
+            awtCanvas = new AWTCanvas(capsRequested, WindowDriver.this.capabilitiesChooser, upstreamScalable);
 
             // canvas.addComponentListener(listener);
             awtContainer.add(awtCanvas, BorderLayout.CENTER);
index cd852bb..0fa4739 100644 (file)
@@ -39,11 +39,13 @@ import javax.media.nativewindow.GraphicsConfigurationFactory;
 import javax.media.nativewindow.NativeWindow;
 import javax.media.nativewindow.NativeWindowException;
 import javax.media.nativewindow.MutableSurface;
+import javax.media.nativewindow.ScalableSurface;
 import javax.media.nativewindow.VisualIDHolder;
 import javax.media.nativewindow.util.Insets;
 import javax.media.nativewindow.util.Point;
 import javax.media.nativewindow.util.PointImmutable;
 
+import jogamp.nativewindow.SurfaceScaleUtils;
 import jogamp.nativewindow.macosx.OSXUtil;
 import jogamp.newt.PointerIconImpl;
 import jogamp.newt.ScreenImpl;
@@ -62,43 +64,42 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl
         DisplayDriver.initSingleton();
     }
 
-    private int pixelScale;
-
     public WindowDriver() {
-        pixelScale = 1;
     }
 
-    private boolean updatePixelScale(final boolean sendEvent, final boolean defer, final float newPixelScaleRaw) {
-        final int newPixelScaleSafe = FloatUtil.isZero(newPixelScaleRaw, FloatUtil.EPSILON) ? 1 : (int) newPixelScaleRaw;
-        final boolean changed = pixelScale != newPixelScaleSafe;
-        if( DEBUG_IMPLEMENTATION ) {
-            System.err.println("WindowDriver.updatePixelScale.X: "+pixelScale+" -> "+newPixelScaleSafe+" (raw "+newPixelScaleRaw+") - changed "+changed);
+    private boolean updatePixelScale(final boolean sendEvent, final boolean defer, final float pixelScaleRaw) {
+        final int[] pixelScaleInt;
+        {
+            final int ps = FloatUtil.isZero(pixelScaleRaw, FloatUtil.EPSILON) ? 1 : (int) pixelScaleRaw;
+            pixelScaleInt = new int[] { ps, ps };
         }
-        if( changed ) {
-            pixelScale = newPixelScaleSafe;
+
+        if( SurfaceScaleUtils.computePixelScale(hasPixelScale, hasPixelScale, reqPixelScale, pixelScaleInt, DEBUG_IMPLEMENTATION ? getClass().getName() : null) ) {
             if( sendEvent ) {
                 super.sizeChanged(defer, getWidth(), getHeight(), true);
             } else {
                 defineSize(getWidth(), getHeight());
             }
+            return true;
+        } else {
+            return false;
         }
-        return changed;
     }
 
     private boolean updatePixelScaleByScreenIdx(final boolean sendEvent) {
         final float newPixelScaleRaw = (float) OSXUtil.GetPixelScale(getScreen().getIndex());
         if( DEBUG_IMPLEMENTATION ) {
-            System.err.println("WindowDriver.updatePixelScale.1: "+pixelScale+" -> "+newPixelScaleRaw);
+            System.err.println("WindowDriver.updatePixelScale.1: "+hasPixelScale[0]+" -> "+newPixelScaleRaw);
         }
         return updatePixelScale(sendEvent, true /* defer */, newPixelScaleRaw);
     }
 
     private boolean updatePixelScaleByWindowHandle(final boolean sendEvent) {
-        final long wh = getWindowHandle();
-        if( 0 != wh ) {
-            final float newPixelScaleRaw = (float)OSXUtil.GetPixelScale(wh);
+        final long handle = getWindowHandle();
+        if( 0 != handle ) {
+            final float newPixelScaleRaw = (float)OSXUtil.GetPixelScale(handle);
             if( DEBUG_IMPLEMENTATION ) {
-                System.err.println("WindowDriver.updatePixelScale.2: "+pixelScale+" -> "+newPixelScaleRaw);
+                System.err.println("WindowDriver.updatePixelScale.2: "+hasPixelScale[0]+" -> "+newPixelScaleRaw);
             }
             return updatePixelScale(sendEvent, true /* defer */, newPixelScaleRaw);
         } else {
@@ -109,10 +110,10 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl
     /** Called from native code */
     protected void updatePixelScale(final boolean defer, final float newPixelScaleRaw) {
         final long handle = getWindowHandle();
+        if( DEBUG_IMPLEMENTATION ) {
+            System.err.println("WindowDriver.updatePixelScale.3: "+hasPixelScale[0]+" (has) -> "+newPixelScaleRaw+" (raw), drop "+(0==handle));
+        }
         if( 0 != handle ) {
-            if( DEBUG_IMPLEMENTATION ) {
-                System.err.println("WindowDriver.updatePixelScale.3: "+pixelScale+" -> "+newPixelScaleRaw);
-            }
             updatePixelScale(true /* sendEvent*/, defer, newPixelScaleRaw);
         }
     }
@@ -134,13 +135,43 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl
     }
 
     @Override
-    protected final int getPixelScaleX() {
-        return pixelScale;
-    }
-
-    @Override
-    protected final int getPixelScaleY() {
-        return pixelScale;
+    public final void setSurfaceScale(final int[] pixelScale) {
+        SurfaceScaleUtils.validateReqPixelScale(reqPixelScale, pixelScale, DEBUG_IMPLEMENTATION ? getClass().getName() : null);
+
+        final int[] resPixelScale;
+        if( isNativeValid() ) {
+            if( isOffscreenInstance ) {
+                final NativeWindow pWin = getParent();
+                if( pWin instanceof ScalableSurface ) {
+                    final ScalableSurface sSurf = (ScalableSurface)pWin;
+                    sSurf.setSurfaceScale(reqPixelScale);
+                    final int[] pPixelScale = sSurf.getSurfaceScale(new int[2]);
+                    updatePixelScale(true /* sendEvent */, true /* defer */, pPixelScale[0]); // HiDPI: uniformPixelScale
+                } else {
+                    // just notify updated pixelScale if offscreen
+                    SurfaceScaleUtils.replaceAutoMaxWithPlatformMax(reqPixelScale);
+                    updatePixelScale(true /* sendEvent */, true /* defer */, reqPixelScale[0]); // HiDPI: uniformPixelScale
+                }
+            } else {
+                // set pixelScale in native code, will issue an update PixelScale
+                OSXUtil.RunOnMainThread(true, new Runnable() {
+                    @Override
+                    public void run() {
+                        setPixelScale0(getWindowHandle(), surfaceHandle, reqPixelScale[0]); // HiDPI: uniformPixelScale
+                    }
+                } );
+            }
+            resPixelScale = hasPixelScale;
+        } else {
+            hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE;
+            hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE;
+            resPixelScale = reqPixelScale;
+        }
+        if( DEBUG_IMPLEMENTATION ) {
+            System.err.println("WindowDriver.setPixelScale: "+pixelScale[0]+"x"+pixelScale[1]+" (req) -> "+
+                                reqPixelScale[0]+"x"+reqPixelScale[1]+" (validated) -> "+
+                                resPixelScale[0]+"x"+resPixelScale[1]+" (result) - realized "+isNativeValid());
+        }
     }
 
     @Override
@@ -351,6 +382,9 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl
             }
         }
 
+        final boolean setVisible = 0 != ( FLAG_IS_VISIBLE & flags);
+        final boolean hasFocus = hasFocus();
+
         if(DEBUG_IMPLEMENTATION) {
             final AbstractGraphicsConfiguration cWinCfg = this.getGraphicsConfiguration();
             final NativeWindow pWin = getParent();
@@ -362,12 +396,10 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl
                                ", isOffscreenInstance(sscSurfaceHandle "+toHexString(sscSurfaceHandle)+
                                ", ioi: "+_isOffscreenInstance+
                                ") -> "+isOffscreenInstance+
-                               "\n\t, "+getReconfigureFlagsAsString(null, flags));
+                               "\n\t, "+getReconfigureFlagsAsString(null, flags)+", setVisible "+setVisible+", hasFocus "+hasFocus);
             // Thread.dumpStack();
         }
 
-        final boolean setVisible = 0 != ( FLAG_IS_VISIBLE & flags);
-
         if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) && !setVisible ) {
             if ( !isOffscreenInstance ) {
                 OSXUtil.RunOnMainThread(false, new Runnable() {
@@ -395,6 +427,9 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl
             updatePixelScaleByWindowHandle(false /* sendEvent */);
             super.sizeChanged(false, width, height, true);
             visibleChanged(false, setVisible);
+            if( hasFocus ) {
+                requestFocusImpl(true);
+            }
         } else {
             if( width>0 && height>0 ) {
                 if( !isOffscreenInstance ) {
@@ -613,7 +648,7 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl
                 if( 0 != surfaceHandle ) {
                     throw new NativeWindowException("Internal Error - create w/o window, but has Newt NSView");
                 }
-                surfaceHandle = createView0(pS.getX(), pS.getY(), width, height, fullscreen);
+                surfaceHandle = createView0(pS.getX(), pS.getY(), width, height);
                 if( 0 == surfaceHandle ) {
                     throw new NativeWindowException("Could not create native view "+Thread.currentThread().getName()+" "+this);
                 }
@@ -633,7 +668,7 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl
             OSXUtil.RunOnMainThread(true, new Runnable() {
                 @Override
                 public void run() {
-                    initWindow0( parentWinHandle, newWin, pS.getX(), pS.getY(), width, height,
+                    initWindow0( parentWinHandle, newWin, pS.getX(), pS.getY(), width, height, reqPixelScale[0] /* HiDPI uniformPixelScale */,
                                  isOpaque, visible && !offscreenInstance, surfaceHandle);
                     if( offscreenInstance ) {
                         orderOut0(0!=parentWinHandle ? parentWinHandle : newWin);
@@ -648,11 +683,12 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl
     }
 
     protected static native boolean initIDs0();
-    private native long createView0(int x, int y, int w, int h, boolean fullscreen);
+    private native long createView0(int x, int y, int w, int h);
     private native long createWindow0(int x, int y, int w, int h, boolean fullscreen, int windowStyle, int backingStoreType, long view);
     /** Must be called on Main-Thread */
-    private native void initWindow0(long parentWindow, long window, int x, int y, int w, int h,
+    private native void initWindow0(long parentWindow, long window, int x, int y, int w, int h, float reqPixelScale,
                                     boolean opaque, boolean visible, long view);
+    private native void setPixelScale0(long window, long view, float reqPixelScale);
     private native boolean lockSurface0(long window, long view);
     private native boolean unlockSurface0(long window, long view);
     /** Must be called on Main-Thread */
index 80e7021..2bd11da 100644 (file)
@@ -747,24 +747,18 @@ JNIEXPORT jboolean JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_initIDs0
 /**
  * Class:     jogamp_newt_driver_macosx_WindowDriver
  * Method:    createView0
- * Signature: (IIIIZ)J
+ * Signature: (IIII)J
  */
 JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_createView0
-  (JNIEnv *env, jobject jthis, jint x, jint y, jint w, jint h, 
-   jboolean fullscreen)
+  (JNIEnv *env, jobject jthis, jint x, jint y, jint w, jint h)
 {
     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
 
-    DBG_PRINT( "createView0 - %p (this), %d/%d %dx%d, fs %d (START)\n",
-        (void*)(intptr_t)jthis, (int)x, (int)y, (int)w, (int)h, (int)fullscreen);
+    DBG_PRINT( "createView0 - %p (this), %d/%d %dx%d (START)\n",
+        (void*)(intptr_t)jthis, (int)x, (int)y, (int)w, (int)h);
 
     NSRect rectView = NSMakeRect(0, 0, w, h);
     NewtView *myView = [[NewtView alloc] initWithFrame: rectView] ;
-NS_DURING
-    // Available >= 10.7
-    [myView setWantsBestResolutionOpenGLSurface: YES]; // HiDPI scaling: Always desired
-NS_HANDLER
-NS_ENDHANDLER
     DBG_PRINT( "createView0.X - new view: %p\n", myView);
 
     [pool release];
@@ -780,7 +774,7 @@ NS_ENDHANDLER
  * Signature: (IIIIZIIJ)J
  */
 JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_createWindow0
-  (JNIEnv *env, jobject jthis, jint x, jint y, jint w, jint h, 
+  (JNIEnv *env, jobject jthis, jint x, jint y, jint w, jint h,
    jboolean fullscreen, jint styleMask, jint bufferingType, jlong jview)
 {
     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
@@ -816,10 +810,10 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_createWindow
  *
  * Class:     jogamp_newt_driver_macosx_WindowDriver
  * Method:    initWindow0
- * Signature: (JJIIIIZZZJ)V
+ * Signature: (JJIIIIFZZZJ)V
  */
 JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_initWindow0
-  (JNIEnv *env, jobject jthis, jlong parent, jlong window, jint x, jint y, jint w, jint h,
+  (JNIEnv *env, jobject jthis, jlong parent, jlong window, jint x, jint y, jint w, jint h, jfloat reqPixelScale,
    jboolean opaque, jboolean visible, jlong jview)
 {
     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
@@ -827,10 +821,20 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_initWindow0
     NewtView* myView = (NewtView*) (intptr_t) jview ;
     BOOL fullscreen = myWindow->isFullscreenWindow;
 
-    DBG_PRINT( "initWindow0 - %p (this), %p (parent), %p (window), %d/%d %dx%d, opaque %d, fs %d, visible %d, view %p (START)\n",
-        (void*)(intptr_t)jthis, (void*)(intptr_t)parent, myWindow, (int)x, (int)y, (int)w, (int)h, 
+    DBG_PRINT( "initWindow0 - %p (this), %p (parent), %p (window), %d/%d %dx%d, reqPixScale %f, opaque %d, fs %d, visible %d, view %p (START)\n",
+        (void*)(intptr_t)jthis, (void*)(intptr_t)parent, myWindow, (int)x, (int)y, (int)w, (int)h, (float)reqPixelScale,
         (int) opaque, (int)fullscreen, (int)visible, myView);
 
+NS_DURING
+    // HiDPI scaling: Setup - Available >= 10.7
+    if( 1.0 == reqPixelScale ) {
+        [myView setWantsBestResolutionOpenGLSurface: NO];
+    } else {
+        [myView setWantsBestResolutionOpenGLSurface: YES];
+    }
+NS_HANDLER
+NS_ENDHANDLER
+
     [myWindow setReleasedWhenClosed: NO]; // We control NSWindow destruction!
     [myWindow setPreservesContentDuringLiveResize: NO];
 NS_DURING
@@ -987,6 +991,44 @@ NS_ENDHANDLER
  * Method is called on Main-Thread, hence no special invocation required inside method.
  *
  * Class:     jogamp_newt_driver_macosx_WindowDriver
+ * Method:    setPixelScale0
+ * Signature: (JJF)V
+ */
+JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_setPixelScale0
+  (JNIEnv *env, jobject jthis, jlong window, jlong view, jfloat reqPixelScale)
+{
+    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+    NewtMacWindow* myWindow = (NewtMacWindow*) ((intptr_t) window);
+    NewtView* myView = (NewtView*) (intptr_t) view ;
+#ifdef VERBOSE_ON
+    int dbgIdx = 1;
+#endif
+
+    DBG_PRINT( "setPixelScale0 - %p (this), %p (window), view %p, reqPixScale %f (START)\n",
+        (void*)(intptr_t)jthis, myWindow, myView, (float)reqPixelScale);
+
+NS_DURING
+    // HiDPI scaling: Setup - Available >= 10.7
+    if( 1.0 == reqPixelScale ) {
+        [myView setWantsBestResolutionOpenGLSurface: NO];
+    } else {
+        [myView setWantsBestResolutionOpenGLSurface: YES];
+    }
+NS_HANDLER
+NS_ENDHANDLER
+
+    DBG_PRINT( "setPixelScale0.%d - %p (this), window: %p, view %p\n",
+        dbgIdx++, (void*)(intptr_t)jthis, myWindow, myView);
+
+    [pool release];
+    DBG_PRINT( "setPixelScale0.X - %p (this), window: %p, view %p\n",
+        (void*)(intptr_t)jthis, myWindow, myView);
+}
+
+/**
+ * Method is called on Main-Thread, hence no special invocation required inside method.
+ *
+ * Class:     jogamp_newt_driver_macosx_WindowDriver
  * Method:    close0
  * Signature: (J)V
  */
index c4800bd..fe76183 100644 (file)
@@ -778,8 +778,12 @@ static jmethodID windowRepaintID = NULL;
 {
     [super viewDidChangeBackingProperties];
 
-    CGFloat pixelScale = [[self window] backingScaleFactor];
-    [[self layer] setContentsScale: pixelScale];
+    // HiDPI scaling
+    BOOL useHiDPI = [self wantsBestResolutionOpenGLSurface];
+    CGFloat pixelScaleRaw = [[self window] backingScaleFactor];
+    CGFloat pixelScaleUse = useHiDPI ? pixelScaleRaw : 1.0;
+    DBG_PRINT("viewDidChangeBackingProperties: PixelScale: HiDPI %d, raw %f -> use %f\n", useHiDPI, (float)pixelScaleRaw, (float)pixelScaleUse);
+    [[self layer] setContentsScale: pixelScaleUse];
 
     if (javaWindowObject == NULL) {
         DBG_PRINT("viewDidChangeBackingProperties: null javaWindowObject\n");
@@ -792,7 +796,7 @@ static jmethodID windowRepaintID = NULL;
         return;
     }
 
-    (*env)->CallVoidMethod(env, javaWindowObject, updatePixelScaleID, JNI_TRUE, (jfloat)pixelScale); // defer 
+    (*env)->CallVoidMethod(env, javaWindowObject, updatePixelScaleID, JNI_TRUE, (jfloat)pixelScaleUse); // defer 
 
     // detaching thread not required - daemon
     // NewtCommon_ReleaseJNIEnv(shallBeDetached);
index 622f51e..3dbfeed 100644 (file)
@@ -28,6 +28,7 @@
 
 package com.jogamp.opengl.test.junit.jogl.demos.es2.awt;
 
+import javax.media.nativewindow.ScalableSurface;
 import javax.media.opengl.*;
 
 import com.jogamp.opengl.util.Animator;
@@ -37,6 +38,7 @@ import javax.media.opengl.awt.GLCanvas;
 import com.jogamp.common.os.Platform;
 import com.jogamp.newt.event.awt.AWTKeyAdapter;
 import com.jogamp.newt.event.awt.AWTWindowAdapter;
+import com.jogamp.newt.event.KeyEvent;
 import com.jogamp.newt.event.TraceKeyAdapter;
 import com.jogamp.newt.event.TraceWindowAdapter;
 import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2;
@@ -78,6 +80,7 @@ public class TestGearsES2AWT extends UITestCase {
     static int xpos = 10, ypos = 10;
     static FrameLayout frameLayout = FrameLayout.None;
     static ResizeBy resizeBy = ResizeBy.Component;
+    static int[] reqSurfacePixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE };
 
     static boolean forceES2 = false;
     static boolean forceGL3 = false;
@@ -167,6 +170,7 @@ public class TestGearsES2AWT extends UITestCase {
         final GLCanvas glCanvas = new GLCanvas(caps);
         Assert.assertNotNull(glCanvas);
         setSize(resizeBy, frame, false, glCanvas, new Dimension(width, height));
+        glCanvas.setSurfaceScale(reqSurfacePixelScale);
         frame.setLocation(xpos, ypos);
 
         switch( frameLayout) {
@@ -242,6 +246,33 @@ public class TestGearsES2AWT extends UITestCase {
         new AWTKeyAdapter(new TraceKeyAdapter(quitAdapter), glCanvas).addTo(glCanvas);
         new AWTWindowAdapter(new TraceWindowAdapter(quitAdapter), glCanvas).addTo(frame);
 
+        final com.jogamp.newt.event.KeyListener kl = new com.jogamp.newt.event.KeyAdapter() {
+            @Override
+            public void keyPressed(final KeyEvent e) {
+                if( e.isAutoRepeat() ) {
+                    return;
+                }
+                if(e.getKeyChar()=='x') {
+                    final int[] hadSurfacePixelScale = glCanvas.getSurfaceScale(new int[2]);
+                    final int[] reqSurfacePixelScale;
+                    if( hadSurfacePixelScale[0] == ScalableSurface.IDENTITY_PIXELSCALE ) {
+                        reqSurfacePixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE };
+                    } else {
+                        reqSurfacePixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE };
+                    }
+                    System.err.println("[set PixelScale pre]: had "+hadSurfacePixelScale[0]+"x"+hadSurfacePixelScale[1]+" -> req "+reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]);
+                    glCanvas.setSurfaceScale(reqSurfacePixelScale);
+                    final int[] hasSurfacePixelScale0 = glCanvas.getNativeSurface().convertToPixelUnits(new int[] { 1, 1 });
+                    final int[] hasSurfacePixelScale1 = glCanvas.getSurfaceScale(new int[2]);
+                    System.err.println("[set PixelScale post]: "+hadSurfacePixelScale[0]+"x"+hadSurfacePixelScale[1]+" (had) -> "+
+                                       reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]+" (req) -> "+
+                                       hasSurfacePixelScale1[0]+"x"+hasSurfacePixelScale1[1]+" (has)");
+                    setTitle(frame, glCanvas, caps);
+                    Assert.assertArrayEquals(hasSurfacePixelScale0, hasSurfacePixelScale1);
+                }
+            } };
+        new AWTKeyAdapter(kl, glCanvas).addTo(glCanvas);
+
         javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
             public void run() {
                if( ResizeBy.Frame == resizeBy ) {
@@ -254,6 +285,13 @@ public class TestGearsES2AWT extends UITestCase {
         Assert.assertEquals(true,  AWTRobotUtil.waitForVisible(frame, true));
         Assert.assertEquals(true,  AWTRobotUtil.waitForRealized(glCanvas, true));
 
+        final int[] hasSurfacePixelScale0 = glCanvas.getNativeSurface().convertToPixelUnits(new int[] { 1, 1 });
+        final int[] hasSurfacePixelScale1 = glCanvas.getSurfaceScale(new int[2]);
+        System.err.println("HiDPI PixelScale: "+reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]+" (req) -> "+
+                           hasSurfacePixelScale1[0]+"x"+hasSurfacePixelScale1[1]+" (has)");
+        setTitle(frame, glCanvas, caps);
+        Assert.assertArrayEquals(hasSurfacePixelScale0, hasSurfacePixelScale1);
+
         if( useAnimator ) {
             animator.start();
             Assert.assertTrue(animator.isStarted());
@@ -365,6 +403,17 @@ public class TestGearsES2AWT extends UITestCase {
         runTestGL(caps, resizeBy, frameLayout);
     }
 
+    @Test
+    public void test99_PixelScale1_DefaultNorm() throws InterruptedException, InvocationTargetException {
+        if( mainRun ) return;
+
+        reqSurfacePixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE;
+        reqSurfacePixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE;
+
+        GLCapabilities caps = new GLCapabilities(GLProfile.getGL2ES2());
+        runTestGL(caps, resizeBy, frameLayout);
+    }
+
     public static void main(String args[]) {
         boolean waitForKey = false;
         int rw=-1, rh=-1;
@@ -392,6 +441,11 @@ public class TestGearsES2AWT extends UITestCase {
             } else if(args[i].equals("-rheight")) {
                 i++;
                 rh = MiscUtils.atoi(args[i], rh);
+            } else if(args[i].equals("-pixelScale")) {
+                i++;
+                final int pS = MiscUtils.atoi(args[i], reqSurfacePixelScale[0]);
+                reqSurfacePixelScale[0] = pS;
+                reqSurfacePixelScale[1] = pS;
             } else if(args[i].equals("-layout")) {
                 i++;
                 frameLayout = FrameLayout.valueOf(args[i]);
index 8172eb3..cfd0990 100644 (file)
@@ -35,6 +35,7 @@ import java.awt.event.ComponentEvent;
 import java.awt.event.ComponentListener;
 import java.lang.reflect.InvocationTargetException;
 
+import javax.media.nativewindow.ScalableSurface;
 import javax.media.opengl.GLCapabilities;
 import javax.media.opengl.GLCapabilitiesImmutable;
 import javax.media.opengl.GLProfile;
@@ -50,12 +51,14 @@ import org.junit.Test;
 import org.junit.FixMethodOrder;
 import org.junit.runners.MethodSorters;
 
+import com.jogamp.newt.event.KeyEvent;
 import com.jogamp.newt.event.TraceKeyAdapter;
 import com.jogamp.newt.event.TraceWindowAdapter;
 import com.jogamp.newt.event.awt.AWTKeyAdapter;
 import com.jogamp.newt.event.awt.AWTWindowAdapter;
 import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2;
 import com.jogamp.opengl.test.junit.jogl.demos.gl2.Gears;
+import com.jogamp.opengl.test.junit.util.AWTRobotUtil;
 import com.jogamp.opengl.test.junit.util.MiscUtils;
 import com.jogamp.opengl.test.junit.util.QuitAdapter;
 import com.jogamp.opengl.test.junit.util.UITestCase;
@@ -75,6 +78,7 @@ public class TestGearsES2GLJPanelAWT extends UITestCase {
     static boolean manualTest = false;
     static boolean skipGLOrientationVerticalFlip = false;
     static int xpos = 10, ypos = 10;
+    static int[] reqSurfacePixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE };
 
     @BeforeClass
     public static void initClass() {
@@ -120,6 +124,7 @@ public class TestGearsES2GLJPanelAWT extends UITestCase {
         glJPanel.setMinimumSize(wsize);
         glJPanel.setPreferredSize(wsize);
         glJPanel.setSize(wsize);
+        glJPanel.setSurfaceScale(reqSurfacePixelScale);
         if( caps.isBitmap() || caps.getGLProfile().isGL2() ) {
             final Gears gears = new Gears(swapInterval);
             gears.setFlipVerticalInGLOrientation(skipGLOrientationVerticalFlip);
@@ -161,6 +166,15 @@ public class TestGearsES2GLJPanelAWT extends UITestCase {
                     frame.pack();
                     frame.setVisible(true);
                 } } ) ;
+        Assert.assertEquals(true,  AWTRobotUtil.waitForVisible(frame, true));
+        Assert.assertEquals(true,  AWTRobotUtil.waitForRealized(glJPanel, true));
+
+        final int[] hasSurfacePixelScale0 = glJPanel.getNativeSurface().convertToPixelUnits(new int[] { 1, 1 });
+        final int[] hasSurfacePixelScale1 = glJPanel.getSurfaceScale(new int[2]);
+        System.err.println("HiDPI PixelScale: "+reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]+" (req) -> "+
+                           hasSurfacePixelScale1[0]+"x"+hasSurfacePixelScale1[1]+" (has)");
+        setTitle(frame, glJPanel, caps);
+        Assert.assertArrayEquals(hasSurfacePixelScale0, hasSurfacePixelScale1);
 
         if( useAnimator ) {
             animator.setUpdateFPSFrames(60, System.err);
@@ -172,6 +186,33 @@ public class TestGearsES2GLJPanelAWT extends UITestCase {
         new AWTKeyAdapter(new TraceKeyAdapter(quitAdapter), glJPanel).addTo(glJPanel);
         new AWTWindowAdapter(new TraceWindowAdapter(quitAdapter), glJPanel).addTo(frame);
 
+        final com.jogamp.newt.event.KeyListener kl = new com.jogamp.newt.event.KeyAdapter() {
+            @Override
+            public void keyPressed(final KeyEvent e) {
+                if( e.isAutoRepeat() ) {
+                    return;
+                }
+                if(e.getKeyChar()=='x') {
+                    final int[] hadSurfacePixelScale = glJPanel.getSurfaceScale(new int[2]);
+                    final int[] reqSurfacePixelScale;
+                    if( hadSurfacePixelScale[0] == ScalableSurface.IDENTITY_PIXELSCALE ) {
+                        reqSurfacePixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE };
+                    } else {
+                        reqSurfacePixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE };
+                    }
+                    System.err.println("[set PixelScale pre]: had "+hadSurfacePixelScale[0]+"x"+hadSurfacePixelScale[1]+" -> req "+reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]);
+                    glJPanel.setSurfaceScale(reqSurfacePixelScale);
+                    final int[] hasSurfacePixelScale0 = glJPanel.getNativeSurface().convertToPixelUnits(new int[] { 1, 1 });
+                    final int[] hasSurfacePixelScale1 = glJPanel.getSurfaceScale(new int[2]);
+                    System.err.println("[set PixelScale post]: "+hadSurfacePixelScale[0]+"x"+hadSurfacePixelScale[1]+" (had) -> "+
+                                       reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]+" (req) -> "+
+                                       hasSurfacePixelScale1[0]+"x"+hasSurfacePixelScale1[1]+" (has)");
+                    setTitle(frame, glJPanel, caps);
+                    Assert.assertArrayEquals(hasSurfacePixelScale0, hasSurfacePixelScale1);
+                }
+            } };
+        new AWTKeyAdapter(kl, glJPanel).addTo(glJPanel);
+
         snap.setMakeSnapshot();
 
         if( null != rwsize ) {
@@ -198,11 +239,13 @@ public class TestGearsES2GLJPanelAWT extends UITestCase {
 
         Assert.assertNotNull(frame);
         Assert.assertNotNull(glJPanel);
-        Assert.assertNotNull(animator);
 
         if( useAnimator ) {
+            Assert.assertNotNull(animator);
             animator.stop();
             Assert.assertEquals(false, animator.isAnimating());
+        } else {
+            Assert.assertNull(animator);
         }
         SwingUtilities.invokeAndWait(new Runnable() {
                 public void run() {
@@ -341,6 +384,20 @@ public class TestGearsES2GLJPanelAWT extends UITestCase {
         runTestGL(caps);
     }
 
+    @Test
+    public void test99_PixelScale1_DefaultNorm()
+            throws AWTException, InterruptedException, InvocationTargetException
+    {
+        if( manualTest ) {
+            return;
+        }
+        reqSurfacePixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE;
+        reqSurfacePixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE;
+
+        GLCapabilities caps = new GLCapabilities(GLProfile.getDefault());
+        runTestGL(caps);
+    }
+
     static long duration = 500; // ms
 
     public static void main(String args[]) {
@@ -374,6 +431,11 @@ public class TestGearsES2GLJPanelAWT extends UITestCase {
             } else if(args[i].equals("-rheight")) {
                 i++;
                 rh = MiscUtils.atoi(args[i], rh);
+            } else if(args[i].equals("-pixelScale")) {
+                i++;
+                final int pS = MiscUtils.atoi(args[i], reqSurfacePixelScale[0]);
+                reqSurfacePixelScale[0] = pS;
+                reqSurfacePixelScale[1] = pS;
             } else if(args[i].equals("-userVFlip")) {
                 skipGLOrientationVerticalFlip = true;
             } else if(args[i].equals("-vsync")) {
index bc6b5e2..b916e21 100644 (file)
@@ -29,6 +29,7 @@
 package com.jogamp.opengl.test.junit.jogl.demos.es2.newt;
 
 import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
 import java.net.URLConnection;
 
 import com.jogamp.common.util.IOUtil;
@@ -54,6 +55,7 @@ import com.jogamp.opengl.util.PNGPixelRect;
 import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2;
 
 import javax.media.nativewindow.NativeWindowFactory;
+import javax.media.nativewindow.ScalableSurface;
 import javax.media.nativewindow.util.Dimension;
 import javax.media.nativewindow.util.Point;
 import javax.media.nativewindow.util.PointImmutable;
@@ -79,6 +81,7 @@ public class TestGearsES2NEWT extends UITestCase {
     static int screenIdx = 0;
     static PointImmutable wpos;
     static DimensionImmutable wsize, rwsize=null;
+    static int[] reqSurfacePixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE };
 
     static long duration = 500; // ms
     static boolean opaque = true;
@@ -119,7 +122,7 @@ public class TestGearsES2NEWT extends UITestCase {
 
     private void setTitle(final Window win, final GLCapabilitiesImmutable caps) {
         final String capsA = caps.isBackgroundOpaque() ? "opaque" : "transl";
-        win.setTitle("GLWindow["+capsA+"], swapI "+swapInterval+", win: "+win.getBounds()+", pix: "+win.getSurfaceBounds());
+        win.setTitle("GLWindow["+capsA+"], swapI "+swapInterval+", win: "+win.getBounds()+", pix: "+win.getSurfaceWidth()+"x"+win.getSurfaceHeight());
     }
     protected void runTestGL(final GLCapabilitiesImmutable caps, boolean undecorated) throws InterruptedException {
         System.err.println("requested: vsync "+swapInterval+", "+caps);
@@ -127,6 +130,7 @@ public class TestGearsES2NEWT extends UITestCase {
         Screen screen = NewtFactory.createScreen(dpy, screenIdx);
         final GLWindow glWindow = GLWindow.create(screen, caps);
         Assert.assertNotNull(glWindow);
+        glWindow.setSurfaceScale(reqSurfacePixelScale);
         glWindow.setSize(wsize.getWidth(), wsize.getHeight());
         if(null != wpos) {
             glWindow.setPosition(wpos.getX(), wpos.getY());
@@ -342,6 +346,23 @@ public class TestGearsES2NEWT extends UITestCase {
                             System.err.println("[set mouse pos post]");
                             glWindow.setExclusiveContextThread(t);
                     } }.start();
+                } else if(e.getKeyChar()=='x') {
+                    final int[] hadSurfacePixelScale = glWindow.getSurfaceScale(new int[2]);
+                    final int[] reqSurfacePixelScale;
+                    if( hadSurfacePixelScale[0] == ScalableSurface.IDENTITY_PIXELSCALE ) {
+                        reqSurfacePixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE };
+                    } else {
+                        reqSurfacePixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE };
+                    }
+                    System.err.println("[set PixelScale pre]: had "+hadSurfacePixelScale[0]+"x"+hadSurfacePixelScale[1]+" -> req "+reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]);
+                    glWindow.setSurfaceScale(reqSurfacePixelScale);
+                    final int[] hasSurfacePixelScale0 = glWindow.convertToPixelUnits(new int[] { 1, 1 });
+                    final int[] hasSurfacePixelScale1 = glWindow.getSurfaceScale(new int[2]);
+                    System.err.println("[set PixelScale post]: "+hadSurfacePixelScale[0]+"x"+hadSurfacePixelScale[1]+" (had) -> "+
+                                       reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]+" (req) -> "+
+                                       hasSurfacePixelScale1[0]+"x"+hasSurfacePixelScale1[1]+" (has)");
+                    setTitle(glWindow, caps);
+                    Assert.assertArrayEquals(hasSurfacePixelScale0, hasSurfacePixelScale1);
                 }
             }
         });
@@ -414,6 +435,13 @@ public class TestGearsES2NEWT extends UITestCase {
         System.err.println("GL chosen: "+glWindow.getChosenCapabilities());
         System.err.println("window pos/siz: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getSurfaceWidth()+"x"+glWindow.getSurfaceHeight()+", "+glWindow.getInsets());
 
+        final int[] hasSurfacePixelScale0 = glWindow.convertToPixelUnits(new int[] { 1, 1 });
+        final int[] hasSurfacePixelScale1 = glWindow.getSurfaceScale(new int[2]);
+        System.err.println("HiDPI PixelScale: "+reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]+" (req) -> "+
+                           hasSurfacePixelScale1[0]+"x"+hasSurfacePixelScale1[1]+" (has)");
+        setTitle(glWindow, caps);
+        Assert.assertArrayEquals(hasSurfacePixelScale0, hasSurfacePixelScale1);
+
         snap.setMakeSnapshot();
 
         if( null != rwsize ) {
@@ -520,6 +548,17 @@ public class TestGearsES2NEWT extends UITestCase {
         runTestGL(caps, undecorated);
     }
 
+    @Test
+    public void test99_PixelScale1_DefaultNorm() throws InterruptedException, InvocationTargetException {
+        if( mainRun ) return;
+
+        reqSurfacePixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE;
+        reqSurfacePixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE;
+
+        GLCapabilities caps = new GLCapabilities(GLProfile.getGL2ES2());
+        runTestGL(caps, undecorated);
+    }
+
     public static void main(String args[]) throws IOException {
         mainRun = true;
 
@@ -584,6 +623,11 @@ public class TestGearsES2NEWT extends UITestCase {
                 i++;
                 y = MiscUtils.atoi(args[i], y);
                 usePos = true;
+            } else if(args[i].equals("-pixelScale")) {
+                i++;
+                final int pS = MiscUtils.atoi(args[i], reqSurfacePixelScale[0]);
+                reqSurfacePixelScale[0] = pS;
+                reqSurfacePixelScale[1] = pS;
             } else if(args[i].equals("-rwidth")) {
                 i++;
                 rw = MiscUtils.atoi(args[i], rw);
index c7a0035..d3e6956 100644 (file)
@@ -3,14 +3,14 @@
  *
  * Redistribution and use in source and binary forms, with or without modification, are
  * permitted provided that the following conditions are met:
- * 
+ *
  *    1. Redistributions of source code must retain the above copyright notice, this list of
  *       conditions and the following disclaimer.
- * 
+ *
  *    2. 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.
- * 
+ *
  * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``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 JogAmp Community OR
  * 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.
- * 
+ *
  * The views and conclusions contained in the software and documentation are those of the
  * authors and should not be interpreted as representing official policies, either expressed
  * or implied, of JogAmp Community.
  */
+
 package com.jogamp.opengl.test.junit.jogl.demos.es2.newt;
 
 import java.awt.BorderLayout;
@@ -34,6 +34,8 @@ import java.awt.Component;
 import java.awt.Container;
 import java.awt.Frame;
 import java.awt.TextArea;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
 
@@ -41,7 +43,9 @@ import com.jogamp.common.os.Platform;
 import com.jogamp.newt.Display;
 import com.jogamp.newt.NewtFactory;
 import com.jogamp.newt.Screen;
+import com.jogamp.newt.Window;
 import com.jogamp.newt.awt.NewtCanvasAWT;
+import com.jogamp.newt.event.KeyEvent;
 import com.jogamp.newt.event.WindowEvent;
 import com.jogamp.newt.event.WindowAdapter;
 import com.jogamp.newt.opengl.GLWindow;
@@ -53,6 +57,7 @@ import com.jogamp.opengl.util.Animator;
 import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2;
 import com.jogamp.opengl.test.junit.newt.parenting.NewtAWTReparentingKeyAdapter;
 
+import javax.media.nativewindow.ScalableSurface;
 import javax.media.nativewindow.util.Dimension;
 import javax.media.nativewindow.util.Point;
 import javax.media.nativewindow.util.PointImmutable;
@@ -71,16 +76,17 @@ import org.junit.FixMethodOrder;
 import org.junit.runners.MethodSorters;
 
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class TestGearsES2NewtCanvasAWT extends UITestCase {    
+public class TestGearsES2NewtCanvasAWT extends UITestCase {
     public enum FrameLayout { None, TextOnBottom, BorderBottom, BorderBottom2, BorderCenter, BorderCenterSurrounded, DoubleBorderCenterSurrounded };
     public enum ResizeBy { GLWindow, Component, Frame };
-    
+
     static int screenIdx = 0;
     static PointImmutable wpos;
     static DimensionImmutable wsize, rwsize = null;
     static FrameLayout frameLayout = FrameLayout.None;
     static ResizeBy resizeBy = ResizeBy.Component;
-    
+    static int[] reqSurfacePixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE };
+
     static long duration = 500; // ms
     static boolean opaque = true;
     static int forceAlpha = -1;
@@ -96,7 +102,7 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase {
     static boolean mainRun = false;
     static boolean exclusiveContext = false;
     static boolean useAnimator = true;
-    
+
     @BeforeClass
     public static void initClass() {
         if(null == wsize) {
@@ -120,7 +126,7 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase {
         } catch( Throwable throwable ) {
             throwable.printStackTrace();
             Assume.assumeNoException( throwable );
-        }       
+        }
     }
     static void setComponentSize(final Frame frame, final Component comp, final DimensionImmutable new_sz) {
         try {
@@ -137,7 +143,7 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase {
         } catch( Throwable throwable ) {
             throwable.printStackTrace();
             Assume.assumeNoException( throwable );
-        }       
+        }
     }
     static void setFrameSize(final Frame frame, final boolean frameLayout, final DimensionImmutable new_sz) {
         try {
@@ -152,9 +158,9 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase {
         } catch( Throwable throwable ) {
             throwable.printStackTrace();
             Assume.assumeNoException( throwable );
-        }       
+        }
     }
-    
+
     static void setSize(final ResizeBy resizeBy, final Frame frame, final boolean frameLayout, final Component comp, final GLWindow glw, final DimensionImmutable new_sz) {
         switch( resizeBy ) {
             case GLWindow:
@@ -166,9 +172,18 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase {
             case Frame:
                 setFrameSize(frame, frameLayout, new_sz);
                 break;
-        }        
+        }
+    }
+
+    private void setTitle(final Frame frame, final NewtCanvasAWT glc, final Window win, final GLCapabilitiesImmutable caps) {
+        final String capsA = caps.isBackgroundOpaque() ? "opaque" : "transl";
+        {
+            final java.awt.Rectangle b = glc.getBounds();
+            frame.setTitle("NewtCanvasAWT["+capsA+"], swapI "+swapInterval+", win: ["+b.x+"/"+b.y+" "+b.width+"x"+b.height+"], pix: "+glc.getNativeWindow().getSurfaceWidth()+"x"+glc.getNativeWindow().getSurfaceHeight());
+        }
+        win.setTitle("GLWindow["+capsA+"], swapI "+swapInterval+", win: "+win.getBounds()+", pix: "+win.getSurfaceWidth()+"x"+win.getSurfaceHeight());
     }
-    
+
     // public enum ResizeBy { GLWindow, Component, Frame };
     protected void runTestGL(final GLCapabilitiesImmutable caps, final ResizeBy resizeBy, final FrameLayout frameLayout) throws InterruptedException, InvocationTargetException {
         System.err.println("requested: vsync "+swapInterval+", "+caps);
@@ -176,14 +191,15 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase {
         Screen screen = NewtFactory.createScreen(dpy, screenIdx);
         final GLWindow glWindow = GLWindow.create(screen, caps);
         Assert.assertNotNull(glWindow);
-        
+        glWindow.setSurfaceScale(reqSurfacePixelScale);
+
         final NewtCanvasAWT newtCanvasAWT = new NewtCanvasAWT(glWindow);
         if ( shallUseOffscreenFBOLayer ) {
             newtCanvasAWT.setShallUseOffscreenLayer(true);
         }
-        
+
         final Frame frame = new Frame("AWT Parent Frame");
-        
+
         setSize(resizeBy, frame, false, newtCanvasAWT, glWindow, wsize);
 
         switch( frameLayout) {
@@ -199,7 +215,7 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase {
                 frame.setLayout(new BorderLayout());
                 frame.add(ta, BorderLayout.SOUTH);
                 frame.add(newtCanvasAWT, BorderLayout.CENTER);
-                break;                
+                break;
             case BorderBottom:
                 frame.setLayout(new BorderLayout());
                 frame.add(newtCanvasAWT, BorderLayout.SOUTH);
@@ -229,7 +245,7 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase {
                 c.add(new Button("east"), BorderLayout.EAST);
                 c.add(new Button("west"), BorderLayout.WEST);
                 c.add(newtCanvasAWT, BorderLayout.CENTER);
-                
+
                 frame.setLayout(new BorderLayout());
                 frame.add(new Button("NORTH"), BorderLayout.NORTH);
                 frame.add(new Button("SOUTH"), BorderLayout.SOUTH);
@@ -238,19 +254,35 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase {
                 frame.add(c, BorderLayout.CENTER);
                 break;
         }
-        
-        frame.setTitle("Gears NewtCanvasAWT Test (translucent "+!caps.isBackgroundOpaque()+"), swapInterval "+swapInterval+", size "+wsize+", pos "+wpos);
-        
+
         final GearsES2 demo = new GearsES2(swapInterval);
         demo.setPMVUseBackingArray(pmvUseBackingArray);
         glWindow.addGLEventListener(demo);
-        
+
+        frame.addComponentListener(new ComponentListener() {
+            @Override
+            public void componentResized(ComponentEvent e) {
+                setTitle(frame, newtCanvasAWT, glWindow, caps);
+            }
+
+            @Override
+            public void componentMoved(ComponentEvent e) {
+                setTitle(frame, newtCanvasAWT, glWindow, caps);
+            }
+
+            @Override
+            public void componentShown(ComponentEvent e) { }
+
+            @Override
+            public void componentHidden(ComponentEvent e) { }
+        });
+
         final Animator animator = useAnimator ? new Animator() : null;
         if( useAnimator ) {
             animator.setModeBits(false, Animator.MODE_EXPECT_AWT_RENDERING_THREAD);
             animator.setExclusiveContext(exclusiveContext);
         }
-        
+
         final QuitAdapter quitAdapter = new QuitAdapter();
         //glWindow.addKeyListener(new TraceKeyAdapter(quitAdapter));
         //glWindow.addWindowListener(new TraceWindowAdapter(quitAdapter));
@@ -263,11 +295,36 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase {
             }
             public void windowMoved(WindowEvent e) {
                 System.err.println("window moved:   "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getSurfaceWidth()+"x"+glWindow.getSurfaceHeight());
-            }            
+            }
         });
-        
+
         glWindow.addKeyListener(new NewtAWTReparentingKeyAdapter(frame, newtCanvasAWT, glWindow, quitAdapter));
-        
+        glWindow.addKeyListener(new com.jogamp.newt.event.KeyAdapter() {
+            @Override
+            public void keyPressed(final KeyEvent e) {
+                if( e.isAutoRepeat() ) {
+                    return;
+                }
+                if(e.getKeyChar()=='x') {
+                    final int[] hadSurfacePixelScale = glWindow.getSurfaceScale(new int[2]);
+                    final int[] reqSurfacePixelScale;
+                    if( hadSurfacePixelScale[0] == ScalableSurface.IDENTITY_PIXELSCALE ) {
+                        reqSurfacePixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE };
+                    } else {
+                        reqSurfacePixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE };
+                    }
+                    System.err.println("[set PixelScale pre]: had "+hadSurfacePixelScale[0]+"x"+hadSurfacePixelScale[1]+" -> req "+reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]);
+                    glWindow.setSurfaceScale(reqSurfacePixelScale);
+                    final int[] hasSurfacePixelScale0 = glWindow.convertToPixelUnits(new int[] { 1, 1 });
+                    final int[] hasSurfacePixelScale1 = glWindow.getSurfaceScale(new int[2]);
+                    System.err.println("[set PixelScale post]: "+hadSurfacePixelScale[0]+"x"+hadSurfacePixelScale[1]+" (had) -> "+
+                                       reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]+" (req) -> "+
+                                       hasSurfacePixelScale1[0]+"x"+hasSurfacePixelScale1[1]+" (has)");
+                    setTitle(frame, newtCanvasAWT, glWindow, caps);
+                    Assert.assertArrayEquals(hasSurfacePixelScale0, hasSurfacePixelScale1);
+                }
+            } } );
+
         if( useAnimator ) {
             animator.add(glWindow);
             animator.start();
@@ -281,28 +338,35 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase {
                if( ResizeBy.Frame == resizeBy ) {
                    frame.validate();
                } else {
-                   frame.pack();                   
-               }                
-               frame.setVisible(true);               
+                   frame.pack();
+               }
+               frame.setVisible(true);
            }
-        });        
+        });
         Assert.assertEquals(true,  AWTRobotUtil.waitForVisible(frame, true));
-        Assert.assertEquals(true,  AWTRobotUtil.waitForRealized(glWindow, true)); 
-        
+        Assert.assertEquals(true,  AWTRobotUtil.waitForRealized(glWindow, true));
+
         if( useAnimator ) {
             animator.setUpdateFPSFrames(60, showFPS ? System.err : null);
         }
-        
+
         System.err.println("NW chosen: "+glWindow.getDelegatedWindow().getChosenCapabilities());
         System.err.println("GL chosen: "+glWindow.getChosenCapabilities());
         System.err.println("window pos/siz: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getSurfaceWidth()+"x"+glWindow.getSurfaceHeight()+", "+glWindow.getInsets());
-                
+
+        final int[] hasSurfacePixelScale0 = glWindow.convertToPixelUnits(new int[] { 1, 1 });
+        final int[] hasSurfacePixelScale1 = glWindow.getSurfaceScale(new int[2]);
+        System.err.println("HiDPI PixelScale: "+reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]+" (req) -> "+
+                           hasSurfacePixelScale1[0]+"x"+hasSurfacePixelScale1[1]+" (has)");
+        setTitle(frame, newtCanvasAWT, glWindow, caps);
+        Assert.assertArrayEquals(hasSurfacePixelScale0, hasSurfacePixelScale1);
+
         if( null != rwsize ) {
-            Thread.sleep(500); // 500ms delay 
+            Thread.sleep(500); // 500ms delay
             setSize(resizeBy, frame, true, newtCanvasAWT, glWindow, rwsize);
             System.err.println("window resize "+rwsize+" -> pos/siz: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getSurfaceWidth()+"x"+glWindow.getSurfaceHeight()+", "+glWindow.getInsets());
         }
-        
+
         final long t0 = System.currentTimeMillis();
         long t1 = t0;
         while(!quitAdapter.shouldQuit() && t1-t0<duration) {
@@ -318,8 +382,8 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase {
         }
         Assert.assertEquals(null, glWindow.getExclusiveContextThread());
         SwingUtilities.invokeAndWait(new Runnable() {
-           public void run() {               
-               frame.dispose();               
+           public void run() {
+               frame.dispose();
            }
         });
         glWindow.destroy();
@@ -341,7 +405,7 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase {
             final GLCapabilities caps = new GLCapabilities( glp );
             caps.setBackgroundOpaque(opaque);
             if(-1 < forceAlpha) {
-                caps.setAlphaBits(forceAlpha); 
+                caps.setAlphaBits(forceAlpha);
             }
             runTestGL(caps, resizeBy, frameLayout);
             if(loop_shutdown) {
@@ -353,7 +417,7 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase {
     @Test
     public void test02GL3() throws InterruptedException, InvocationTargetException {
         if(mainRun) return;
-        
+
         if( !GLProfile.isAvailable(GLProfile.GL3) ) {
             System.err.println("GL3 n/a");
             return;
@@ -362,14 +426,25 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase {
         final GLCapabilities caps = new GLCapabilities( glp );
         runTestGL(caps, resizeBy, frameLayout);
     }
-    
+
+    @Test
+    public void test99_PixelScale1_DefaultNorm() throws InterruptedException, InvocationTargetException {
+        if( mainRun ) return;
+
+        reqSurfacePixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE;
+        reqSurfacePixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE;
+
+        GLCapabilities caps = new GLCapabilities(GLProfile.getGL2ES2());
+        runTestGL(caps, resizeBy, frameLayout);
+    }
+
     public static void main(String args[]) throws IOException {
         mainRun = true;
-        
+
         int x=0, y=0, w=640, h=480;
         int rw=-1, rh=-1;
         boolean usePos = false;
-        
+
         for(int i=0; i<args.length; i++) {
             if(args[i].equals("-time")) {
                 i++;
@@ -424,6 +499,11 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase {
                 i++;
                 y = MiscUtils.atoi(args[i], y);
                 usePos = true;
+            } else if(args[i].equals("-pixelScale")) {
+                i++;
+                final int pS = MiscUtils.atoi(args[i], reqSurfacePixelScale[0]);
+                reqSurfacePixelScale[0] = pS;
+                reqSurfacePixelScale[1] = pS;
             } else if(args[i].equals("-screen")) {
                 i++;
                 screenIdx = MiscUtils.atoi(args[i], 0);
@@ -438,11 +518,11 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase {
         if( 0 < rw && 0 < rh ) {
             rwsize = new Dimension(rw, rh);
         }
-        
+
         if(usePos) {
             wpos = new Point(x, y);
         }
-        
+
         System.err.println("frameLayout "+frameLayout);
         System.err.println("resizeBy "+resizeBy);
         System.err.println("position "+wpos);
@@ -450,9 +530,9 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase {
         System.err.println("resize "+rwsize);
         System.err.println("screen "+screenIdx);
         System.err.println("translucent "+(!opaque));
-        System.err.println("forceAlpha "+forceAlpha);        
+        System.err.println("forceAlpha "+forceAlpha);
         System.err.println("fullscreen "+fullscreen);
-        System.err.println("pmvDirect "+(!pmvUseBackingArray));        
+        System.err.println("pmvDirect "+(!pmvUseBackingArray));
         System.err.println("loops "+loops);
         System.err.println("loop shutdown "+loop_shutdown);
         System.err.println("shallUseOffscreenFBOLayer     "+shallUseOffscreenFBOLayer);
http://JogAmp.org git info: FAQ, tutorial and man pages.