From a31f9df9dec48a9c2bc9de7c648e064e6ee6d235 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 21 May 2024 02:37:54 +0200 Subject: [PATCH 1/5] wayland: use weak surface manager references in property listeners Otherwise the component will hold on to the buffer which will leak it. Signed-off-by: Julian Orth --- .../java2d/wl/WLVolatileSurfaceManager.java | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/src/java.desktop/unix/classes/sun/java2d/wl/WLVolatileSurfaceManager.java b/src/java.desktop/unix/classes/sun/java2d/wl/WLVolatileSurfaceManager.java index 7de6b1906ff9..526da923e9fe 100644 --- a/src/java.desktop/unix/classes/sun/java2d/wl/WLVolatileSurfaceManager.java +++ b/src/java.desktop/unix/classes/sun/java2d/wl/WLVolatileSurfaceManager.java @@ -31,19 +31,20 @@ import java.awt.ImageCapabilities; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.lang.ref.Cleaner; +import java.lang.ref.WeakReference; import sun.awt.image.SunVolatileImage; import sun.awt.image.VolatileSurfaceManager; import sun.java2d.SurfaceData; -public class WLVolatileSurfaceManager extends VolatileSurfaceManager implements PropertyChangeListener { - private static final String SCALE_PROPERTY_NAME = "graphicsContextScaleTransform"; - +public class WLVolatileSurfaceManager extends VolatileSurfaceManager { public WLVolatileSurfaceManager(SunVolatileImage vImg, Object context) { super(vImg, context); + Component component = vImg.getComponent(); if (component != null) { - component.addPropertyChangeListener(SCALE_PROPERTY_NAME, this); + ChangeListener.register(component, this); } } @@ -62,10 +63,30 @@ public ImageCapabilities getCapabilities(GraphicsConfiguration gc) { return new ImageCapabilities(false); } - @Override - public void propertyChange(PropertyChangeEvent evt) { - assert SCALE_PROPERTY_NAME.equals(evt.getPropertyName()); + private static class ChangeListener implements PropertyChangeListener { + private static final String SCALE_PROPERTY_NAME = "graphicsContextScaleTransform"; + private static final Cleaner cleaner = Cleaner.create(); + + private final WeakReference manager; + + private ChangeListener(WLVolatileSurfaceManager manager) { + this.manager = new WeakReference<>(manager); + } + + static void register(Component component, WLVolatileSurfaceManager manager) { + ChangeListener listener = new ChangeListener(manager); + component.addPropertyChangeListener(SCALE_PROPERTY_NAME, listener); + cleaner.register(manager, () -> component.removePropertyChangeListener(SCALE_PROPERTY_NAME, listener)); + } - displayChanged(); + @Override + public void propertyChange(PropertyChangeEvent evt) { + assert SCALE_PROPERTY_NAME.equals(evt.getPropertyName()); + + WLVolatileSurfaceManager manager = this.manager.get(); + if (manager != null) { + manager.displayChanged(); + } + } } } From bc4503de8b5d61b587921feaa23a34e516a0879b Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 21 May 2024 06:34:09 +0200 Subject: [PATCH 2/5] wayland: defer commit until double-buffered state has been flushed Signed-off-by: Julian Orth --- src/java.desktop/share/classes/javax/swing/JComponent.java | 3 +++ src/java.desktop/share/classes/javax/swing/RepaintManager.java | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/java.desktop/share/classes/javax/swing/JComponent.java b/src/java.desktop/share/classes/javax/swing/JComponent.java index 1acd44c6f76a..5235f1e1c22c 100644 --- a/src/java.desktop/share/classes/javax/swing/JComponent.java +++ b/src/java.desktop/share/classes/javax/swing/JComponent.java @@ -1112,6 +1112,7 @@ public void paint(Graphics g) { } finally { repaintManager.endPaint(); } + repaintManager.afterPaint(this); } else { // Will occasionally happen in 1.2, especially when printing. @@ -1162,6 +1163,7 @@ void paintForceDoubleBuffered(Graphics g) { rm.endPaint(); setFlag(IS_REPAINTING, false); } + rm.afterPaint(this); } /** @@ -5272,6 +5274,7 @@ void _paintImmediately(int x, int y, int w, int h) { } finally { rm.endPaint(); } + rm.afterPaint(this); } else { g.setClip(paintImmediatelyClip.x, paintImmediatelyClip.y, paintImmediatelyClip.width, paintImmediatelyClip.height); diff --git a/src/java.desktop/share/classes/javax/swing/RepaintManager.java b/src/java.desktop/share/classes/javax/swing/RepaintManager.java index 12da1d8bdd53..52065d6ca8b3 100644 --- a/src/java.desktop/share/classes/javax/swing/RepaintManager.java +++ b/src/java.desktop/share/classes/javax/swing/RepaintManager.java @@ -1354,7 +1354,9 @@ void paint(JComponent paintingComponent, g.setClip(x, y, w, h); paintingComponent.paintToOffscreen(g, x, y, w, h, x + w, y + h); } + } + void afterPaint(JComponent paintingComponent) { if (Toolkit.getDefaultToolkit() instanceof SunToolkit tk) { final Window window = SwingUtilities.getWindowAncestor(paintingComponent); if (window != null && tk.needUpdateWindowAfterPaint()) { From 81776b960479edf4ad1533543fda6aac1d2e351a Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 21 May 2024 06:46:30 +0200 Subject: [PATCH 3/5] wayland: don't commit partially incomplete buffers Signed-off-by: Julian Orth --- .../unix/native/common/java2d/wl/WLBuffers.c | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/java.desktop/unix/native/common/java2d/wl/WLBuffers.c b/src/java.desktop/unix/native/common/java2d/wl/WLBuffers.c index 145ad55ccd4e..9e3b19c4127e 100644 --- a/src/java.desktop/unix/native/common/java2d/wl/WLBuffers.c +++ b/src/java.desktop/unix/native/common/java2d/wl/WLBuffers.c @@ -221,6 +221,7 @@ struct WLDrawBuffer { jint width; jint height; jboolean resizePending; /// The next access to the buffer requires DrawBufferResize() + jboolean committed; /// Whether the content of the draw buffer has been committed size_t bytesAllocated; /// the size of the memory segment pointed to by data pixel_t * data; /// Actual pixels of the buffer DamageList * damageList; /// Areas of the buffer that may have been altered @@ -410,15 +411,15 @@ SurfaceBufferDestroy(WLSurfaceBuffer * buffer) static WLSurfaceBuffer * SurfaceBufferCreate(WLSurfaceBufferManager * manager) { + ASSERT_DRAW_LOCK_IS_HELD(manager); + WLBufferTrace(manager, "SurfaceBufferCreate"); WLSurfaceBuffer * buffer = calloc(1, sizeof(WLSurfaceBuffer)); if (!buffer) return NULL; - MUTEX_LOCK(manager->drawLock); buffer->width = manager->bufferForDraw.width; buffer->height = manager->bufferForDraw.height; - MUTEX_UNLOCK(manager->drawLock); buffer->bytesAllocated = SurfaceBufferSizeInBytes(buffer); buffer->wlPool = CreateShmPool(buffer->bytesAllocated, "jwlshm", (void**)&buffer->data, &buffer->fd); @@ -455,11 +456,10 @@ static bool SurfaceBufferNeedsResize(WLSurfaceBufferManager * manager, WLSurfaceBuffer* buffer) { assert(buffer != NULL); + ASSERT_DRAW_LOCK_IS_HELD(manager); - MUTEX_LOCK(manager->drawLock); jint newWidth = manager->bufferForDraw.width; jint newHeight = manager->bufferForDraw.height; - MUTEX_UNLOCK(manager->drawLock); return newWidth != buffer->width || newHeight != buffer->height; } @@ -469,11 +469,10 @@ SurfaceBufferResize(WLSurfaceBufferManager * manager, WLSurfaceBuffer* buffer) { assert(buffer != NULL); assert(buffer->wlBuffer != NULL); + ASSERT_DRAW_LOCK_IS_HELD(manager); - MUTEX_LOCK(manager->drawLock); jint newWidth = manager->bufferForDraw.width; jint newHeight = manager->bufferForDraw.height; - MUTEX_UNLOCK(manager->drawLock); wl_buffer_destroy(buffer->wlBuffer); buffer->wlBuffer = NULL; @@ -660,9 +659,13 @@ TrySendShowBufferToWayland(WLSurfaceBufferManager * manager, bool sendNow) { WLBufferTrace(manager, "TrySendShowBufferToWayland(%s)", sendNow ? "now" : "later"); - sendNow = sendNow && ShowBufferIsAvailable(manager); + MUTEX_LOCK(manager->drawLock); + sendNow = sendNow && manager->bufferForDraw.committed && ShowBufferIsAvailable(manager); if (sendNow) { CopyDrawBufferToShowBuffer(manager); + } + MUTEX_UNLOCK(manager->drawLock); + if (sendNow) { SendShowBufferToWayland(manager); } else { ScheduleFrameCallback(manager); @@ -826,10 +829,8 @@ static void CopyDrawBufferToShowBuffer(WLSurfaceBufferManager * manager) { ASSERT_SHOW_LOCK_IS_HELD(manager); - MUTEX_LOCK(manager->drawLock); if (manager->bufferForShow.wlSurfaceBuffer == NULL || manager->bufferForDraw.data == NULL) { - MUTEX_UNLOCK(manager->drawLock); return; } @@ -870,8 +871,6 @@ CopyDrawBufferToShowBuffer(WLSurfaceBufferManager * manager) jlong endTime = GetJavaTimeNanos(); WLBufferTrace(manager, "CopyDrawBufferToShowBuffer: copied %d area(s) in %lldns", count, endTime - startTime); - - MUTEX_UNLOCK(manager->drawLock); } static void @@ -970,6 +969,7 @@ WLSBM_Create(jint width, manager->bufferForDraw.manager = manager; manager->bufferForDraw.resizePending = true; + manager->bufferForDraw.committed = false; J2dTrace3(J2D_TRACE_INFO, "WLSBM_Create: created %p for %dx%d px\n", manager, width, height); return manager; @@ -1082,6 +1082,10 @@ WLSBM_SurfaceCommit(WLSurfaceBufferManager * manager) manager->wlSurface, frameCallbackScheduled ? "wait for frame" : "now"); + MUTEX_LOCK(manager->drawLock); + manager->bufferForDraw.committed = true; + MUTEX_UNLOCK(manager->drawLock); + if (manager->wlSurface && !frameCallbackScheduled) { // Don't always send the frame immediately so as not to overwhelm Wayland TrySendShowBufferToWayland(manager, manager->sendBufferASAP); @@ -1099,6 +1103,7 @@ WLSB_Damage(WLDrawBuffer * buffer, jint x, jint y, jint width, jint height) assert(x + width <= buffer->manager->bufferForDraw.width); assert(y + height <= buffer->manager->bufferForDraw.height); + buffer->committed = false; buffer->damageList = DamageList_Add(buffer->damageList, x, y, width, height); WLBufferTrace(buffer->manager, "WLSB_Damage (at %d, %d %dx%d)", x, y, width, height); } @@ -1118,12 +1123,13 @@ WLSBM_SizeChangeTo(WLSurfaceBufferManager * manager, jint width, jint height) return; } - MUTEX_LOCK(manager->drawLock); MUTEX_LOCK(manager->showLock); + MUTEX_LOCK(manager->drawLock); if (manager->bufferForDraw.width != width || manager->bufferForDraw.height != height) { manager->bufferForDraw.width = width; manager->bufferForDraw.height = height; manager->bufferForDraw.resizePending = true; + manager->bufferForDraw.committed = false; // Send the buffer at the nearest commit or else Mutter may not remember // the latest size of the window. @@ -1136,8 +1142,8 @@ WLSBM_SizeChangeTo(WLSurfaceBufferManager * manager, jint width, jint height) WLBufferTrace(manager, "WLSBM_SizeChangeTo %dx%d", width, height); } - MUTEX_UNLOCK(manager->showLock); MUTEX_UNLOCK(manager->drawLock); + MUTEX_UNLOCK(manager->showLock); } #endif From e33dbd5bd2016d3616754b5dc7720ebcf8b5a699 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 21 May 2024 11:51:35 +0200 Subject: [PATCH 4/5] java2d: implement drawImage as a copy if src & dst have the same scale This is required for fractional scaling since the generic code rounds too much to perform pixel-perfect copies at all scales. Signed-off-by: Julian Orth --- .../classes/sun/java2d/SunGraphics2D.java | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java index 88d16e1e8f44..b3addaad96fe 100644 --- a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java +++ b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java @@ -3469,6 +3469,13 @@ public boolean drawImage(Image img, return true; } + boolean blitImageDrawn = drawImageBlit(img, dx1, dy1, dx2, dy2, + sx1, sy1, sx2, sy2, + bgcolor, observer); + if (blitImageDrawn) { + return true; + } + Boolean hidpiImageDrawn = drawHiDPIImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer, null); @@ -3525,6 +3532,52 @@ public boolean drawImage(Image img, } } + /** + * Tries to draw the image via copyImage if the image is compatible with + * the current transformation. That is, the scaling of the image is the + * same as the scaling of the transformation and the transformation does + * not contain any rotation. This leads to better results when the scaling + * is fractional. + */ + private boolean drawImageBlit(Image img, + int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, + Color bgcolor, ImageObserver observer) { + if (dx2 - dx1 == sx2 - sx1 && + dy2 - dy1 == sy2 - sy1 && + sx1 <= sx2 && + sy1 <= sy2 && + dx1 <= dx2 && + dy1 <= dy2 && + transformState <= TRANSFORM_TRANSLATESCALE + ) { + final SurfaceData sd; + try { + sd = SurfaceManager.getManager(img).getPrimarySurfaceData(); + } catch (InvalidPipeException e) { + return false; + } + if (sd.getDefaultScaleX() == transform.getScaleX() && sd.getDefaultScaleY() == transform.getScaleY()) { + dx1 = Region.clipRound(transform.getScaleX() * dx1); + dx2 = Region.clipRound(transform.getScaleX() * dx2); + dy1 = Region.clipRound(transform.getScaleY() * dy1); + dy2 = Region.clipRound(transform.getScaleY() * dy2); + sx1 = Region.clipRound(transform.getScaleX() * sx1); + sy1 = Region.clipRound(transform.getScaleY() * sy1); + int width = dx2 - dx1; + int height = dy2 - dy1; + AffineTransform old = new AffineTransform(transform); + setTransform(AffineTransform.getTranslateInstance(transform.getTranslateX(), transform.getTranslateY())); + try { + return copyImage(img, dx1, dy1, sx1, sy1, width, height, bgcolor, observer); + } finally { + setTransform(old); + } + } + } + return false; + } + /** * Draw an image, applying a transform from image space into user space * before drawing. From 705a2e11b3f3fd05b24330ec7a1e77711028b303 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 21 May 2024 07:14:32 +0200 Subject: [PATCH 5/5] wayland: implement fractional scaling Signed-off-by: Julian Orth --- .../classes/sun/awt/wl/WLComponentPeer.java | 129 +++++---- .../classes/sun/awt/wl/WLFrameDecoration.java | 20 +- .../unix/classes/sun/awt/wl/WLFramePeer.java | 4 +- .../classes/sun/awt/wl/WLGraphicsConfig.java | 31 +- .../classes/sun/awt/wl/WLGraphicsDevice.java | 22 +- .../sun/awt/wl/WLGraphicsEnvironment.java | 12 +- .../unix/classes/sun/awt/wl/WLInputState.java | 4 +- .../sun/awt/wl/WLSMGraphicsConfig.java | 13 +- .../unix/classes/sun/awt/wl/WLWindowPeer.java | 1 + .../sun/java2d/vulkan/WLVKGraphicsConfig.java | 13 +- .../sun/java2d/vulkan/WLVKSurfaceData.java | 4 +- .../native/libawt_wlawt/WLComponentPeer.c | 41 +++ .../libawt_wlawt/WLGraphicsEnvironment.c | 4 +- .../unix/native/libawt_wlawt/WLToolkit.c | 3 + .../unix/native/libawt_wlawt/WLToolkit.h | 2 + .../fractional-scale-v1-client-protocol.c | 73 +++++ .../fractional-scale-v1-client-protocol.h | 264 ++++++++++++++++++ 17 files changed, 532 insertions(+), 108 deletions(-) create mode 100644 src/java.desktop/unix/native/libawt_wlawt/fractional-scale-v1-client-protocol.c create mode 100644 src/java.desktop/unix/native/libawt_wlawt/fractional-scale-v1-client-protocol.h diff --git a/src/java.desktop/unix/classes/sun/awt/wl/WLComponentPeer.java b/src/java.desktop/unix/classes/sun/awt/wl/WLComponentPeer.java index cbbe43c12dfe..1ff5a1b411ef 100644 --- a/src/java.desktop/unix/classes/sun/awt/wl/WLComponentPeer.java +++ b/src/java.desktop/unix/classes/sun/awt/wl/WLComponentPeer.java @@ -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"); @@ -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 { @@ -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; } } @@ -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()); } } @@ -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) @@ -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(); } @@ -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(); @@ -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); @@ -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() { @@ -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)); @@ -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) { @@ -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(); @@ -1185,7 +1199,7 @@ int surfaceUnitsToJavaUnits(int value) { return value; } else { synchronized (dataLock) { - return (int)(value * wlBufferScale / effectiveScale); + return (int)(value * scale120 / (effectiveScale * SCALE120)); } } } @@ -1199,7 +1213,7 @@ int javaUnitsToSurfaceUnits(int value) { return value; } else { synchronized (dataLock) { - return (int)(value * effectiveScale / wlBufferScale); + return (int)(value * effectiveScale * SCALE120 / scale120); } } } @@ -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 @@ -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; } } @@ -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); } @@ -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); }); } } diff --git a/src/java.desktop/unix/classes/sun/awt/wl/WLFrameDecoration.java b/src/java.desktop/unix/classes/sun/awt/wl/WLFrameDecoration.java index f459160a5291..27d06482a04a 100644 --- a/src/java.desktop/unix/classes/sun/awt/wl/WLFrameDecoration.java +++ b/src/java.desktop/unix/classes/sun/awt/wl/WLFrameDecoration.java @@ -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() { @@ -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; } @@ -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 { @@ -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; @@ -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) { @@ -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; diff --git a/src/java.desktop/unix/classes/sun/awt/wl/WLFramePeer.java b/src/java.desktop/unix/classes/sun/awt/wl/WLFramePeer.java index 292a46c6574d..09caa68c597a 100644 --- a/src/java.desktop/unix/classes/sun/awt/wl/WLFramePeer.java +++ b/src/java.desktop/unix/classes/sun/awt/wl/WLFramePeer.java @@ -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); diff --git a/src/java.desktop/unix/classes/sun/awt/wl/WLGraphicsConfig.java b/src/java.desktop/unix/classes/sun/awt/wl/WLGraphicsConfig.java index 9358f813c2fd..aef033d2fc2c 100644 --- a/src/java.desktop/unix/classes/sun/awt/wl/WLGraphicsConfig.java +++ b/src/java.desktop/unix/classes/sun/awt/wl/WLGraphicsConfig.java @@ -36,22 +36,24 @@ import sun.java2d.loops.SurfaceType; public abstract class WLGraphicsConfig extends GraphicsConfiguration { - private final WLGraphicsDevice device; - private final int width; - private final int height; - private final int wlScale; // as reported by Wayland + public final static long SCALE120 = 120; + + protected final WLGraphicsDevice device; + protected final int width; + protected final int height; + private final long wlScale120; // as reported by Wayland private final double effectiveScale; // as enforced by Java - protected WLGraphicsConfig(WLGraphicsDevice device, int width, int height, int wlScale) { + protected WLGraphicsConfig(WLGraphicsDevice device, int width, int height, long scale120) { this.device = device; this.width = width; this.height = height; - this.wlScale = wlScale; - this.effectiveScale = WLGraphicsEnvironment.effectiveScaleFrom(wlScale); + this.wlScale120 = scale120; + this.effectiveScale = WLGraphicsEnvironment.effectiveScaleFrom((double)scale120 / SCALE120); } - boolean differsFrom(int width, int height, int scale) { - return width != this.width || height != this.height || scale != this.wlScale; + boolean differsFrom(int width, int height, long scale120) { + return width != this.width || height != this.height || scale120 != this.wlScale120; } @Override @@ -89,14 +91,14 @@ public Rectangle getBounds() { } /** - * Returns the preferred Wayland buffer scale for this display configuration. + * Returns the wayland scale in base 1/120. */ - public int getWlScale() { - return wlScale; + public long getWlScale120() { + return wlScale120; } /** - * Returns the effective scale, which can differ from the buffer scale + * Returns the effective scale, which can differ from the wayland scale * if overridden with the sun.java2d.uiScale system property. */ public double getEffectiveScale() { @@ -105,9 +107,10 @@ public double getEffectiveScale() { public abstract SurfaceType getSurfaceType(); public abstract SurfaceData createSurfaceData(WLComponentPeer peer); + public abstract WLGraphicsConfig withScale120(long scale120); @Override public String toString() { - return String.format("%dx%d %dx scale", width, height, wlScale); + return String.format("%dx%d %fx scale", width, height, (double) wlScale120 / SCALE120); } } diff --git a/src/java.desktop/unix/classes/sun/awt/wl/WLGraphicsDevice.java b/src/java.desktop/unix/classes/sun/awt/wl/WLGraphicsDevice.java index 608f8a5cc012..5c3fb08658f7 100644 --- a/src/java.desktop/unix/classes/sun/awt/wl/WLGraphicsDevice.java +++ b/src/java.desktop/unix/classes/sun/awt/wl/WLGraphicsDevice.java @@ -89,27 +89,27 @@ int getID() { return wlID; } - void updateConfiguration(String name, int width, int height, int scale) { + void updateConfiguration(String name, int width, int height, long scale120) { this.name = name; WLGraphicsConfig config = defaultConfig; // Note that all configs are of equal size and scale - if (config == null || config.differsFrom(width, height, scale)) { + if (config == null || config.differsFrom(width, height, scale120)) { GraphicsConfiguration[] newConfigs; WLGraphicsConfig newDefaultConfig; // It is necessary to create a new object whenever config changes as its // identity is used to detect changes in scale, among other things. if (WLGraphicsEnvironment.isVulkanEnabled()) { - newDefaultConfig = WLVKGraphicsConfig.getConfig(this, width, height, scale); + newDefaultConfig = WLVKGraphicsConfig.getConfig(this, width, height, scale120); newConfigs = new GraphicsConfiguration[1]; newConfigs[0] = newDefaultConfig; } else { // TODO: Actually, Wayland may support a lot more shared memory buffer configurations, need to // subscribe to the wl_shm:format event and get the list from there. - newDefaultConfig = WLSMGraphicsConfig.getConfig(this, width, height, scale, false); + newDefaultConfig = WLSMGraphicsConfig.getConfig(this, width, height, scale120, false); newConfigs = new GraphicsConfiguration[2]; newConfigs[0] = newDefaultConfig; - newConfigs[1] = WLSMGraphicsConfig.getConfig(this, width, height, scale, true); + newConfigs[1] = WLSMGraphicsConfig.getConfig(this, width, height, scale120, true); } configs = newConfigs; @@ -145,18 +145,18 @@ void invalidate(WLGraphicsDevice similarDevice) { this.x = similarDevice.x; this.y = similarDevice.y; - int newScale = similarDevice.getWlScale(); + long newScale120 = similarDevice.getWlScale120(); Rectangle newBounds = similarDevice.defaultConfig.getBounds(); - updateConfiguration(similarDevice.name, newBounds.width, newBounds.height, newScale); + updateConfiguration(similarDevice.name, newBounds.width, newBounds.height, newScale120); } public static WLGraphicsDevice createWithConfiguration(int id, String name, int x, int y, int width, int height, int widthMm, int heightMm, - int scale) { + long scale120) { WLGraphicsDevice device = new WLGraphicsDevice(id, x, y, widthMm, heightMm); - device.updateConfiguration(name, width, height, scale); + device.updateConfiguration(name, width, height, scale120); return device; } @@ -207,8 +207,8 @@ public GraphicsConfiguration getDefaultConfiguration() { return defaultConfig; } - int getWlScale() { - return defaultConfig.getWlScale(); + long getWlScale120() { + return defaultConfig.getWlScale120(); } int getResolution() { diff --git a/src/java.desktop/unix/classes/sun/awt/wl/WLGraphicsEnvironment.java b/src/java.desktop/unix/classes/sun/awt/wl/WLGraphicsEnvironment.java index d7c3fe1639c2..fb68a07484a7 100644 --- a/src/java.desktop/unix/classes/sun/awt/wl/WLGraphicsEnvironment.java +++ b/src/java.desktop/unix/classes/sun/awt/wl/WLGraphicsEnvironment.java @@ -128,11 +128,11 @@ public boolean isDisplayLocal() { private void notifyOutputConfigured(String name, String make, String model, int wlID, int x, int y, int width, int height, int widthMm, int heightMm, - int subpixel, int transform, int scale) { + int subpixel, int transform, long scale120) { // Called from native code whenever a new output appears or an existing one changes its properties // NB: initially called during WLToolkit.initIDs() on the main thread; later on EDT. if (log.isLoggable(Level.FINE)) { - log.fine(String.format("Output configured id=%d at (%d, %d) %dx%d %dx scale", wlID, x, y, width, height, scale)); + log.fine(String.format("Output configured id=%d at (%d, %d) %dx%d %fx scale", wlID, x, y, width, height, scale120 / 120.0)); } String humanID = @@ -147,10 +147,10 @@ private void notifyOutputConfigured(String name, String make, String model, int newOutput = false; if (gd.isSameDeviceAs(wlID, x, y)) { // These coordinates and the size are not scaled. - gd.updateConfiguration(humanID, width, height, scale); + gd.updateConfiguration(humanID, width, height, scale120); } else { final WLGraphicsDevice updatedDevice = WLGraphicsDevice.createWithConfiguration(wlID, humanID, - x, y, width, height, widthMm, heightMm, scale); + x, y, width, height, widthMm, heightMm, scale120); devices.set(i, updatedDevice); gd.invalidate(updatedDevice); } @@ -159,7 +159,7 @@ private void notifyOutputConfigured(String name, String make, String model, int } if (newOutput) { final WLGraphicsDevice gd = WLGraphicsDevice.createWithConfiguration(wlID, humanID, x, y, - width, height, widthMm, heightMm, scale); + width, height, widthMm, heightMm, scale120); devices.add(gd); } } @@ -253,7 +253,7 @@ private void updateTotalDisplayBounds() { } } - static double effectiveScaleFrom(int displayScale) { + static double effectiveScaleFrom(double displayScale) { return debugScaleEnabled ? SunGraphicsEnvironment.getDebugScale() : displayScale; } diff --git a/src/java.desktop/unix/classes/sun/awt/wl/WLInputState.java b/src/java.desktop/unix/classes/sun/awt/wl/WLInputState.java index c5904eac617f..15dfd6c891ef 100644 --- a/src/java.desktop/unix/classes/sun/awt/wl/WLInputState.java +++ b/src/java.desktop/unix/classes/sun/awt/wl/WLInputState.java @@ -266,9 +266,9 @@ public boolean isPointerOverPeer() { WLComponentPeer peer = getPeer(); if (peer != null) { return x >= 0 - && x < peer.getWidth() + && x < peer.getJavaWidth() && y >= 0 - && y < peer.getHeight(); + && y < peer.getJavaHeight(); } } return true; diff --git a/src/java.desktop/unix/classes/sun/awt/wl/WLSMGraphicsConfig.java b/src/java.desktop/unix/classes/sun/awt/wl/WLSMGraphicsConfig.java index b02009716d1b..0f8235e5aea9 100644 --- a/src/java.desktop/unix/classes/sun/awt/wl/WLSMGraphicsConfig.java +++ b/src/java.desktop/unix/classes/sun/awt/wl/WLSMGraphicsConfig.java @@ -55,9 +55,9 @@ public class WLSMGraphicsConfig extends WLGraphicsConfig { private WLSMGraphicsConfig(WLGraphicsDevice device, int width, int height, - int scale, + long scale120, boolean translucencyCapable) { - super(device, width, height, scale); + super(device, width, height, scale120); this.translucencyCapable = translucencyCapable; this.colorModel = colorModelFor(translucencyCapable ? Transparency.TRANSLUCENT : Transparency.OPAQUE); // Note: GNOME Shell definitely expects alpha values to be pre-multiplied @@ -67,9 +67,9 @@ private WLSMGraphicsConfig(WLGraphicsDevice device, public static WLSMGraphicsConfig getConfig(WLGraphicsDevice device, int width, int height, - int scale, + long scale120, boolean translucencyCapable) { - var newConfig = new WLSMGraphicsConfig(device, width, height, scale, translucencyCapable); + var newConfig = new WLSMGraphicsConfig(device, width, height, scale120, translucencyCapable); if (log.isLoggable(PlatformLogger.Level.FINE)) { log.fine("New shared memory config " + newConfig); } @@ -110,6 +110,11 @@ public SurfaceData createSurfaceData(WLComponentPeer peer) { return WLSMSurfaceData.createData(peer, this); } + @Override + public WLGraphicsConfig withScale120(long scale120) { + return new WLSMGraphicsConfig(device, width, height, scale120, translucencyCapable); + } + public int getWlShmFormat() { // The value is one of enum wl_shm_format from wayland-client-protocol.h return translucencyCapable ? WL_SHM_FORMAT_ARGB8888 : WL_SHM_FORMAT_XRGB8888; diff --git a/src/java.desktop/unix/classes/sun/awt/wl/WLWindowPeer.java b/src/java.desktop/unix/classes/sun/awt/wl/WLWindowPeer.java index dc6b239ec3ea..946bd12a1f12 100644 --- a/src/java.desktop/unix/classes/sun/awt/wl/WLWindowPeer.java +++ b/src/java.desktop/unix/classes/sun/awt/wl/WLWindowPeer.java @@ -33,6 +33,7 @@ import java.awt.Insets; import java.awt.SystemColor; import java.awt.Window; +import java.awt.event.InvocationEvent; import java.awt.event.WindowEvent; import java.awt.peer.ComponentPeer; import java.awt.peer.WindowPeer; diff --git a/src/java.desktop/unix/classes/sun/java2d/vulkan/WLVKGraphicsConfig.java b/src/java.desktop/unix/classes/sun/java2d/vulkan/WLVKGraphicsConfig.java index 9be494986468..58af7fc904c4 100644 --- a/src/java.desktop/unix/classes/sun/java2d/vulkan/WLVKGraphicsConfig.java +++ b/src/java.desktop/unix/classes/sun/java2d/vulkan/WLVKGraphicsConfig.java @@ -74,8 +74,8 @@ public final class WLVKGraphicsConfig extends WLGraphicsConfig private static native long getVKConfigInfo(); - public WLVKGraphicsConfig(WLGraphicsDevice device, int width, int height, int scale, ContextCapabilities vkCaps) { - super(device, width, height, scale); + public WLVKGraphicsConfig(WLGraphicsDevice device, int width, int height, long scale120, ContextCapabilities vkCaps) { + super(device, width, height, scale120); this.vkCaps = vkCaps; context = new VKContext(VKRenderQueue.getInstance()); } @@ -85,13 +85,13 @@ public Object getProxyKey() { return this; } - public static WLVKGraphicsConfig getConfig(WLGraphicsDevice device, int width, int height, int scale) + public static WLVKGraphicsConfig getConfig(WLGraphicsDevice device, int width, int height, long scale120) { ContextCapabilities caps = new VKContext.VKContextCaps( CAPS_PS30 | CAPS_PS20 | CAPS_RT_TEXTURE_ALPHA | CAPS_RT_TEXTURE_OPAQUE | CAPS_MULTITEXTURE | CAPS_TEXNONPOW2 | CAPS_TEXNONSQUARE, null); - return new WLVKGraphicsConfig(device, width, height, scale, caps); + return new WLVKGraphicsConfig(device, width, height, scale120, caps); } /** @@ -212,6 +212,11 @@ public SurfaceData createSurfaceData(WLComponentPeer peer) { return WLVKSurfaceData.createData(peer); } + @Override + public WLGraphicsConfig withScale120(long scale120) { + return new WLVKGraphicsConfig(device, width, height, scale120, vkCaps); + } + /** * {@inheritDoc} * diff --git a/src/java.desktop/unix/classes/sun/java2d/vulkan/WLVKSurfaceData.java b/src/java.desktop/unix/classes/sun/java2d/vulkan/WLVKSurfaceData.java index ace52d86058f..c2fd70ad803d 100644 --- a/src/java.desktop/unix/classes/sun/java2d/vulkan/WLVKSurfaceData.java +++ b/src/java.desktop/unix/classes/sun/java2d/vulkan/WLVKSurfaceData.java @@ -60,8 +60,8 @@ protected WLVKSurfaceData(WLComponentPeer peer, WLVKGraphicsConfig gc, final int backgroundRGB = peer.getBackground() != null ? peer.getBackground().getRGB() : 0; - int scale = ((WLGraphicsConfig)peer.getGraphicsConfiguration()).getWlScale(); - initOps(peer.getBufferWidth(), peer.getBufferHeight(), scale, backgroundRGB); + // scale parameter is unused + initOps(peer.getBufferWidth(), peer.getBufferHeight(), 1, backgroundRGB); } @Override diff --git a/src/java.desktop/unix/native/libawt_wlawt/WLComponentPeer.c b/src/java.desktop/unix/native/libawt_wlawt/WLComponentPeer.c index a9d36483c993..92f1cc749fc3 100644 --- a/src/java.desktop/unix/native/libawt_wlawt/WLComponentPeer.c +++ b/src/java.desktop/unix/native/libawt_wlawt/WLComponentPeer.c @@ -44,6 +44,7 @@ static jmethodID notifyConfiguredMID; static jmethodID notifyEnteredOutputMID; static jmethodID notifyLeftOutputMID; static jmethodID notifyPopupDoneMID; +static jmethodID notifyPreferredScaleMID; struct activation_token_list_item { struct xdg_activation_token_v1 *token; @@ -91,6 +92,7 @@ struct WLFrame { jobject nativeFramePeer; // weak reference struct wl_surface *wl_surface; struct wp_viewport *wp_viewport; + struct wp_fractional_scale_v1 *wp_fractional_scale; struct xdg_surface *xdg_surface; struct gtk_surface1 *gtk_surface; struct WLFrame *parent; @@ -308,6 +310,28 @@ static const struct xdg_activation_token_v1_listener xdg_activation_token_v1_lis .done = xdg_activation_token_v1_done, }; +static void wp_fractional_scale_preferred_scale(void *data, + struct wp_fractional_scale_v1 *wp_fractional_scale_v1, + uint32_t scale) { + J2dTrace2(J2D_TRACE_INFO, "WLComponentPeer: wp_fractional_scale_preferred_scale(%p, %u)\n", + wp_fractional_scale_v1, scale); + + struct WLFrame *wlFrame = data; + assert(wlFrame); + + JNIEnv *env = getEnv(); + const jobject nativeFramePeer = (*env)->NewLocalRef(env, wlFrame->nativeFramePeer); + if (nativeFramePeer) { + (*env)->CallVoidMethod(env, nativeFramePeer, notifyPreferredScaleMID, (int64_t)scale); + (*env)->DeleteLocalRef(env, nativeFramePeer); + JNU_CHECK_EXCEPTION(env); + } +} + +static const struct wp_fractional_scale_v1_listener wp_fractional_scale_listener = { + .preferred_scale = wp_fractional_scale_preferred_scale, +}; + JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_initIDs (JNIEnv *env, jclass clazz) @@ -324,6 +348,9 @@ Java_sun_awt_wl_WLComponentPeer_initIDs CHECK_NULL_THROW_IE(env, notifyPopupDoneMID = (*env)->GetMethodID(env, clazz, "notifyPopupDone", "()V"), "Failed to find method WLComponentPeer.notifyPopupDone"); + CHECK_NULL_THROW_IE(env, + notifyPreferredScaleMID = (*env)->GetMethodID(env, clazz, "notifyPreferredScale", "(J)V"), + "Failed to find method WLComponentPeer.notifyPreferredScale"); } JNIEXPORT void JNICALL @@ -454,6 +481,11 @@ Java_sun_awt_wl_WLComponentPeer_nativeCreateWLSurface CHECK_NULL(frame->wl_surface); frame->wp_viewport = wp_viewporter_get_viewport(wp_viewporter, frame->wl_surface); CHECK_NULL(frame->wp_viewport); + if (wp_fractional_scale_manager) { + frame->wp_fractional_scale = wp_fractional_scale_manager_v1_get_fractional_scale(wp_fractional_scale_manager, frame->wl_surface); + CHECK_NULL(frame->wp_fractional_scale); + wp_fractional_scale_v1_add_listener(frame->wp_fractional_scale, &wp_fractional_scale_listener, frame); + } frame->xdg_surface = xdg_wm_base_get_xdg_surface(xdg_wm_base, frame->wl_surface); CHECK_NULL(frame->xdg_surface); if (gtk_shell1 != NULL) { @@ -534,6 +566,11 @@ Java_sun_awt_wl_WLComponentPeer_nativeCreateWLPopup CHECK_NULL(frame->wl_surface); frame->wp_viewport = wp_viewporter_get_viewport(wp_viewporter, frame->wl_surface); CHECK_NULL(frame->wp_viewport); + if (wp_fractional_scale_manager) { + frame->wp_fractional_scale = wp_fractional_scale_manager_v1_get_fractional_scale(wp_fractional_scale_manager, frame->wl_surface); + CHECK_NULL(frame->wp_fractional_scale); + wp_fractional_scale_v1_add_listener(frame->wp_fractional_scale, &wp_fractional_scale_listener, frame); + } frame->xdg_surface = xdg_wm_base_get_xdg_surface(xdg_wm_base, frame->wl_surface); CHECK_NULL(frame->xdg_surface); @@ -590,6 +627,9 @@ DoHide(JNIEnv *env, struct WLFrame *frame) gtk_surface1_destroy(frame->gtk_surface); } wp_viewport_destroy(frame->wp_viewport); + if (frame->wp_fractional_scale) { + wp_fractional_scale_v1_destroy(frame->wp_fractional_scale); + } xdg_surface_destroy(frame->xdg_surface); wl_surface_destroy(frame->wl_surface); delete_all_tokens(frame->activation_token_list); @@ -601,6 +641,7 @@ DoHide(JNIEnv *env, struct WLFrame *frame) frame->xdg_toplevel = NULL; frame->xdg_popup = NULL; frame->wp_viewport = NULL; + frame->wp_fractional_scale = NULL; frame->toplevel = JNI_FALSE; } } diff --git a/src/java.desktop/unix/native/libawt_wlawt/WLGraphicsEnvironment.c b/src/java.desktop/unix/native/libawt_wlawt/WLGraphicsEnvironment.c index 4d8a361352a4..c2d1b61b8687 100644 --- a/src/java.desktop/unix/native/libawt_wlawt/WLGraphicsEnvironment.c +++ b/src/java.desktop/unix/native/libawt_wlawt/WLGraphicsEnvironment.c @@ -165,7 +165,7 @@ wl_output_done( output->height_mm, (jint)output->subpixel, (jint)output->transform, - (jint)output->scale); + (jlong)output->scale * 120); JNU_CHECK_EXCEPTION(env); } @@ -197,7 +197,7 @@ WLGraphicsEnvironment_initIDs CHECK_NULL_RETURN( notifyOutputConfiguredMID = (*env)->GetMethodID(env, clazz, "notifyOutputConfigured", - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIIIIIIII)V"), + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIIIIIIIJ)V"), JNI_FALSE); CHECK_NULL_RETURN( notifyOutputDestroyedMID = (*env)->GetMethodID(env, clazz, diff --git a/src/java.desktop/unix/native/libawt_wlawt/WLToolkit.c b/src/java.desktop/unix/native/libawt_wlawt/WLToolkit.c index a66e92f3e2b2..4de8007b0e84 100644 --- a/src/java.desktop/unix/native/libawt_wlawt/WLToolkit.c +++ b/src/java.desktop/unix/native/libawt_wlawt/WLToolkit.c @@ -65,6 +65,7 @@ struct wl_shm *wl_shm = NULL; struct wl_compositor *wl_compositor = NULL; struct xdg_wm_base *xdg_wm_base = NULL; struct wp_viewporter *wp_viewporter = NULL; +struct wp_fractional_scale_manager_v1 *wp_fractional_scale_manager = NULL; struct xdg_activation_v1 *xdg_activation_v1 = NULL; struct gtk_shell1* gtk_shell1 = NULL; struct wl_seat *wl_seat = NULL; @@ -527,6 +528,8 @@ registry_global(void *data, struct wl_registry *wl_registry, zwp_selection_dm = wl_registry_bind(wl_registry, name, &zwp_primary_selection_device_manager_v1_interface, 1); } else if (strcmp(interface, wp_viewporter_interface.name) == 0) { wp_viewporter = wl_registry_bind(wl_registry, name, &wp_viewporter_interface, 1); + } else if (strcmp(interface, wp_fractional_scale_manager_v1_interface.name) == 0) { + wp_fractional_scale_manager = wl_registry_bind(wl_registry, name, &wp_fractional_scale_manager_v1_interface, 1); } #ifdef WAKEFIELD_ROBOT diff --git a/src/java.desktop/unix/native/libawt_wlawt/WLToolkit.h b/src/java.desktop/unix/native/libawt_wlawt/WLToolkit.h index 4b01194e2ef3..ec549c5a3fe8 100644 --- a/src/java.desktop/unix/native/libawt_wlawt/WLToolkit.h +++ b/src/java.desktop/unix/native/libawt_wlawt/WLToolkit.h @@ -29,6 +29,7 @@ #include "xdg-activation-v1-client-protocol.h" #include "primary-selection-client-protocol.h" #include "viewporter-client-protocol.h" +#include "fractional-scale-v1-client-protocol.h" #include "jvm_md.h" #include "jni_util.h" @@ -56,6 +57,7 @@ extern struct wl_pointer *wl_pointer; extern struct wl_compositor *wl_compositor; extern struct xdg_wm_base *xdg_wm_base; extern struct wp_viewporter *wp_viewporter; +extern struct wp_fractional_scale_manager_v1 *wp_fractional_scale_manager; extern struct xdg_activation_v1 *xdg_activation_v1; extern struct gtk_shell1* gtk_shell1; // optional, check for NULL before use diff --git a/src/java.desktop/unix/native/libawt_wlawt/fractional-scale-v1-client-protocol.c b/src/java.desktop/unix/native/libawt_wlawt/fractional-scale-v1-client-protocol.c new file mode 100644 index 000000000000..090166bcf301 --- /dev/null +++ b/src/java.desktop/unix/native/libawt_wlawt/fractional-scale-v1-client-protocol.c @@ -0,0 +1,73 @@ +/* Generated by wayland-scanner 1.22.0 */ + +/* + * Copyright © 2022 Kenny Levinsen + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface wl_surface_interface; +extern const struct wl_interface wp_fractional_scale_v1_interface; + +static const struct wl_interface *fractional_scale_v1_types[] = { + NULL, + &wp_fractional_scale_v1_interface, + &wl_surface_interface, +}; + +static const struct wl_message wp_fractional_scale_manager_v1_requests[] = { + { "destroy", "", fractional_scale_v1_types + 0 }, + { "get_fractional_scale", "no", fractional_scale_v1_types + 1 }, +}; + +WL_PRIVATE const struct wl_interface wp_fractional_scale_manager_v1_interface = { + "wp_fractional_scale_manager_v1", 1, + 2, wp_fractional_scale_manager_v1_requests, + 0, NULL, +}; + +static const struct wl_message wp_fractional_scale_v1_requests[] = { + { "destroy", "", fractional_scale_v1_types + 0 }, +}; + +static const struct wl_message wp_fractional_scale_v1_events[] = { + { "preferred_scale", "u", fractional_scale_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface wp_fractional_scale_v1_interface = { + "wp_fractional_scale_v1", 1, + 1, wp_fractional_scale_v1_requests, + 1, wp_fractional_scale_v1_events, +}; + diff --git a/src/java.desktop/unix/native/libawt_wlawt/fractional-scale-v1-client-protocol.h b/src/java.desktop/unix/native/libawt_wlawt/fractional-scale-v1-client-protocol.h new file mode 100644 index 000000000000..4bb28da48b9d --- /dev/null +++ b/src/java.desktop/unix/native/libawt_wlawt/fractional-scale-v1-client-protocol.h @@ -0,0 +1,264 @@ +/* Generated by wayland-scanner 1.22.0 */ + +#ifndef FRACTIONAL_SCALE_V1_CLIENT_PROTOCOL_H +#define FRACTIONAL_SCALE_V1_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client-core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_fractional_scale_v1 The fractional_scale_v1 protocol + * Protocol for requesting fractional surface scales + * + * @section page_desc_fractional_scale_v1 Description + * + * This protocol allows a compositor to suggest for surfaces to render at + * fractional scales. + * + * A client can submit scaled content by utilizing wp_viewport. This is done by + * creating a wp_viewport object for the surface and setting the destination + * rectangle to the surface size before the scale factor is applied. + * + * The buffer size is calculated by multiplying the surface size by the + * intended scale. + * + * The wl_surface buffer scale should remain set to 1. + * + * If a surface has a surface-local size of 100 px by 50 px and wishes to + * submit buffers with a scale of 1.5, then a buffer of 150px by 75 px should + * be used and the wp_viewport destination rectangle should be 100 px by 50 px. + * + * For toplevel surfaces, the size is rounded halfway away from zero. The + * rounding algorithm for subsurface position and size is not defined. + * + * @section page_ifaces_fractional_scale_v1 Interfaces + * - @subpage page_iface_wp_fractional_scale_manager_v1 - fractional surface scale information + * - @subpage page_iface_wp_fractional_scale_v1 - fractional scale interface to a wl_surface + * @section page_copyright_fractional_scale_v1 Copyright + *
+ *
+ * Copyright © 2022 Kenny Levinsen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ */ +struct wl_surface; +struct wp_fractional_scale_manager_v1; +struct wp_fractional_scale_v1; + +#ifndef WP_FRACTIONAL_SCALE_MANAGER_V1_INTERFACE +#define WP_FRACTIONAL_SCALE_MANAGER_V1_INTERFACE +/** + * @page page_iface_wp_fractional_scale_manager_v1 wp_fractional_scale_manager_v1 + * @section page_iface_wp_fractional_scale_manager_v1_desc Description + * + * A global interface for requesting surfaces to use fractional scales. + * @section page_iface_wp_fractional_scale_manager_v1_api API + * See @ref iface_wp_fractional_scale_manager_v1. + */ +/** + * @defgroup iface_wp_fractional_scale_manager_v1 The wp_fractional_scale_manager_v1 interface + * + * A global interface for requesting surfaces to use fractional scales. + */ +extern const struct wl_interface wp_fractional_scale_manager_v1_interface; +#endif +#ifndef WP_FRACTIONAL_SCALE_V1_INTERFACE +#define WP_FRACTIONAL_SCALE_V1_INTERFACE +/** + * @page page_iface_wp_fractional_scale_v1 wp_fractional_scale_v1 + * @section page_iface_wp_fractional_scale_v1_desc Description + * + * An additional interface to a wl_surface object which allows the compositor + * to inform the client of the preferred scale. + * @section page_iface_wp_fractional_scale_v1_api API + * See @ref iface_wp_fractional_scale_v1. + */ +/** + * @defgroup iface_wp_fractional_scale_v1 The wp_fractional_scale_v1 interface + * + * An additional interface to a wl_surface object which allows the compositor + * to inform the client of the preferred scale. + */ +extern const struct wl_interface wp_fractional_scale_v1_interface; +#endif + +#ifndef WP_FRACTIONAL_SCALE_MANAGER_V1_ERROR_ENUM +#define WP_FRACTIONAL_SCALE_MANAGER_V1_ERROR_ENUM +enum wp_fractional_scale_manager_v1_error { + /** + * the surface already has a fractional_scale object associated + */ + WP_FRACTIONAL_SCALE_MANAGER_V1_ERROR_FRACTIONAL_SCALE_EXISTS = 0, +}; +#endif /* WP_FRACTIONAL_SCALE_MANAGER_V1_ERROR_ENUM */ + +#define WP_FRACTIONAL_SCALE_MANAGER_V1_DESTROY 0 +#define WP_FRACTIONAL_SCALE_MANAGER_V1_GET_FRACTIONAL_SCALE 1 + + +/** + * @ingroup iface_wp_fractional_scale_manager_v1 + */ +#define WP_FRACTIONAL_SCALE_MANAGER_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_wp_fractional_scale_manager_v1 + */ +#define WP_FRACTIONAL_SCALE_MANAGER_V1_GET_FRACTIONAL_SCALE_SINCE_VERSION 1 + +/** @ingroup iface_wp_fractional_scale_manager_v1 */ +static inline void +wp_fractional_scale_manager_v1_set_user_data(struct wp_fractional_scale_manager_v1 *wp_fractional_scale_manager_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) wp_fractional_scale_manager_v1, user_data); +} + +/** @ingroup iface_wp_fractional_scale_manager_v1 */ +static inline void * +wp_fractional_scale_manager_v1_get_user_data(struct wp_fractional_scale_manager_v1 *wp_fractional_scale_manager_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) wp_fractional_scale_manager_v1); +} + +static inline uint32_t +wp_fractional_scale_manager_v1_get_version(struct wp_fractional_scale_manager_v1 *wp_fractional_scale_manager_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) wp_fractional_scale_manager_v1); +} + +/** + * @ingroup iface_wp_fractional_scale_manager_v1 + * + * Informs the server that the client will not be using this protocol + * object anymore. This does not affect any other objects, + * wp_fractional_scale_v1 objects included. + */ +static inline void +wp_fractional_scale_manager_v1_destroy(struct wp_fractional_scale_manager_v1 *wp_fractional_scale_manager_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) wp_fractional_scale_manager_v1, + WP_FRACTIONAL_SCALE_MANAGER_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wp_fractional_scale_manager_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_wp_fractional_scale_manager_v1 + * + * Create an add-on object for the the wl_surface to let the compositor + * request fractional scales. If the given wl_surface already has a + * wp_fractional_scale_v1 object associated, the fractional_scale_exists + * protocol error is raised. + */ +static inline struct wp_fractional_scale_v1 * +wp_fractional_scale_manager_v1_get_fractional_scale(struct wp_fractional_scale_manager_v1 *wp_fractional_scale_manager_v1, struct wl_surface *surface) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) wp_fractional_scale_manager_v1, + WP_FRACTIONAL_SCALE_MANAGER_V1_GET_FRACTIONAL_SCALE, &wp_fractional_scale_v1_interface, wl_proxy_get_version((struct wl_proxy *) wp_fractional_scale_manager_v1), 0, NULL, surface); + + return (struct wp_fractional_scale_v1 *) id; +} + +/** + * @ingroup iface_wp_fractional_scale_v1 + * @struct wp_fractional_scale_v1_listener + */ +struct wp_fractional_scale_v1_listener { + /** + * notify of new preferred scale + * + * Notification of a new preferred scale for this surface that + * the compositor suggests that the client should use. + * + * The sent scale is the numerator of a fraction with a denominator + * of 120. + * @param scale the new preferred scale + */ + void (*preferred_scale)(void *data, + struct wp_fractional_scale_v1 *wp_fractional_scale_v1, + uint32_t scale); +}; + +/** + * @ingroup iface_wp_fractional_scale_v1 + */ +static inline int +wp_fractional_scale_v1_add_listener(struct wp_fractional_scale_v1 *wp_fractional_scale_v1, + const struct wp_fractional_scale_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) wp_fractional_scale_v1, + (void (**)(void)) listener, data); +} + +#define WP_FRACTIONAL_SCALE_V1_DESTROY 0 + +/** + * @ingroup iface_wp_fractional_scale_v1 + */ +#define WP_FRACTIONAL_SCALE_V1_PREFERRED_SCALE_SINCE_VERSION 1 + +/** + * @ingroup iface_wp_fractional_scale_v1 + */ +#define WP_FRACTIONAL_SCALE_V1_DESTROY_SINCE_VERSION 1 + +/** @ingroup iface_wp_fractional_scale_v1 */ +static inline void +wp_fractional_scale_v1_set_user_data(struct wp_fractional_scale_v1 *wp_fractional_scale_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) wp_fractional_scale_v1, user_data); +} + +/** @ingroup iface_wp_fractional_scale_v1 */ +static inline void * +wp_fractional_scale_v1_get_user_data(struct wp_fractional_scale_v1 *wp_fractional_scale_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) wp_fractional_scale_v1); +} + +static inline uint32_t +wp_fractional_scale_v1_get_version(struct wp_fractional_scale_v1 *wp_fractional_scale_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) wp_fractional_scale_v1); +} + +/** + * @ingroup iface_wp_fractional_scale_v1 + * + * Destroy the fractional scale object. When this object is destroyed, + * preferred_scale events will no longer be sent. + */ +static inline void +wp_fractional_scale_v1_destroy(struct wp_fractional_scale_v1 *wp_fractional_scale_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) wp_fractional_scale_v1, + WP_FRACTIONAL_SCALE_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wp_fractional_scale_v1), WL_MARSHAL_FLAG_DESTROY); +} + +#ifdef __cplusplus +} +#endif + +#endif