Przeglądaj źródła

fix incorrect input cursor position on MacOS with retina screen in use (#1607)

* [M]Fix code error when checking UTF-8 data

Let a 3 bytes UTF-8 data = [0xE4, 0x8A, 0xBC], when b = 0xE4 (1110 0100), it will be treated as 2 bytes.

See this part:

```java
            if (b < 0x80) {
                // good
            }
            else if ((b & 0xC0) == 0xC0) {//   (0xE4 & 0xC0) == 0xC0     =====>  true
                utf8State = UTF8_2BYTE;
            }
            else if ((b & 0xE0) == 0xE0) {//   (0xE4 & 0xE0) == 0xE0      =====>  true
                utf8State = UTF8_3BYTE_1;
            }
            else {
                utf8State = UTF8_ILLEGAL;
            }
```

3 bytes UTF-8 data while always be treated as 2 bytes UTF-8 data.

It's better that always treat String data as UTF-8 now.

see https://hub.jmonkeyengine.org/t/code-error-on-checking-utf-8-data/43909

* [M]Use StandardCharsets.UTF_8 instead of constant 'UTF8'

* [Add]Add HiDPI support with lwjgl3-glfw

* [M]Add a parameter UseRetinaFrameBuffer to enable/disable usage of full resolution framebuffers on Retina Display

* [M]WindowContentScale is incorrect when glfw window is created, and we don't get any callback when it is changed.So just get it from time to time when mouse cursor is moved.

* [M]Get the real frame buffer size at the 2nd frame after the context is restarted.

* [M]Change default value of UseRetinaFrameBuffer to true.

* [M]Add resolution check before reshape

Co-authored-by: 闫茂源 <[email protected]>
Co-authored-by: 闫茂源 <[email protected]>
Yan 3 lat temu
rodzic
commit
a3a77eb0f4

+ 19 - 0
jme3-core/src/main/java/com/jme3/system/AppSettings.java

@@ -289,6 +289,7 @@ public final class AppSettings extends HashMap<String, Object> {
         defaults.put("SwapBuffers", true);
         defaults.put("OpenCL", false);
         defaults.put("OpenCLPlatformChooser", DefaultPlatformChooser.class.getName());
+        defaults.put("UseRetinaFrameBuffer", true);// MacOS spec
         //  defaults.put("Icons", null);
     }
 
@@ -1331,4 +1332,22 @@ public final class AppSettings extends HashMap<String, Object> {
     public void setGraphicsTrace(boolean trace) {
         putBoolean("GraphicsTrace", trace);
     }
+
+    /**
+     * Determine whether to use full resolution framebuffers on Retina displays.
+     *
+     * @return whether to use full resolution framebuffers on Retina displays.
+     */
+    public boolean isUseRetinaFrameBuffer() {
+        return getBoolean("UseRetinaFrameBuffer");
+    }
+
+    /**
+     * Specifies whether to use full resolution framebuffers on Retina displays. This is ignored on other platforms.
+     *
+     * @param useRetinaFrameBuffer whether to use full resolution framebuffers on Retina displays.
+     */
+    public void setUseRetinaFrameBuffer(boolean useRetinaFrameBuffer) {
+        putBoolean("UseRetinaFrameBuffer", useRetinaFrameBuffer);
+    }
 }

+ 17 - 6
jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwMouseInput.java

@@ -36,15 +36,18 @@ import com.jme3.input.MouseInput;
 import com.jme3.input.RawInputListener;
 import com.jme3.input.event.MouseButtonEvent;
 import com.jme3.input.event.MouseMotionEvent;
+import com.jme3.math.Vector2f;
 import com.jme3.system.lwjgl.LwjglWindow;
 import com.jme3.util.BufferUtils;
 import java.nio.ByteBuffer;
 import java.nio.DoubleBuffer;
+import java.nio.FloatBuffer;
 import java.nio.IntBuffer;
 import java.util.ArrayDeque;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Queue;
+import java.util.logging.Level;
 import java.util.logging.Logger;
 import org.lwjgl.glfw.*;
 import static org.lwjgl.glfw.GLFW.*;
@@ -141,11 +144,14 @@ public class GlfwMouseInput implements MouseInput {
     }
 
     private void onCursorPos(final long window, final double xpos, final double ypos) {
+        float[] xScale = new float[1];
+        float[] yScale = new float[1];
+        glfwGetWindowContentScale(window, xScale, yScale);
 
         int xDelta;
         int yDelta;
-        int x = (int) Math.round(xpos);
-        int y = currentHeight - (int) Math.round(ypos);
+        int x = (int) Math.round(xpos * xScale[0]);
+        int y = (int) Math.round((currentHeight - ypos) * yScale[0]);
 
         xDelta = x - mouseX;
         yDelta = y - mouseY;
@@ -243,11 +249,16 @@ public class GlfwMouseInput implements MouseInput {
     }
 
     private void initCurrentMousePosition(long window) {
-        DoubleBuffer x = BufferUtils.createDoubleBuffer(1);
-        DoubleBuffer y = BufferUtils.createDoubleBuffer(1);
+        double[] x = new double[1];
+        double[] y = new double[1];
         glfwGetCursorPos(window, x, y);
-        mouseX = (int) Math.round(x.get());
-        mouseY = currentHeight - (int) Math.round(y.get());
+
+        float[] xScale = new float[1];
+        float[] yScale = new float[1];
+        glfwGetWindowContentScale(window, xScale, yScale);
+
+        mouseX = (int) Math.round(x[0] * xScale[0]);
+        mouseY = (int) Math.round((currentHeight - y[0]) * yScale[0]);
     }
 
     /**

+ 45 - 0
jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java

@@ -39,6 +39,7 @@ import com.jme3.input.TouchInput;
 import com.jme3.input.lwjgl.GlfwJoystickInput;
 import com.jme3.input.lwjgl.GlfwKeyInput;
 import com.jme3.input.lwjgl.GlfwMouseInput;
+import com.jme3.math.Vector2f;
 import com.jme3.system.AppSettings;
 import com.jme3.system.JmeContext;
 import com.jme3.system.JmeSystem;
@@ -226,6 +227,7 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
         glfwWindowHint(GLFW_SAMPLES, settings.getSamples());
         glfwWindowHint(GLFW_STEREO, settings.useStereo3D() ? GLFW_TRUE : GLFW_FALSE);
         glfwWindowHint(GLFW_REFRESH_RATE, settings.getFrequency()<=0?GLFW_DONT_CARE:settings.getFrequency());
+        glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, settings.isUseRetinaFrameBuffer() ? GLFW_TRUE : GLFW_FALSE);
 
         if (settings.getBitsPerPixel() == 24) {
             glfwWindowHint(GLFW_RED_BITS, 8);
@@ -330,6 +332,8 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
         if (settings.isOpenCLSupport()) {
             initOpenCL(window);
         }
+
+        framesAfterContextStarted = 0;
     }
 
     private void onWindowSizeChanged(final int width, final int height) {
@@ -521,6 +525,8 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
         return true;
     }
 
+    private int framesAfterContextStarted = 0;
+
     /**
      * execute one iteration of the render loop in the OpenGL thread
      */
@@ -534,6 +540,21 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
             throw new IllegalStateException();
         }
 
+        // Update the frame buffer size from 2nd frame since the initial value
+        // of frame buffer size from glfw maybe incorrect when HiDPI display is in use
+        if (framesAfterContextStarted < 2) {
+            framesAfterContextStarted++;
+            if (framesAfterContextStarted == 2) {
+                int[] width = new int[1];
+                int[] height = new int[1];
+                glfwGetFramebufferSize(window, width, height);
+
+                if (settings.getWidth() != width[0] || settings.getHeight() != height[0]) {
+                    listener.reshape(width[0], height[0]);
+                }
+            }
+        }
+
         listener.update();
 
         // All this does is call swap buffers
@@ -688,4 +709,28 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
     public long getWindowHandle() {
         return window;
     }
+
+    /**
+     * Get the window content scale, for HiDPI support.
+     *
+     * The content scale is the ratio between the current DPI and the platform's default DPI.
+     * This is especially important for text and any UI elements. If the pixel dimensions of
+     * your UI scaled by this look appropriate on your machine then it should appear at a
+     * reasonable size on other machines regardless of their DPI and scaling settings. This
+     * relies on the system DPI and scaling settings being somewhat correct.
+     *
+     * @param store A vector2f to store the result
+     * @return The window content scale
+     * @see <a href="https://www.glfw.org/docs/latest/window_guide.html#window_scale">Window content scale</a>
+     */
+    public Vector2f getWindowContentScale(Vector2f store) {
+        float[] xScale = new float[1];
+        float[] yScale = new float[1];
+        glfwGetWindowContentScale(window, xScale, yScale);
+
+        if (store != null) {
+            return store.set(xScale[0], yScale[0]);
+        }
+        return new Vector2f(xScale[0], yScale[0]);
+    }
 }