Skip to content

Commit

Permalink
wayland: implement fractional scaling
Browse files Browse the repository at this point in the history
Signed-off-by: Julian Orth <[email protected]>
  • Loading branch information
mahkoh committed May 24, 2024
1 parent 1d75a41 commit 2898470
Show file tree
Hide file tree
Showing 17 changed files with 532 additions and 108 deletions.
129 changes: 78 additions & 51 deletions src/java.desktop/unix/classes/sun/awt/wl/WLComponentPeer.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@
import java.util.Objects;
import java.util.function.Supplier;

import static sun.awt.wl.WLGraphicsConfig.SCALE120;

public class WLComponentPeer implements ComponentPeer {
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.wl.WLComponentPeer");
private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.awt.wl.focus.WLComponentPeer");
Expand Down Expand Up @@ -119,9 +121,12 @@ public class WLComponentPeer implements ComponentPeer {
boolean visible = false;

private final Object dataLock = new Object();
int width; // in native pixels, protected by dataLock
int height; // in native pixels, protected by dataLock
int wlBufferScale; // protected by dataLock
int javaWidth; // in java coordinates, protected by dataLock
int javaHeight; // in java coordinates, protected by dataLock
int surfaceWidth; // in surface coordinates, protected by dataLock
int surfaceHeight; // in surface coordinates, protected by dataLock
long scale120; // protected by dataLock
Long preferredScale120 = null; // protected by dataLock
double effectiveScale; // protected by dataLock

static {
Expand All @@ -135,31 +140,33 @@ public class WLComponentPeer implements ComponentPeer {
this.target = target;
this.background = target.getBackground();
Dimension size = constrainSize(target.getBounds().getSize());
width = size.width;
height = size.height;
javaWidth = size.width;
javaHeight = size.height;
final WLGraphicsConfig config = (WLGraphicsConfig)target.getGraphicsConfiguration();
wlBufferScale = config.getWlScale();
scale120 = config.getWlScale120();
effectiveScale = config.getEffectiveScale();
surfaceData = config.createSurfaceData(this);
nativePtr = nativeCreateFrame();
paintArea = new WLRepaintArea();
surfaceWidth = Math.max(javaUnitsToSurfaceUnits(javaWidth), 1);
surfaceHeight = Math.max(javaUnitsToSurfaceUnits(javaHeight), 1);

if (log.isLoggable(Level.FINE)) {
log.fine("WLComponentPeer: target=" + target + " with size=" + width + "x" + height);
log.fine("WLComponentPeer: target=" + target + " with size=" + javaWidth + "x" + javaHeight);
}
// TODO
// setup parent window for target
}

public int getWidth() {
public int getJavaWidth() {
synchronized (dataLock) {
return width;
return javaWidth;
}
}

public int getHeight() {
public int getJavaHeight() {
synchronized (dataLock) {
return height;
return javaHeight;
}
}

Expand All @@ -181,7 +188,7 @@ public void postPaintEvent(int x, int y, int w, int h) {

void postPaintEvent() {
if (isVisible()) {
postPaintEvent(0, 0, getWidth(), getHeight());
postPaintEvent(0, 0, getJavaWidth(), getJavaHeight());
}
}

Expand Down Expand Up @@ -268,8 +275,11 @@ protected void wlSetVisible(boolean v) {
if (v) {
String title = getTitle();
boolean isWlPopup = targetIsWlPopup();
int thisWidth = javaUnitsToSurfaceUnits(getWidth());
int thisHeight = javaUnitsToSurfaceUnits(getHeight());
int thisWidth, thisHeight;
synchronized (dataLock) {
thisWidth = surfaceWidth;
thisHeight = surfaceHeight;
}
boolean isModal = targetIsModal();

int state = (target instanceof Frame frame)
Expand Down Expand Up @@ -354,14 +364,15 @@ private boolean targetIsModal() {
}

void updateSurfaceData() {
// The scale parameter is ignored.
SurfaceData.convertTo(WLSurfaceDataExt.class, surfaceData).revalidate(
getBufferWidth(), getBufferHeight(), getBufferScale());
getBufferWidth(), getBufferHeight(), 1);
updateWindowGeometry();
}

void configureWLSurface() {
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine(String.format("%s is configured to %dx%d with %dx scale", this, getBufferWidth(), getBufferHeight(), getBufferScale()));
log.fine(String.format("%s is configured to %dx%d with %fx scale", this, getBufferWidth(), getBufferHeight(), getWlScale()));
}
updateSurfaceData();
}
Expand Down Expand Up @@ -471,8 +482,8 @@ public void setBounds(int newX, int newY, int newWidth, int newHeight, int op) {
if (sizeChanged) {
setSizeTo(newSize.width, newSize.height);
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine(String.format("%s is resizing its buffer to %dx%d with %dx scale",
this, getBufferWidth(), getBufferHeight(), getBufferScale()));
log.fine(String.format("%s is resizing its buffer to %dx%d with %fx scale",
this, getBufferWidth(), getBufferHeight(), getWlScale()));
}
updateSurfaceData();
layout();
Expand All @@ -485,18 +496,25 @@ public void setBounds(int newX, int newY, int newWidth, int newHeight, int op) {

private void setSizeTo(int newWidth, int newHeight) {
Dimension newSize = constrainSize(newWidth, newHeight);
final int surfaceWidth = Math.max(javaUnitsToSurfaceUnits(newSize.width), 1);
final int surfaceHeight = Math.max(javaUnitsToSurfaceUnits(newSize.height), 1);
performLocked(() -> {
nativeSetSurfaceSize(nativePtr, javaUnitsToSurfaceUnits(newSize.width), javaUnitsToSurfaceUnits(newSize.height));
nativeSetSurfaceSize(nativePtr, surfaceWidth, surfaceHeight);
});
synchronized (dataLock) {
this.width = newSize.width;
this.height = newSize.height;
this.javaWidth = newSize.width;
this.javaHeight = newSize.height;
this.surfaceWidth = surfaceWidth;
this.surfaceHeight = surfaceHeight;
}
}

private void repositionWlPopup(int newX, int newY) {
final int thisWidth = getWidth();
final int thisHeight = getHeight();
final int thisWidth, thisHeight;
synchronized (dataLock) {
thisWidth = surfaceWidth;
thisHeight = surfaceHeight;
}
performLocked(() -> {
Window popup = (Window) target;
final Component popupParent = AWTAccessor.getWindowAccessor().getPopupParent(popup);
Expand All @@ -523,32 +541,28 @@ private void repositionWlPopup(int newX, int newY) {

public Rectangle getVisibleBounds() {
synchronized(dataLock) {
return new Rectangle(0, 0, width, height);
return new Rectangle(0, 0, javaWidth, javaHeight);
}
}

/**
* Represents the scale ratio of Wayland's backing buffer in pixel units
* to surface units. Wayland events are generated in surface units, while
* painting should be performed in pixel units.
* The ratio is enforced with nativeSetSize().
*/
int getBufferScale() {
private double getWlScale() {
synchronized(dataLock) {
return wlBufferScale;
return (double)scale120 / SCALE120;
}
}

public int getBufferWidth() {
private int getBufferSize(int size) {
synchronized (dataLock) {
return (int)(width * effectiveScale);
return (int)((size * scale120 + SCALE120 / 2) / SCALE120);
}
}

public int getBufferWidth() {
return getBufferSize(surfaceWidth);
}

public int getBufferHeight() {
synchronized (dataLock) {
return (int)(height * effectiveScale);
}
return getBufferSize(surfaceHeight);
}

public Rectangle getBufferBounds() {
Expand All @@ -566,8 +580,8 @@ private void updateWindowGeometry() {
Rectangle nativeVisibleBounds = getVisibleBounds();
nativeVisibleBounds.x = javaUnitsToSurfaceUnits(nativeVisibleBounds.x);
nativeVisibleBounds.y = javaUnitsToSurfaceUnits(nativeVisibleBounds.y);
nativeVisibleBounds.width = javaUnitsToSurfaceUnits(nativeVisibleBounds.width);
nativeVisibleBounds.height = javaUnitsToSurfaceUnits(nativeVisibleBounds.height);
nativeVisibleBounds.width = Math.max(javaUnitsToSurfaceUnits(nativeVisibleBounds.width), 1);
nativeVisibleBounds.height = Math.max(javaUnitsToSurfaceUnits(nativeVisibleBounds.height), 1);
performLocked(() -> nativeSetWindowGeometry(nativePtr,
nativeVisibleBounds.x, nativeVisibleBounds.y,
nativeVisibleBounds.width, nativeVisibleBounds.height));
Expand Down Expand Up @@ -831,7 +845,7 @@ private void updateCursorImmediately(WLInputState inputState) {
WLComponentPeer peer = inputState.getPeer();
if (peer == null) return;
Cursor cursor = peer.getCursor(inputState.getPointerX(), inputState.getPointerY());
setCursor(cursor, getGraphicsDevice() != null ? getGraphicsDevice().getWlScale() : 1);
setCursor(cursor, (int)((scale120 + SCALE120 - 1) / SCALE120));
}

Cursor getCursor(int x, int y) {
Expand Down Expand Up @@ -942,16 +956,16 @@ public void applyShape(Region shape) {

@Override
public boolean updateGraphicsData(GraphicsConfiguration gc) {
final int newWlScale = ((WLGraphicsConfig)gc).getWlScale();
final long newScale120 = ((WLGraphicsConfig)gc).getWlScale120();

WLGraphicsDevice gd = ((WLGraphicsConfig) gc).getDevice();
gd.addWindow(this);
synchronized (dataLock) {
if (newWlScale != wlBufferScale) {
wlBufferScale = newWlScale;
if (newScale120 != scale120) {
scale120 = newScale120;
effectiveScale = ((WLGraphicsConfig)gc).getEffectiveScale();
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine(String.format("%s is updating buffer to %dx%d with %dx scale", this, getBufferWidth(), getBufferHeight(), wlBufferScale));
log.fine(String.format("%s is updating buffer to %dx%d with %fx scale", this, getBufferWidth(), getBufferHeight(), getWlScale()));
}
updateSurfaceData();
postPaintEvent();
Expand Down Expand Up @@ -1185,7 +1199,7 @@ int surfaceUnitsToJavaUnits(int value) {
return value;
} else {
synchronized (dataLock) {
return (int)(value * wlBufferScale / effectiveScale);
return (int)(value * scale120 / (effectiveScale * SCALE120));
}
}
}
Expand All @@ -1199,7 +1213,7 @@ int javaUnitsToSurfaceUnits(int value) {
return value;
} else {
synchronized (dataLock) {
return (int)(value * effectiveScale / wlBufferScale);
return (int)(value * effectiveScale * SCALE120 / scale120);
}
}
}
Expand Down Expand Up @@ -1285,8 +1299,17 @@ void notifyPopupDone() {
WLToolkit.postEvent(new WindowEvent((Window) target, WindowEvent.WINDOW_CLOSING));
}

void notifyPreferredScale(long scale120) {
synchronized (dataLock) {
if (preferredScale120 == null || preferredScale120 != scale120) {
preferredScale120 = scale120;
checkIfOnNewScreen();
}
}
}

private WLGraphicsDevice getGraphicsDevice() {
int scale = 0;
long scale120 = 0;
WLGraphicsDevice theDevice = null;
// AFAIK there's no way of knowing which WLGraphicsDevice is displaying
// the largest portion of this component, so choose the first in the ordered list
Expand All @@ -1295,8 +1318,8 @@ private WLGraphicsDevice getGraphicsDevice() {
// Wayland's output and are removed as soon as we have left.
synchronized (devices) {
for (WLGraphicsDevice gd : devices) {
if (gd.getWlScale() > scale) {
scale = gd.getWlScale();
if (gd.getWlScale120() > scale120) {
scale120 = gd.getWlScale120();
theDevice = gd;
}
}
Expand All @@ -1308,7 +1331,7 @@ private WLGraphicsDevice getGraphicsDevice() {
private void checkIfOnNewScreen() {
final WLGraphicsDevice newDevice = getGraphicsDevice();
if (newDevice != null) { // could be null when screens are being reconfigured
final GraphicsConfiguration gc = newDevice.getDefaultConfiguration();
final WLGraphicsConfig gc = (WLGraphicsConfig) newDevice.getDefaultConfiguration();
if (log.isLoggable(Level.FINE)) {
log.fine(this + " is on (possibly) new device " + newDevice);
}
Expand All @@ -1318,8 +1341,12 @@ private void checkIfOnNewScreen() {
newDevice.addWindow(this);
}
performUnlocked(() -> {
WLGraphicsConfig gcEffective = gc;
if (preferredScale120 != null) {
gcEffective = gc.withScale120(preferredScale120);
}
var acc = AWTAccessor.getComponentAccessor();
acc.setGraphicsConfiguration(target, gc);
acc.setGraphicsConfiguration(target, gcEffective);
});
}
}
Expand Down
20 changes: 10 additions & 10 deletions src/java.desktop/unix/classes/sun/awt/wl/WLFrameDecoration.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public Insets getInsets() {
public Rectangle getBounds() {
return isUndecorated
? new Rectangle(0, 0, 0, 0)
: new Rectangle(0, 0, peer.getWidth(), HEIGHT);
: new Rectangle(0, 0, peer.getJavaWidth(), HEIGHT);
}

public Dimension getMinimumSize() {
Expand All @@ -120,19 +120,19 @@ private boolean hasMaximizeButton() {
}

private Point getCloseButtonCenter() {
int width = peer.getWidth();
int width = peer.getJavaWidth();
return width >= HEIGHT ? new Point(width - HEIGHT / 2, HEIGHT / 2) : null;
}

private Point getMaximizeButtonCenter() {
if (!hasMaximizeButton()) return null;
int width = peer.getWidth();
int width = peer.getJavaWidth();
return width >= 2 * HEIGHT ? new Point(width - HEIGHT * 3 / 2, HEIGHT / 2) : null;
}

private Point getMinimizeButtonCenter() {
if (!hasMinimizeButton()) return null;
int width = peer.getWidth();
int width = peer.getJavaWidth();
int buttonSpaceWidth = getButtonSpaceWidth();
return width >= buttonSpaceWidth ? new Point(width - buttonSpaceWidth + HEIGHT / 2, HEIGHT / 2) : null;
}
Expand All @@ -147,8 +147,8 @@ private int getButtonSpaceWidth() {
public void paint(final Graphics g) {
if (isUndecorated) return;

int width = peer.getWidth();
int height = peer.getHeight();
int width = peer.getJavaWidth();
int height = peer.getJavaHeight();
if (width <= 0 || height <= 0) return;
Graphics2D g2d = (Graphics2D) g.create(0, 0, width, HEIGHT);
try {
Expand All @@ -160,7 +160,7 @@ public void paint(final Graphics g) {
}

private void doPaint(Graphics2D g) {
int width = peer.getWidth();
int width = peer.getJavaWidth();
String title = peer.getTitle();
Color foregroundColor = active ? ACTIVE_FOREGROUND : INACTIVE_FOREGROUND;

Expand Down Expand Up @@ -265,7 +265,7 @@ private boolean pressedInDragStartArea() {
pressedLocation.y >= 0 &&
pressedLocation.y < HEIGHT &&
pressedLocation.x >= 0 &&
pressedLocation.x < peer.getWidth() - getButtonSpaceWidth();
pressedLocation.x < peer.getJavaWidth() - getButtonSpaceWidth();
}

boolean processMouseEvent(MouseEvent e) {
Expand Down Expand Up @@ -334,12 +334,12 @@ private int getResizeEdges(int x, int y) {
int edges = 0;
if (x < RESIZE_EDGE_THICKNESS) {
edges |= XDG_TOPLEVEL_RESIZE_EDGE_LEFT;
} else if (x > peer.getWidth() - RESIZE_EDGE_THICKNESS) {
} else if (x > peer.getJavaWidth() - RESIZE_EDGE_THICKNESS) {
edges |= XDG_TOPLEVEL_RESIZE_EDGE_RIGHT;
}
if (y < RESIZE_EDGE_THICKNESS) {
edges |= XDG_TOPLEVEL_RESIZE_EDGE_TOP;
} else if (y > peer.getHeight() - RESIZE_EDGE_THICKNESS) {
} else if (y > peer.getJavaHeight() - RESIZE_EDGE_THICKNESS) {
edges |= XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM;
}
return edges;
Expand Down
4 changes: 2 additions & 2 deletions src/java.desktop/unix/classes/sun/awt/wl/WLFramePeer.java
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,8 @@ public void toBack() {

@Override
void notifyConfigured(int newXNative, int newYNative, int newWidthNative, int newHeightNative, boolean active, boolean maximized) {
int widthBefore = getWidth();
int heightBefore = getHeight();
int widthBefore = getJavaWidth();
int heightBefore = getJavaHeight();

super.notifyConfigured(newXNative, newYNative, newWidthNative, newHeightNative, active, maximized);

Expand Down
Loading

0 comments on commit 2898470

Please sign in to comment.