Skip to content

Commit

Permalink
JBR-5968 Wayland: support PERPIXEL_TRANSLUCENT
Browse files Browse the repository at this point in the history
  • Loading branch information
mkartashev authored and jbrbot committed Nov 8, 2024
1 parent 8210596 commit 35bb2d2
Show file tree
Hide file tree
Showing 13 changed files with 353 additions and 173 deletions.
62 changes: 33 additions & 29 deletions src/java.desktop/unix/classes/sun/awt/wl/WLGraphicsConfig.java
Original file line number Diff line number Diff line change
@@ -1,30 +1,54 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright 2023 JetBrains s.r.o.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package sun.awt.wl;

import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.ColorModel;
import java.awt.image.DirectColorModel;
import java.awt.image.WritableRaster;

import sun.awt.image.OffScreenImage;
import sun.java2d.SurfaceData;
import sun.java2d.loops.SurfaceType;
import sun.java2d.wl.WLSurfaceData;

public class WLGraphicsConfig extends GraphicsConfiguration {
public abstract class WLGraphicsConfig extends GraphicsConfiguration {
private final WLGraphicsDevice device;
private final int width;
private final int height;
private final int scale;

public WLGraphicsConfig(WLGraphicsDevice device, int width, int height, int scale) {
protected WLGraphicsConfig(WLGraphicsDevice device, int width, int height, int scale) {
this.device = device;
this.width = width;
this.height = height;
this.scale = scale;
}

public boolean differsFrom(int width, int height, int scale) {
boolean differsFrom(int width, int height, int scale) {
return width != this.width || height != this.height || scale != this.scale;
}

Expand All @@ -33,21 +57,6 @@ public WLGraphicsDevice getDevice() {
return device;
}

@Override
public ColorModel getColorModel() {
return new DirectColorModel(24, 0xff0000, 0xff00, 0xff);
}

@Override
public ColorModel getColorModel(int transparency) {
return switch (transparency) {
case Transparency.OPAQUE -> getColorModel();
case Transparency.BITMASK -> new DirectColorModel(25, 0xff0000, 0xff00, 0xff, 0x1000000);
case Transparency.TRANSLUCENT -> new DirectColorModel(32, 0xff0000, 0xff00, 0xff, 0xff000000);
default -> null;
};
}

public Image createAcceleratedImage(Component target,
int width, int height)
{
Expand Down Expand Up @@ -75,20 +84,15 @@ public Rectangle getBounds() {
return new Rectangle(width, height);
}

public SurfaceType getSurfaceType() {
return SurfaceType.IntArgb;
}

public SurfaceData createSurfaceData(WLComponentPeer peer) {
return WLSurfaceData.createData(peer);
}

public int getScale() {
return scale;
}

public abstract SurfaceType getSurfaceType();
public abstract SurfaceData createSurfaceData(WLComponentPeer peer);

@Override
public String toString() {
return String.format("WLGraphicsConfig: %dx%d %dx scale", width, height, scale);
return String.format("%dx%d %dx scale", width, height, scale);
}
}
54 changes: 35 additions & 19 deletions src/java.desktop/unix/classes/sun/awt/wl/WLGraphicsDevice.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@
package sun.awt.wl;

import sun.awt.AWTAccessor;
import sun.awt.DisplayChangedListener;
import sun.java2d.vulkan.WLVKGraphicsConfig;

import java.awt.*;
import java.util.ArrayList;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.Rectangle;
import java.awt.Window;

/**
* Corresponds to Wayland's output and is identified by its wlID and x, y coordinates
Expand Down Expand Up @@ -59,8 +60,11 @@ public class WLGraphicsDevice extends GraphicsDevice {
*/
private volatile int y; // only changes when the device gets invalidated

private final java.util.List<WLComponentPeer> peers = new ArrayList<>();
private volatile WLGraphicsConfig config = null;
// Configs are always the same in size and scale
private volatile WLGraphicsConfig[] configs = null;

// The default config is an object from the configs array
private volatile WLGraphicsConfig defaultConfig = null;

private WLGraphicsDevice(int id, int x, int y) {
this.wlID = id;
Expand All @@ -74,12 +78,22 @@ int getID() {

void updateConfiguration(String name, int width, int height, int scale) {
this.name = name == null ? "wl_output." + wlID : name;
if (config == null || config.differsFrom(width, height, scale)) {

if (configs == null || configs[0].differsFrom(width, height, scale)) {
// It is necessary to create a new object whenever config changes as its
// identity is used to detect changes in scale, among other things.
config = WLGraphicsEnvironment.isVulkanEnabled() ?
WLVKGraphicsConfig.getConfig(this, width, height, scale) :
new WLGraphicsConfig(this, width, height, scale);
if (WLGraphicsEnvironment.isVulkanEnabled()) {
defaultConfig = WLVKGraphicsConfig.getConfig(this, width, height, scale);
configs = new WLGraphicsConfig[1];
configs[0] = defaultConfig;
} 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.
defaultConfig = WLSMGraphicsConfig.getConfig(this, width, height, scale, false);
configs = new WLGraphicsConfig[2];
configs[0] = defaultConfig;
configs[1] = WLSMGraphicsConfig.getConfig(this, width, height, scale, true);
}
}
}

Expand All @@ -94,7 +108,7 @@ void invalidate(WLGraphicsDevice similarDevice) {
this.y = similarDevice.y;

final int newScale = similarDevice.getScale();
final Rectangle newBounds = similarDevice.config.getBounds();
final Rectangle newBounds = similarDevice.defaultConfig.getBounds();
updateConfiguration(similarDevice.name, newBounds.width, newBounds.height, newScale);
}

Expand All @@ -108,16 +122,16 @@ public static WLGraphicsDevice createWithConfiguration(int id, String name, int
* Compares the identity of this device with the given attributes
* and returns true iff the attributes identify the same device.
*/
public boolean isSameDeviceAs(int wlID, int x, int y) {
boolean isSameDeviceAs(int wlID, int x, int y) {
return this.wlID == wlID && this.x == x && this.y == y;
}

public boolean hasSameNameAs(WLGraphicsDevice otherDevice) {
boolean hasSameNameAs(WLGraphicsDevice otherDevice) {
return name != null && otherDevice.name != null && name.equals(otherDevice.name);
}

public boolean hasSameSizeAs(WLGraphicsDevice modelDevice) {
return config != null && modelDevice.config != null && config.getBounds().equals(modelDevice.config.getBounds());
boolean hasSameSizeAs(WLGraphicsDevice modelDevice) {
return defaultConfig != null && modelDevice.defaultConfig != null && defaultConfig.getBounds().equals(modelDevice.defaultConfig.getBounds());
}

@Override
Expand All @@ -136,17 +150,17 @@ public GraphicsConfiguration[] getConfigurations() {
// "Non-current modes are deprecated. A compositor can decide to only
// advertise the current mode and never send other modes. Clients
// should not rely on non-current modes."
// So there is just one config, always.
return new GraphicsConfiguration[] {config};
// So there's always the same set of configs.
return configs.clone();
}

@Override
public GraphicsConfiguration getDefaultConfiguration() {
return config;
return defaultConfig;
}

int getScale() {
return config.getScale();
return defaultConfig.getScale();
}

@Override
Expand Down Expand Up @@ -197,6 +211,8 @@ public void removeWindow(WLComponentPeer peer) {

@Override
public String toString() {
return String.format("WLGraphicsDevice: id=%d at (%d, %d) with %s", wlID, x, y, config);
return String.format("WLGraphicsDevice: id=%d at (%d, %d) with %s",
wlID, x, y,
defaultConfig != null ? defaultConfig : "<no configs>");
}
}
116 changes: 116 additions & 0 deletions src/java.desktop/unix/classes/sun/awt/wl/WLSMGraphicsConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright 2023 JetBrains s.r.o.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package sun.awt.wl;

import sun.java2d.SurfaceData;
import sun.java2d.loops.SurfaceType;
import sun.java2d.wl.WLSMSurfaceData;
import sun.util.logging.PlatformLogger;

import java.awt.Transparency;
import java.awt.image.ColorModel;
import java.awt.image.DirectColorModel;

/**
* Graphics configuration for shared memory buffers (the wl_shm Wayland protocol).
*/
public class WLSMGraphicsConfig extends WLGraphicsConfig {
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.wl.WLSMGraphicsConfig");

// The memory layout of an individual pixel corresponding exactly to
// the values of wl_shm::format
public static final int WL_SHM_FORMAT_ARGB8888 = 0;
public static final int WL_SHM_FORMAT_XRGB8888 = 1;

private final boolean translucencyCapable;
private final ColorModel colorModel;
private final SurfaceType surfaceType;

private WLSMGraphicsConfig(WLGraphicsDevice device,
int width,
int height,
int scale,
boolean translucencyCapable) {
super(device, width, height, scale);
this.translucencyCapable = translucencyCapable;
this.colorModel
= translucencyCapable
? new DirectColorModel(32, 0xff0000, 0xff00, 0xff, 0xff000000)
: new DirectColorModel(24, 0xff0000, 0xff00, 0xff);
this.surfaceType = translucencyCapable ? SurfaceType.IntArgb : SurfaceType.IntRgb;
}

public static WLSMGraphicsConfig getConfig(WLGraphicsDevice device,
int width,
int height,
int scale,
boolean translucencyCapable) {
var newConfig = new WLSMGraphicsConfig(device, width, height, scale, translucencyCapable);
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("New shared memory config " + newConfig);
}
return newConfig;
}

public SurfaceType getSurfaceType() {
return surfaceType;
}

@Override
public ColorModel getColorModel() {
return colorModel;
}

@Override
public ColorModel getColorModel(int transparency) {
return switch (transparency) {
case Transparency.OPAQUE -> new DirectColorModel(24, 0xff0000, 0xff00, 0xff);
case Transparency.BITMASK -> new DirectColorModel(25, 0xff0000, 0xff00, 0xff, 0x1000000);
case Transparency.TRANSLUCENT -> new DirectColorModel(32, 0xff0000, 0xff00, 0xff, 0xff000000);
default -> null;
};
}

public SurfaceData createSurfaceData(WLComponentPeer peer) {
return WLSMSurfaceData.createData(peer, this);
}

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;
}

@Override
public boolean isTranslucencyCapable() {
return translucencyCapable;
}

@Override
public String toString() {
return "WLSMGraphicsConfig[" + (translucencyCapable ? "translucent" : "opaque") + "] " + super.toString();
}
}
9 changes: 4 additions & 5 deletions src/java.desktop/unix/classes/sun/awt/wl/WLToolkit.java
Original file line number Diff line number Diff line change
Expand Up @@ -965,25 +965,24 @@ public boolean areExtraMouseButtonsEnabled() throws HeadlessException {

@Override
public boolean isWindowOpacitySupported() {
log.info("Not implemented: WLToolkit.isWindowOpacitySupported()");
return false;
}

@Override
public boolean isWindowShapingSupported() {
log.info("Not implemented: WLToolkit.isWindowShapingSupported()");
return false;
}

@Override
public boolean isWindowTranslucencySupported() {
log.info("Not implemented: WLToolkit.isWindowTranslucencySupported()");
return false;
return true;
}

@Override
public boolean isTranslucencyCapable(GraphicsConfiguration gc) {
log.info("Not implemented: WLToolkit.isWindowTranslucencySupported()");
if (gc instanceof WLGraphicsConfig wlGraphicsConfig) {
return wlGraphicsConfig.isTranslucencyCapable();
}
return false;
}

Expand Down
2 changes: 1 addition & 1 deletion src/java.desktop/unix/classes/sun/awt/wl/WLWindowPeer.java
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public void setOpacity(float opacity) {
@Override
public void setOpaque(boolean isOpaque) {
if (!isOpaque) {
throw new UnsupportedOperationException();
throw new UnsupportedOperationException("Transparent windows are not supported");
}
}

Expand Down
Loading

0 comments on commit 35bb2d2

Please sign in to comment.