Compare commits

..

1 Commits

Author SHA1 Message Date
Dmitry Batrak
44f519d8c4 optimize tree implementation 2025-09-16 17:58:55 +03:00
127 changed files with 1145 additions and 7803 deletions

View File

@@ -152,7 +152,7 @@ $ sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-noble.list https://package
$ sudo apt update
$ sudo apt-get -y install openjdk-21-jdk file zip unzip autoconf make build-essential libx11-dev libxext-dev libxrender-dev \
libxtst-dev libxt-dev libxrandr-dev libcups2-dev libfontconfig1-dev libasound2-dev libspeechd-dev libwayland-dev \
libxkbcommon-x11-0 libdbus-1-dev vulkan-sdk vulkan-utility-libraries-dev
libxkbcommon-x11-0 vulkan-sdk vulkan-utility-libraries-dev
```
Then run the following:

View File

@@ -109,12 +109,6 @@ else
WITH_BUNDLED_FREETYPE=""
fi
if [ "$bundle_type" == "lb" ]; then
WITH_VULKAN=""
else
WITH_VULKAN="--with-vulkan"
fi
REPRODUCIBLE_BUILD_OPTS="--with-source-date=$SOURCE_DATE_EPOCH
--with-hotspot-build-time=$BUILD_TIME
--with-copyright-year=$COPYRIGHT_YEAR

View File

@@ -35,7 +35,7 @@ function do_configure {
--with-version-opt=b"$build_number" \
--with-boot-jdk="$BOOT_JDK" \
--enable-cds=yes \
$WITH_VULKAN \
--with-vulkan \
$DISABLE_WARNINGS_AS_ERRORS \
$STATIC_CONF_ARGS \
$REPRODUCIBLE_BUILD_OPTS \
@@ -113,11 +113,6 @@ case "$bundle_type" in
jbr_name_postfix="_${bundle_type}"
do_maketest=1
;;
"lb")
do_reset_changes=1
jbr_name_postfix="_${bundle_type}"
do_maketest=1
;;
"nomod" | "")
bundle_type=""
;;

View File

@@ -32,6 +32,13 @@ function do_configure {
--build=x86_64-unknown-linux-gnu \
--openjdk-target=x86_64-unknown-linux-gnu"
fi
if [ -n "${JCEF_BUILD_LEGACY:-}" ]; then
WITH_VULKAN=""
else
WITH_VULKAN="--with-vulkan"
fi
sh configure \
$WITH_DEBUG_LEVEL \
--with-vendor-name="$VENDOR_NAME" \
@@ -95,7 +102,9 @@ function create_image_bundle {
# jmod does not preserve file permissions (JDK-8173610)
[ -f "$IMAGES_DIR"/"$__root_dir"/lib/jcef_helper ] && chmod a+x "$IMAGES_DIR"/"$__root_dir"/lib/jcef_helper
[ -f "$IMAGES_DIR"/"$__root_dir"/lib/cef_server ] && chmod a+x "$IMAGES_DIR"/"$__root_dir"/lib/cef_server
if [ ! -n "${JCEF_BUILD_LEGACY:-}" ]; then
[ -f "$IMAGES_DIR"/"$__root_dir"/lib/cef_server ] && chmod a+x "$IMAGES_DIR"/"$__root_dir"/lib/cef_server
fi
echo Creating "$JBR".tar.gz ...
@@ -121,11 +130,6 @@ case "$bundle_type" in
jbr_name_postfix="_${bundle_type}"
do_maketest=1
;;
"lb")
do_reset_changes=1
jbr_name_postfix="_${bundle_type}"
do_maketest=1
;;
"nomod" | "")
bundle_type=""
;;
@@ -154,7 +158,7 @@ JBRSDK_BUNDLE=jbrsdk
echo Fixing permissions
chmod -R a+r $JSDK
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "lb" ]; then
if [ "$bundle_type" == "jcef" ]; then
git apply -p0 < jb/project/tools/patches/add_jcef_module.patch || do_exit $?
update_jsdk_mods $JSDK $JCEF_PATH/jmods $JSDK/jmods $JSDK_MODS_DIR || do_exit $?
cp $JCEF_PATH/jmods/* $JSDK_MODS_DIR # $JSDK/jmods is not changed
@@ -167,7 +171,7 @@ create_image_bundle "jbr${jbr_name_postfix}" "jbr" $JSDK_MODS_DIR "$modules" ||
# create sdk image bundle
modules=$(cat $JSDK/release | grep MODULES | sed s/MODULES=//g | sed s/' '/','/g | sed s/\"//g | sed s/\\n//g) || do_exit $?
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "lb" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
if [ "$bundle_type" == "jcef" ]|| [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
modules=${modules},$(get_mods_list "$JCEF_PATH"/jmods)
fi
create_image_bundle "$JBRSDK_BUNDLE${jbr_name_postfix}" $JBRSDK_BUNDLE $JSDK_MODS_DIR "$modules" || do_exit $?

View File

@@ -93,7 +93,7 @@ WITH_DEBUG_LEVEL="--with-debug-level=release"
RELEASE_NAME=linux-x86-server-release
case "$bundle_type" in
"jcef" | "lb")
"jcef")
echo "not implemented" && do_exit 1
;;
"nomod" | "")
@@ -120,7 +120,7 @@ JBRSDK_BUNDLE=jbrsdk
echo Fixing permissions
chmod -R a+r $JSDK
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ] || [ "$bundle_type" == "lb" ]; then
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ]; then
jbr_name_postfix="_${bundle_type}"
else
jbr_name_postfix=""

View File

@@ -40,13 +40,13 @@ else ifeq ($(call isBuildOsEnv, windows.wsl1 windows.wsl2), true)
else
M2_REPO := $(HOME)/.m2/repository
endif
M2_ARTIFACT := $(M2_REPO)/org/jetbrains/runtime/jbr-api/SNAPSHOT
M2_ARTIFACT := $(M2_REPO)/com/jetbrains/jbr-api/SNAPSHOT
M2_POM_CONTENT := \
<?xml version="1.0" encoding="UTF-8"?> \
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" \
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> \
<modelVersion>4.0.0</modelVersion> \
<groupId>org.jetbrains.runtime</groupId> \
<groupId>com.jetbrains</groupId> \
<artifactId>jbr-api</artifactId> \
<version>SNAPSHOT</version> \
</project> \
@@ -65,8 +65,7 @@ jbr-api:
$(MKDIR) -p $(M2_ARTIFACT); \
$(ECHO) "$(M2_POM_CONTENT)" > $(M2_ARTIFACT)/$(ARTIFACT_NAME).pom; \
$(CP) "$(JBR_API_DIR)/out/$(ARTIFACT_NAME).jar" "$(M2_ARTIFACT)"; \
$(ECHO) "Installed into local Maven repository as org.jetbrains.runtime:jbr-api:SNAPSHOT"; \
cd "$(M2_ARTIFACT)" && sha256sum --binary "$(ARTIFACT_NAME).jar"; \
$(ECHO) "Installed into local Maven repository as com.jetbrains:jbr-api:SNAPSHOT"; \
else \
$(ECHO) "No Maven repository found at $(M2_REPO) - skipping local installation"; \
fi

View File

@@ -89,7 +89,7 @@ AC_DEFUN_ONCE([LIB_SETUP_VULKAN],
if (test "x${with_vulkan_shader_compiler}" = x || test "x${with_vulkan_shader_compiler}" = xglslc); then
UTIL_LOOKUP_PROGS(GLSLC, glslc)
SHADER_COMPILER="$GLSLC"
VULKAN_SHADER_COMPILER="glslc --target-env=vulkan1.2 -mfmt=num"
VULKAN_SHADER_COMPILER="glslc --target-env=vulkan1.2 -mfmt=num -o"
fi
# Check glslangValidator
@@ -97,8 +97,7 @@ AC_DEFUN_ONCE([LIB_SETUP_VULKAN],
test "x$SHADER_COMPILER" = x; then
UTIL_LOOKUP_PROGS(GLSLANG, glslangValidator)
SHADER_COMPILER="$GLSLANG"
# Newer glslangValidator could use -P\"\#extension GL_GOOGLE_include_directive: require\"
VULKAN_SHADER_COMPILER="glslangValidator --target-env vulkan1.2 -x"
VULKAN_SHADER_COMPILER="glslangValidator --target-env vulkan1.2 -x -o"
fi
if test "x$SHADER_COMPILER" = x; then

View File

@@ -241,7 +241,7 @@ endif
# Compile Vulkan shaders
define compile-spirv
$(call MakeTargetDir)
$(VULKAN_SHADER_COMPILER) -D$(call uppercase,$(patsubst .%,STAGE_%,$(suffix $<))) -o '$(call DecodeSpace, $@)' '$(call DecodeSpace, $<)'
$(VULKAN_SHADER_COMPILER) '$(call DecodeSpace, $@)' '$(call DecodeSpace, $<)'
endef
spirv-name = $(strip $1).h

View File

@@ -41,8 +41,7 @@ public abstract class CGraphicsConfig extends GraphicsConfiguration
private final CGraphicsDevice device;
private ColorModel colorModel;
private final SurfaceManager.ProxyCache surfaceDataProxyCache =
new SurfaceManager.ProxyCache();
private final SurfaceManager.ProxyCache surfaceDataProxyCache = new SurfaceManager.ProxyCache();
protected CGraphicsConfig(CGraphicsDevice device) {
this.device = device;

View File

@@ -42,7 +42,9 @@ import java.util.ListIterator;
import java.util.Map;
import sun.java2d.MacOSFlags;
import sun.java2d.MacosxSurfaceManagerFactory;
import sun.java2d.SunGraphicsEnvironment;
import sun.java2d.SurfaceManagerFactory;
import sun.java2d.metal.MTLGraphicsConfig;
import sun.util.logging.PlatformLogger;
@@ -110,6 +112,9 @@ public final class CGraphicsEnvironment extends SunGraphicsEnvironment {
metalPipelineEnabled = true;
}
}
// Install the correct surface manager factory.
SurfaceManagerFactory.setInstance(new MacosxSurfaceManagerFactory());
}
/**

View File

@@ -0,0 +1,75 @@
/*
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
* 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.java2d;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
import sun.awt.CGraphicsEnvironment;
import sun.awt.image.SunVolatileImage;
import sun.awt.image.SurfaceManager;
import sun.awt.image.TextureWrapperSurfaceManager;
import sun.awt.image.VolatileSurfaceManager;
import sun.java2d.metal.MTLGraphicsConfig;
import sun.java2d.metal.MTLSurfaceData;
import sun.java2d.metal.MTLVolatileSurfaceManager;
import sun.java2d.opengl.CGLVolatileSurfaceManager;
/**
* This is a factory class with static methods for creating a
* platform-specific instance of a particular SurfaceManager. Each platform
* (Windows, Unix, etc.) has its own specialized SurfaceManagerFactory.
*/
public class MacosxSurfaceManagerFactory extends SurfaceManagerFactory {
/**
* Creates a new instance of a VolatileSurfaceManager given any
* arbitrary SunVolatileImage. An optional context Object can be supplied
* as a way for the caller to pass pipeline-specific context data to
* the VolatileSurfaceManager (such as a backbuffer handle, for example).
*
* For Mac OS X, this method returns either an CGL/MTL-specific
* VolatileSurfaceManager based on the GraphicsConfiguration
* under which the SunVolatileImage was created.
*/
public VolatileSurfaceManager createVolatileManager(SunVolatileImage vImg,
Object context)
{
return CGraphicsEnvironment.usingMetalPipeline() ? new MTLVolatileSurfaceManager(vImg, context) :
new CGLVolatileSurfaceManager(vImg, context);
}
@Override
public SurfaceManager createTextureWrapperSurfaceManager(GraphicsConfiguration gc, Image image, long texture) {
SurfaceData sd;
if (gc instanceof MTLGraphicsConfig) {
sd = MTLSurfaceData.createData((MTLGraphicsConfig) gc, image, texture);
} else {
throw new UnsupportedOperationException("Unsupported GraphicsConfiguration");
}
return new TextureWrapperSurfaceManager(sd);
}
}

View File

@@ -30,9 +30,6 @@ import sun.awt.CGraphicsDevice;
import sun.awt.CGraphicsEnvironment;
import sun.awt.image.OffScreenImage;
import sun.awt.image.SunVolatileImage;
import sun.awt.image.SurfaceManager;
import sun.awt.image.TextureWrapperSurfaceManager;
import sun.awt.image.VolatileSurfaceManager;
import sun.java2d.Disposer;
import sun.java2d.DisposerRecord;
import sun.java2d.Surface;
@@ -49,7 +46,6 @@ import java.awt.BufferCapabilities;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
import java.awt.ImageCapabilities;
import java.awt.Rectangle;
@@ -71,7 +67,7 @@ import static sun.java2d.pipe.hw.ContextCapabilities.*;
import static sun.java2d.metal.MTLContext.MTLContextCaps.CAPS_EXT_BIOP_SHADER;
public final class MTLGraphicsConfig extends CGraphicsConfig
implements AccelGraphicsConfig, SurfaceManager.Factory, SurfaceManager.TextureWrapperFactory
implements AccelGraphicsConfig
{
private static boolean mtlAvailable;
private static ImageCapabilities imageCaps = new MTLImageCaps();
@@ -384,17 +380,4 @@ public final class MTLGraphicsConfig extends CGraphicsConfig
return Math.max(maxTextureSize / getDevice().getScaleFactor(),
getBounds().height);
}
@Override
public VolatileSurfaceManager createVolatileManager(SunVolatileImage image,
Object context) {
return new MTLVolatileSurfaceManager(image, context);
}
@Override
public SurfaceManager createTextureWrapperSurfaceManager(
GraphicsConfiguration gc, Image image, long texture) {
SurfaceData sd = MTLSurfaceData.createData(this, image, texture);
return new TextureWrapperSurfaceManager(sd);
}
}

View File

@@ -47,8 +47,6 @@ import sun.awt.CGraphicsConfig;
import sun.awt.CGraphicsDevice;
import sun.awt.image.OffScreenImage;
import sun.awt.image.SunVolatileImage;
import sun.awt.image.SurfaceManager;
import sun.awt.image.VolatileSurfaceManager;
import sun.java2d.Disposer;
import sun.java2d.DisposerRecord;
import sun.java2d.Surface;
@@ -416,10 +414,4 @@ public final class CGLGraphicsConfig extends CGraphicsConfig
return Math.max(maxTextureSize / getDevice().getScaleFactor(),
getBounds().height);
}
@Override
public VolatileSurfaceManager createVolatileManager(SunVolatileImage image,
Object context) {
return new CGLVolatileSurfaceManager(image, context);
}
}

View File

@@ -75,19 +75,6 @@ BOOL isColorMatchingEnabled() {
return (BOOL)colorMatchingEnabled;
}
BOOL isWindowAnimationEnabled() {
static int windowAnimationEnabled = -1;
if (windowAnimationEnabled == -1) {
JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
if (env == NULL) return NO;
NSString* windowAnimationEnabledProp = [PropertiesUtilities javaSystemPropertyForKey:@"apple.awt.window.animation"
withEnv:env];
windowAnimationEnabled = [@"true" isCaseInsensitiveLike:windowAnimationEnabledProp] ? YES : NO;
J2dRlsTraceLn1(J2D_TRACE_INFO, "AWTWindow_windowAnimationEnabled: %d", windowAnimationEnabled);
}
return (BOOL)windowAnimationEnabled;
}
@interface NSTitlebarAccessoryViewController (Private)
- (void)_setHidden:(BOOL)h animated:(BOOL)a;
@end
@@ -618,10 +605,6 @@ AWT_ASSERT_APPKIT_THREAD;
if (self.nsWindow == nil) return nil; // no hope either
[self.nsWindow release]; // the property retains the object already
if (!isWindowAnimationEnabled()
&& (self.nsWindow.animationBehavior != NSWindowAnimationBehaviorNone)) {
self.nsWindow.animationBehavior = NSWindowAnimationBehaviorNone;
}
if (isColorMatchingEnabled()) {
// Supported by both OpenGL & Metal pipelines
// Tell the system we have an sRGB backing store
@@ -3027,6 +3010,8 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetRoundedCor
NSWindow *w = (NSWindow *)jlong_to_ptr(windowPtr);
[ThreadUtilities performOnMainThreadWaiting:NO block:^(){
w.hasShadow = YES;
w.contentView.wantsLayer = YES;
w.contentView.layer.cornerRadius = radius;
w.contentView.layer.masksToBounds = YES;
w.contentView.layer.opaque = NO;
@@ -3041,11 +3026,11 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetRoundedCor
w.contentView.layer.borderWidth = borderWidth;
w.contentView.layer.borderColor = color.CGColor;
}
w.contentView.wantsLayer = YES;
w.backgroundColor = NSColor.clearColor;
w.opaque = NO;
w.hasShadow = YES;
// remove corner radius animation
[w.contentView.layer removeAllAnimations];
[w invalidateShadow];
}];

View File

@@ -129,7 +129,6 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
[NSNull null], @"anchorPoint",
[NSNull null], @"bounds",
[NSNull null], @"contents",
[NSNull null], @"cornerRadius",
[NSNull null], @"contentsScale",
[NSNull null], @"onOrderIn",
[NSNull null], @"onOrderOut",

View File

@@ -63,7 +63,6 @@ AWT_ASSERT_APPKIT_THREAD;
[NSNull null], @"anchorPoint",
[NSNull null], @"bounds",
[NSNull null], @"contents",
[NSNull null], @"cornerRadius",
[NSNull null], @"contentsScale",
[NSNull null], @"onOrderIn",
[NSNull null], @"onOrderOut",

View File

@@ -1,68 +0,0 @@
/*
* Copyright 2025 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 com.jetbrains.desktop;
import com.jetbrains.exported.JBRApi;
import sun.java2d.vulkan.VKEnv;
import sun.java2d.vulkan.VKGPU;
@JBRApi.Service
@JBRApi.Provides("Vulkan")
public class Vulkan {
Vulkan() {
if (!VKEnv.isVulkanEnabled()) throw new JBRApi.ServiceNotAvailableException("Vulkan is not enabled");
}
boolean isPresentationEnabled() {
return VKEnv.isPresentationEnabled();
}
Device[] getDevices() {
return VKEnv.getDevices().map(Device::new).toArray(Device[]::new);
}
@JBRApi.Provides("Vulkan.Device")
static class Device {
private final VKGPU device;
Device(VKGPU device) {
this.device = device;
}
String getName() {
return device.getName();
}
String getTypeString() {
return device.getType().toString();
}
int getCapabilities() {
return device.getCaps();
}
}
}

View File

@@ -27,6 +27,7 @@ package com.jetbrains.desktop.image;
import sun.awt.image.SurfaceManager;
import sun.java2d.SurfaceData;
import sun.java2d.SurfaceManagerFactory;
import java.awt.AlphaComposite;
import java.awt.GraphicsConfiguration;
@@ -70,10 +71,7 @@ public class TextureWrapperImage extends Image {
public TextureWrapperImage(GraphicsConfiguration gc, long texture)
throws UnsupportedOperationException, IllegalArgumentException {
this.gc = gc;
SurfaceManager surfaceManager;
if (gc instanceof SurfaceManager.TextureWrapperFactory factory) {
surfaceManager = factory.createTextureWrapperSurfaceManager(gc, this, texture);
} else throw new UnsupportedOperationException();
SurfaceManager surfaceManager = SurfaceManagerFactory.getInstance().createTextureWrapperSurfaceManager(gc, this, texture);
sd = surfaceManager.getPrimarySurfaceData();
SurfaceManager.setManager(this, surfaceManager);
}

View File

@@ -4547,11 +4547,6 @@ public class Window extends Container implements Accessible {
}
}
@Override
public void addWindowListener(Window w, WindowListener listener) {
w.addWindowListener(listener);
}
private static void dumpCounter(final String counterName, final double valPerSecond) {
if (USE_COUNTERS) {
doLog(String.format("%s per second: %.2f", counterName, valPerSecond),

View File

@@ -689,27 +689,6 @@ public class BasicTreeUI extends TreeUI
return bounds;
}
/**
* A potentially faster version of {@link #getPathBounds(JTree, TreePath)}
* which calculates only {@code y} and {@code height}
* of the bounding {@code Rectangle}
*/
private Rectangle getVerticalPathBounds(TreePath path) {
if (tree == null || treeState == null) {
return null;
}
int rowHeight = treeState.getRowHeight();
if (rowHeight <= 0) {
return getPathBounds(tree, path);
}
int row = treeState.getRowForPath(path);
if (row < 0) {
return null;
}
return new Rectangle(0, tree.getInsets().top + row * rowHeight,
0, rowHeight);
}
/**
* Returns the path for passed in row. If row is not visible
* null is returned.
@@ -4340,9 +4319,10 @@ public class BasicTreeUI extends TreeUI
updateSize();
}
else if (treeState.isExpanded(parentPath)) {
boolean isShowing = tree.isShowing();
TreePath minPath = null;
Rectangle minBounds = null;
if (tree.isShowing()) {
if (isShowing) {
// Changed nodes are visible
// Find the minimum index, we only need paint from there
// down.
@@ -4353,27 +4333,28 @@ public class BasicTreeUI extends TreeUI
Object minChild = treeModel.getChild(
parentPath.getLastPathComponent(), minIndex);
minPath = parentPath.pathByAddingChild(minChild);
minBounds = getVerticalPathBounds(minPath);
minBounds = getPathBounds(tree, minPath);
}
// Forward to the treestate
treeState.treeNodesChanged(e);
// Mark preferred size as bogus.
updateSize0();
if (minBounds != null) {
if (isShowing) {
// And repaint
Rectangle newMinBounds = getVerticalPathBounds(minPath);
if (newMinBounds != null) {
if (indices.length == 1 &&
newMinBounds.height == minBounds.height) {
tree.repaint(0, minBounds.y, tree.getWidth(),
minBounds.height);
} else {
tree.repaint(0, minBounds.y, tree.getWidth(),
tree.getHeight() - minBounds.y);
}
Rectangle newMinBounds = getPathBounds(tree, minPath);
if (minBounds == null || newMinBounds == null) {
return;
}
if (indices.length == 1 &&
newMinBounds.height == minBounds.height) {
tree.repaint(0, minBounds.y, tree.getWidth(),
minBounds.height);
} else {
tree.repaint(0, minBounds.y, tree.getWidth(),
tree.getHeight() - minBounds.y);
}
}
}

View File

@@ -37,7 +37,6 @@ import java.awt.event.InputEvent;
import java.awt.event.InvocationEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.WindowListener;
import java.awt.geom.Point2D;
import java.awt.image.BufferStrategy;
import java.awt.peer.ComponentPeer;
@@ -358,8 +357,6 @@ public final class AWTAccessor {
double getCounterPerSecond(Window w, String counterName);
void dumpStats(Window w, boolean reset, StringBuilder sb);
void addWindowListener(Window w, WindowListener listener);
}
/**

View File

@@ -50,7 +50,6 @@ import java.awt.KeyboardFocusManager;
import java.awt.Label;
import java.awt.MenuComponent;
import java.awt.Panel;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.ScrollPane;
import java.awt.Scrollbar;
@@ -92,7 +91,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import com.jetbrains.exported.JBRApi;
import sun.awt.im.InputContext;
import sun.awt.image.ByteArrayImageSource;
import sun.awt.image.FileImageSource;
@@ -2148,23 +2146,6 @@ public abstract class SunToolkit extends Toolkit
return AWTAccessor.getAWTEventAccessor().isSystemGenerated(e);
}
@JBRApi.Service
@JBRApi.Provides("RelativePointerMovement")
public interface RelativePointerMovementInfoProvider {
private static RelativePointerMovementInfoProvider create() {
var tk = Toolkit.getDefaultToolkit();
if (tk instanceof ComponentFactory cf) {
var mouseInfoPeer = cf.getMouseInfoPeer();
if (mouseInfoPeer instanceof RelativePointerMovementInfoProvider p){
return p;
}
}
throw new JBRApi.ServiceNotAvailableException("Service not supported for toolkit " + tk.getClass().getName());
}
Point getAccumulatedMouseDeltaAndReset();
}
} // class SunToolkit

View File

@@ -37,6 +37,7 @@ import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.awt.image.VolatileImage;
import sun.java2d.SunGraphics2D;
import sun.java2d.SurfaceManagerFactory;
import sun.java2d.DestSurfaceProvider;
import sun.java2d.Surface;
import sun.java2d.pipe.Region;
@@ -157,19 +158,29 @@ public class SunVolatileImage extends VolatileImage
return forcedAccelSurfaceType;
}
private VolatileSurfaceManager createSurfaceManager(
Object context, ImageCapabilities caps) {
// GraphicsConfig may provide some specific surface manager
// implementation.
// In case it doesn't, or we were specifically requested to use
// an unaccelerated surface, fall back to the buffered image
// surface manager.
if ((caps == null || caps.isAccelerated()) &&
graphicsConfig instanceof SurfaceManager.Factory factory) {
return factory.createVolatileManager(this, context);
} else {
protected VolatileSurfaceManager createSurfaceManager(Object context,
ImageCapabilities caps)
{
/**
* Platform-specific SurfaceManagerFactories will return a
* manager suited to acceleration on each platform. But if
* the user is asking for a VolatileImage from a BufferedImageGC,
* then we need to return the appropriate unaccelerated manager.
* Note: this could change in the future; if some platform would
* like to accelerate BIGC volatile images, then this special-casing
* of the BIGC graphicsConfig should live in platform-specific
* code instead.
* We do the same for a Printer Device, and if user requested an
* unaccelerated VolatileImage by passing the capabilities object.
*/
if (graphicsConfig instanceof BufferedImageGraphicsConfig ||
graphicsConfig instanceof sun.print.PrinterGraphicsConfig ||
(caps != null && !caps.isAccelerated()))
{
return new BufImgVolatileSurfaceManager(this, context);
}
SurfaceManagerFactory smf = SurfaceManagerFactory.getInstance();
return smf.createVolatileManager(this, context);
}
private Color getForeground() {

View File

@@ -184,31 +184,6 @@ public abstract class SurfaceManager {
}
}
/**
* See TextureWrapperImage.
*/
public interface TextureWrapperFactory {
SurfaceManager createTextureWrapperSurfaceManager(
GraphicsConfiguration gc, Image image, long texture);
}
/**
* An interface for GraphicsConfiguration objects to implement if
* they create their own VolatileSurfaceManager implementations.
*/
public interface Factory {
/**
* Creates a new instance of a VolatileSurfaceManager given a
* compatible SunVolatileImage.
* An optional context Object can be supplied as a way for the caller
* to pass pipeline-specific context data to the VolatileSurfaceManager
* (such as a backbuffer handle, for example).
*/
VolatileSurfaceManager createVolatileManager(SunVolatileImage image,
Object context);
}
/**
* An interface for GraphicsConfiguration objects to implement if
* their surfaces accelerate images using SurfaceDataProxy objects.
@@ -227,8 +202,7 @@ public abstract class SurfaceManager {
}
public static class ProxyCache {
private final Map<SurfaceManager, SurfaceDataProxy> map =
Collections.synchronizedMap(new WeakHashMap<>());
private final Map<SurfaceManager, SurfaceDataProxy> map = Collections.synchronizedMap(new WeakHashMap<>());
/**
* Return a cached SurfaceDataProxy object for a given SurfaceManager.
@@ -279,8 +253,7 @@ public abstract class SurfaceManager {
void flush(boolean deaccelerate) {
synchronized (weakCache) {
Iterator<WeakReference<SurfaceDataProxy>> i =
weakCache.values().iterator();
Iterator<WeakReference<SurfaceDataProxy>> i = weakCache.values().iterator();
while (i.hasNext()) {
SurfaceDataProxy sdp = i.next().get();
if (sdp == null || sdp.flush(deaccelerate)) {

View File

@@ -0,0 +1,97 @@
/*
* Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved.
* 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.java2d;
import sun.awt.image.SunVolatileImage;
import sun.awt.image.SurfaceManager;
import sun.awt.image.VolatileSurfaceManager;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
/**
* This factory creates platform specific VolatileSurfaceManager
* implementations.
*
* There are two platform specific SurfaceManagerFactories in OpenJDK,
* UnixSurfaceManagerFactory and WindowsSurfaceManagerFactory.
* The actually used SurfaceManagerFactory is set by the respective platform
* GraphicsEnvironment implementations in the static initializer.
*/
public abstract class SurfaceManagerFactory {
/**
* The single shared instance.
*/
private static SurfaceManagerFactory instance;
/**
* Returns the surface manager factory instance. This returns a factory
* that has been set by {@link #setInstance(SurfaceManagerFactory)}.
*
* @return the surface manager factory
*/
public static synchronized SurfaceManagerFactory getInstance() {
if (instance == null) {
throw new IllegalStateException("No SurfaceManagerFactory set.");
}
return instance;
}
/**
* Sets the surface manager factory. This may only be called once, and it
* may not be set back to {@code null} when the factory is already
* instantiated.
*
* @param factory the factory to set
*/
public static synchronized void setInstance(SurfaceManagerFactory factory) {
if (factory == null) {
// We don't want to allow setting this to null at any time.
throw new IllegalArgumentException("factory must be non-null");
}
if (instance != null) {
// We don't want to re-set the instance at any time.
throw new IllegalStateException("The surface manager factory is already initialized");
}
instance = factory;
}
/**
* Creates a new instance of a VolatileSurfaceManager given any
* arbitrary SunVolatileImage. An optional context Object can be supplied
* as a way for the caller to pass pipeline-specific context data to
* the VolatileSurfaceManager (such as a backbuffer handle, for example).
*/
public abstract VolatileSurfaceManager
createVolatileManager(SunVolatileImage image, Object context);
public abstract SurfaceManager createTextureWrapperSurfaceManager(GraphicsConfiguration gc, Image image, long texture);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -144,9 +144,7 @@ final class Curve {
// finds points where the first and second derivative are
// perpendicular. This happens when g(t) = f'(t)*f''(t) == 0 (where
// * is a dot product). Unfortunately, we have to solve a cubic.
private int perpendiculardfddf(final double[] pts, final int off,
final double A, final double B)
{
private int perpendiculardfddf(final double[] pts, final int off) {
assert pts.length >= off + 4;
// these are the coefficients of some multiple of g(t) (not g(t),
@@ -157,7 +155,7 @@ final class Curve {
final double c = 2.0d * (dax * cx + day * cy) + dbx * dbx + dby * dby;
final double d = dbx * cx + dby * cy;
return Helpers.cubicRootsInAB(a, b, c, d, pts, off, A, B);
return Helpers.cubicRootsInAB(a, b, c, d, pts, off, 0.0d, 1.0d);
}
// Tries to find the roots of the function ROC(t)-w in [0, 1). It uses
@@ -173,43 +171,35 @@ final class Curve {
// at most 4 sub-intervals of (0,1). ROC has asymptotes at inflection
// points, so roc-w can have at least 6 roots. This shouldn't be a
// problem for what we're trying to do (draw a nice looking curve).
int rootsOfROCMinusW(final double[] roots, final int off, final double w2,
final double A, final double B)
{
int rootsOfROCMinusW(final double[] roots, final int off, final double w2, final double err) {
// no OOB exception, because by now off<=6, and roots.length >= 10
assert off <= 6 && roots.length >= 10;
int ret = off;
final int end = off + perpendiculardfddf(roots, off, A, B);
Helpers.isort(roots, off, end);
final int end = off + perpendiculardfddf(roots, off);
roots[end] = 1.0d; // always check interval end points
double t0 = 0.0d;
double ft0 = eliminateInf(ROCsq(t0) - w2);
double t1, ft1;
double t0 = 0.0d, ft0 = ROCsq(t0) - w2;
for (int i = off; i <= end; i++) {
t1 = roots[i];
ft1 = eliminateInf(ROCsq(t1) - w2);
double t1 = roots[i], ft1 = ROCsq(t1) - w2;
if (ft0 == 0.0d) {
roots[ret++] = t0;
} else if (ft1 * ft0 < 0.0d) { // have opposite signs
// (ROC(t)^2 == w^2) == (ROC(t) == w) is true because
// ROC(t) >= 0 for all t.
roots[ret++] = falsePositionROCsqMinusX(t0, t1, ft0, ft1, w2, A); // A = err
roots[ret++] = falsePositionROCsqMinusX(t0, t1, w2, err);
}
t0 = t1;
ft0 = ft1;
}
return ret - off;
}
private final static double MAX_ROC_SQ = 1e20;
private static double eliminateInf(final double x2) {
// limit the value of x to avoid numerical problems (smaller step):
// must handle NaN and +Infinity:
return (x2 <= MAX_ROC_SQ) ? x2 : MAX_ROC_SQ;
private static double eliminateInf(final double x) {
return (x == Double.POSITIVE_INFINITY ? Double.MAX_VALUE :
(x == Double.NEGATIVE_INFINITY ? Double.MIN_VALUE : x));
}
// A slight modification of the false position algorithm on wikipedia.
@@ -220,18 +210,17 @@ final class Curve {
// and turn out. Same goes for the newton's method
// algorithm in Helpers.java
private double falsePositionROCsqMinusX(final double t0, final double t1,
final double ft0, final double ft1,
final double w2, final double err)
{
final int iterLimit = 100;
int side = 0;
double s = t0, fs = eliminateInf(ft0);
double t = t1, ft = eliminateInf(ft1);
double t = t1, ft = eliminateInf(ROCsq(t) - w2);
double s = t0, fs = eliminateInf(ROCsq(s) - w2);
double r = s, fr;
for (int i = 0; i < iterLimit && Math.abs(t - s) > err; i++) {
for (int i = 0; i < iterLimit && Math.abs(t - s) > err * Math.abs(t + s); i++) {
r = (fs * t - ft * s) / (fs - ft);
fr = eliminateInf(ROCsq(r) - w2);
fr = ROCsq(r) - w2;
if (sameSign(fr, ft)) {
ft = fr; t = r;
if (side < 0) {
@@ -252,7 +241,7 @@ final class Curve {
break;
}
}
return (Math.abs(ft) <= Math.abs(fs)) ? t : s;
return r;
}
private static boolean sameSign(final double x, final double y) {
@@ -267,9 +256,9 @@ final class Curve {
final double dy = t * (t * day + dby) + cy;
final double ddx = 2.0d * dax * t + dbx;
final double ddy = 2.0d * day * t + dby;
final double dx2dy2 = dx * dx + dy * dy; // positive
final double dxddyddxdy = dx * ddy - dy * ddx;
// may return +Infinity if dxddyddxdy = 0 or NaN if 0/0:
return (dx2dy2 * dx2dy2 * dx2dy2) / (dxddyddxdy * dxddyddxdy); // both positive
final double dx2dy2 = dx * dx + dy * dy;
final double ddx2ddy2 = ddx * ddx + ddy * ddy;
final double ddxdxddydy = ddx * dx + ddy * dy;
return dx2dy2 * ((dx2dy2 * dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy * ddxdxddydy));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -566,7 +566,7 @@ public final class DMarlinRenderingEngine extends RenderingEngine
}
private static boolean nearZero(final double num) {
return Math.abs(num) < 2.0d * Helpers.ulp(num);
return Math.abs(num) < 2.0d * Math.ulp(num);
}
abstract static class NormalizingPathIterator implements PathIterator {

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -31,19 +31,12 @@ import sun.java2d.marlin.stats.StatLong;
final class Helpers implements MarlinConst {
private final static double T_ERR = 1e-4;
private final static double T_A = T_ERR;
private final static double T_B = 1.0 - T_ERR;
private static final double EPS = 1e-9d;
private Helpers() {
throw new Error("This is a non instantiable class");
}
/** use lower precision like former Pisces and Marlin (float-precision) */
static double ulp(final double value) { return Math.ulp((float)value); }
static boolean within(final double x, final double y) {
return within(x, y, EPS);
}
@@ -329,10 +322,10 @@ final class Helpers implements MarlinConst {
// now we must subdivide at points where one of the offset curves will have
// a cusp. This happens at ts where the radius of curvature is equal to w.
ret += c.rootsOfROCMinusW(ts, ret, w2, T_A, T_B);
ret += c.rootsOfROCMinusW(ts, ret, w2, 0.0001d);
ret = filterOutNotInAB(ts, 0, ret, T_A, T_B);
isort(ts, 0, ret);
ret = filterOutNotInAB(ts, 0, ret, 0.0001d, 0.9999d);
isort(ts, ret);
return ret;
}
@@ -361,7 +354,7 @@ final class Helpers implements MarlinConst {
if ((outCodeOR & OUTCODE_BOTTOM) != 0) {
ret += curve.yPoints(ts, ret, clipRect[1]);
}
isort(ts, 0, ret);
isort(ts, ret);
return ret;
}
@@ -381,11 +374,11 @@ final class Helpers implements MarlinConst {
}
}
static void isort(final double[] a, final int off, final int len) {
for (int i = off + 1, j; i < len; i++) {
static void isort(final double[] a, final int len) {
for (int i = 1, j; i < len; i++) {
final double ai = a[i];
j = i - 1;
for (; j >= off && a[j] > ai; j--) {
for (; j >= 0 && a[j] > ai; j--) {
a[j + 1] = a[j];
}
a[j + 1] = ai;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -886,8 +886,8 @@ final class Stroker implements StartFlagPathConsumer2D, MarlinConst {
// if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4,
// in which case ignore if p1 == p2
final boolean p1eqp2 = Helpers.withinD(dx1, dy1, 6.0d * Helpers.ulp(y2));
final boolean p3eqp4 = Helpers.withinD(dx4, dy4, 6.0d * Helpers.ulp(y4));
final boolean p1eqp2 = Helpers.withinD(dx1, dy1, 6.0d * Math.ulp(y2));
final boolean p3eqp4 = Helpers.withinD(dx4, dy4, 6.0d * Math.ulp(y4));
if (p1eqp2 && p3eqp4) {
return getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
@@ -905,7 +905,7 @@ final class Stroker implements StartFlagPathConsumer2D, MarlinConst {
final double l1sq = dx1 * dx1 + dy1 * dy1;
final double l4sq = dx4 * dx4 + dy4 * dy4;
if (Helpers.within(dotsq, l1sq * l4sq, 4.0d * Helpers.ulp(dotsq))) {
if (Helpers.within(dotsq, l1sq * l4sq, 4.0d * Math.ulp(dotsq))) {
return getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
}
@@ -1078,8 +1078,8 @@ final class Stroker implements StartFlagPathConsumer2D, MarlinConst {
// equal if they're very close to each other.
// if p1 == p2 or p2 == p3: draw line from p1->p3
final boolean p1eqp2 = Helpers.withinD(dx12, dy12, 6.0d * Helpers.ulp(y2));
final boolean p2eqp3 = Helpers.withinD(dx23, dy23, 6.0d * Helpers.ulp(y3));
final boolean p1eqp2 = Helpers.withinD(dx12, dy12, 6.0d * Math.ulp(y2));
final boolean p2eqp3 = Helpers.withinD(dx23, dy23, 6.0d * Math.ulp(y3));
if (p1eqp2 || p2eqp3) {
return getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
@@ -1091,7 +1091,7 @@ final class Stroker implements StartFlagPathConsumer2D, MarlinConst {
final double l1sq = dx12 * dx12 + dy12 * dy12;
final double l3sq = dx23 * dx23 + dy23 * dy23;
if (Helpers.within(dotsq, l1sq * l3sq, 4.0d * Helpers.ulp(dotsq))) {
if (Helpers.within(dotsq, l1sq * l3sq, 4.0d * Math.ulp(dotsq))) {
return getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,7 +27,7 @@ package sun.java2d.marlin;
public final class Version {
private static final String VERSION = "marlin-0.9.4.9-Unsafe-OpenJDK";
private static final String VERSION = "marlin-0.9.4.7-Unsafe-OpenJDK";
public static String getVersion() {
return VERSION;

View File

@@ -31,20 +31,18 @@ package sun.java2d.marlin.stats;
public class StatLong {
public final String name;
public long count = 0L;
public long sum = 0L;
public long min = Integer.MAX_VALUE;
public long max = Integer.MIN_VALUE;
public long count, sum, min, max;
public StatLong(final String name) {
this.name = name;
reset();
}
public void reset() {
count = 0L;
sum = 0L;
min = Integer.MAX_VALUE;
max = Integer.MIN_VALUE;
min = Long.MAX_VALUE;
max = Long.MIN_VALUE;
}
public void add(final int val) {
@@ -78,7 +76,7 @@ public class StatLong {
sb.append(name).append('[').append(count);
sb.append("] sum: ").append(sum).append(" avg: ");
sb.append(trimTo3Digits(((double) sum) / count));
sb.append(" [").append(min).append(" | ").append(max).append("]");
sb.append(" [").append(min).append(" - ").append(max).append("]");
return sb;
}
@@ -89,7 +87,7 @@ public class StatLong {
* @return double value with only 3 decimal digits
*/
public static double trimTo3Digits(final double value) {
return ((long) (1e3d * value)) / 1e3d;
return Double.isFinite(value) ? ((long) (1e3d * value)) / 1e3d : Double.NaN;
}
}

View File

@@ -35,7 +35,7 @@ import sun.java2d.pipe.hw.AccelGraphicsConfig;
* methods directly from OGLSurfaceData.
*/
interface OGLGraphicsConfig extends
AccelGraphicsConfig, SurfaceManager.ProxiedGraphicsConfig, SurfaceManager.Factory
AccelGraphicsConfig, SurfaceManager.ProxiedGraphicsConfig
{
OGLContext getContext();
long getNativeConfigInfo();

View File

@@ -48,7 +48,7 @@ public final class VKEnv {
@SuppressWarnings("removal")
private static final boolean accelsd = vulkan && "true".equalsIgnoreCase(AccessController.doPrivileged(
(PrivilegedAction<String>) () -> System.getProperty("sun.java2d.vulkan.accelsd", "true")));
(PrivilegedAction<String>) () -> System.getProperty("sun.java2d.vulkan.accelsd", "")));
@SuppressWarnings("removal")
private static final int deviceNumber = !vulkan ? 0 : AccessController.doPrivileged(

View File

@@ -33,6 +33,7 @@ import sun.awt.image.SunVolatileImage;
import sun.awt.image.SurfaceManager;
import sun.awt.image.VolatileSurfaceManager;
import sun.java2d.Surface;
import sun.java2d.SurfaceManagerFactory;
import sun.java2d.pipe.BufferedContext;
import sun.java2d.pipe.hw.AccelGraphicsConfig;
import sun.java2d.pipe.hw.AccelTypedVolatileImage;
@@ -53,9 +54,8 @@ import static sun.java2d.pipe.hw.AccelSurface.TEXTURE;
* for most of the methods, including base methods of GraphicsConfiguration class.
*/
public interface VKGraphicsConfig extends AccelGraphicsConfig,
SurfaceManager.ProxiedGraphicsConfig, SurfaceManager.Factory {
SurfaceManager.ProxiedGraphicsConfig {
@Override
default VolatileSurfaceManager createVolatileManager(SunVolatileImage image,
Object context) {
return new VKVolatileSurfaceManager(image, context);

View File

@@ -36,7 +36,6 @@ import sun.java2d.loops.CompositeType;
import sun.java2d.loops.GraphicsPrimitive;
import sun.java2d.loops.SurfaceType;
import static sun.java2d.pipe.BufferedOpCodes.CONFIGURE_SURFACE;
import static sun.java2d.pipe.BufferedOpCodes.DISPOSE_SURFACE;
import sun.java2d.pipe.BufferedContext;
import sun.java2d.pipe.ParallelogramPipe;
import sun.java2d.pipe.PixelToParallelogramConverter;
@@ -44,6 +43,7 @@ import sun.java2d.pipe.RenderBuffer;
import sun.java2d.pipe.TextPipe;
import sun.java2d.pipe.hw.AccelSurface;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
@@ -137,28 +137,6 @@ public abstract class VKSurfaceData extends SurfaceData
}
}
/**
* Disposes the native resources associated with the given VKSurfaceData
* (referenced by the pData parameter). This method is invoked from
* the native Dispose() method from the Disposer thread when the
* Java-level VKSurfaceData object is about to go away.
*/
static void dispose(long pData) {
VKRenderQueue rq = VKRenderQueue.getInstance();
rq.lock();
try {
RenderBuffer buf = rq.getBuffer();
rq.ensureCapacityAndAlignment(12, 4);
buf.putInt(DISPOSE_SURFACE);
buf.putLong(pData);
// this call is expected to complete synchronously, so flush now
rq.flushNow();
} finally {
rq.unlock();
}
}
public BufferedImage getSnapshot(int x, int y, int width, int height) {
BufferedImage image = getFormat().createCompatibleImage(width, height, getTransparency());
SurfaceData sd = SurfaceData.getPrimarySurfaceData(image);

View File

@@ -0,0 +1,20 @@
#define ALPHA_TYPE_PRE_MULTIPLIED 0U
#define ALPHA_TYPE_STRAIGHT 1U
vec4 convertAlpha(vec4 color, uint inType, uint outType) {
if (inType == ALPHA_TYPE_STRAIGHT && outType == ALPHA_TYPE_PRE_MULTIPLIED) {
return vec4(color.rgb * color.a, color.a);
} else if (inType == ALPHA_TYPE_PRE_MULTIPLIED && outType == ALPHA_TYPE_STRAIGHT && color.a > 0.0) {
return vec4(color.rgb / color.a, color.a);
} else return color;
}
#ifdef ALPHA_TYPE_SPEC_INDEX
layout (constant_id = ALPHA_TYPE_SPEC_INDEX ) const uint const_InAlphaType = ALPHA_TYPE_PRE_MULTIPLIED;
layout (constant_id = ALPHA_TYPE_SPEC_INDEX+1) const uint const_OutAlphaType = ALPHA_TYPE_PRE_MULTIPLIED;
vec4 convertAlpha(vec4 color) {
return convertAlpha(color, const_InAlphaType, const_OutAlphaType);
}
#endif

View File

@@ -1,7 +1,7 @@
#version 450
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
DEFAULT_PUSH_CONSTANTS();
#define ALPHA_TYPE_SPEC_INDEX 0
#include "alpha_type.glsl"
layout(set = 0, binding = 0) uniform texture2D u_Texture;
layout(set = 1, binding = 0) uniform sampler u_Sampler;
@@ -9,5 +9,5 @@ layout(location = 0) in vec2 in_TexCoord;
layout(location = 0) out vec4 out_Color;
void main() {
out_Color = APPLY_COMPOSITE(convertAlpha(texture(sampler2D(u_Texture, u_Sampler), in_TexCoord)));
out_Color = convertAlpha(texture(sampler2D(u_Texture, u_Sampler), in_TexCoord));
}

View File

@@ -1,12 +1,14 @@
#version 450
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
layout(push_constant) uniform PushConstants {
mat2x3 transform;
} push;
layout(location = 0) in vec2 in_Position;
layout(location = 1) in vec2 in_TexCoord;
layout(location = 0) out vec2 out_TexCoord;
void main() {
gl_Position = transformToDeviceSpace(in_Position);
gl_Position = vec4(vec3(in_Position, 1.0)*push.transform, 0.0, 1.0);
out_TexCoord = in_TexCoord;
}

View File

@@ -1,9 +1,11 @@
#version 450
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
layout(push_constant) uniform PushConstants {
mat2x3 transform;
} push;
layout(location = 0) in ivec2 in_Position;
void main() {
gl_Position = transformToDeviceSpace(in_Position);
gl_Position = vec4(vec3(in_Position, 1)*push.transform, 0.0, 1.0);
}

View File

@@ -1,12 +1,14 @@
#version 450
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
layout(push_constant) uniform PushConstants {
mat2x3 transform;
} push;
layout(location = 0) in vec2 in_Position;
layout(location = 1) in uint in_Color;
layout(location = 1) in vec4 in_Color;
layout(location = 0) out flat vec4 out_Color;
void main() {
gl_Position = transformToDeviceSpace(in_Position);
out_Color = convertAlpha(decodeColor(in_Color)); // No need to APPLY_COMPOSITE - it was already done on the host.
gl_Position = vec4(vec3(in_Position, 1)*push.transform, 0.0, 1.0);
out_Color = in_Color;
}

View File

@@ -1,103 +0,0 @@
// Shader specialization.
#define SHADER_MOD_XOR 1U // Xor composite mode
#define SHADER_MOD_MASK 2U // MASK_FILL / MASK_BLIT
layout (constant_id = 0) const uint const_InAlphaType = 0;
layout (constant_id = 1) const uint const_OutAlphaType = 0;
layout (constant_id = 2) const uint const_ShaderVariant = 0;
layout (constant_id = 3) const uint const_ShaderModifier = 0;
// Host structs.
struct VKTransform {
float m00, m01, m02;
float m10, m11, m12;
};
struct VKCompositeConstants {
uint xorColor;
float extraAlpha;
};
// Vertex shader transformation support.
#ifdef STAGE_VERT
layout(push_constant) uniform PushConstants { VKTransform transform; } push;
vec4 transformToDeviceSpace(vec2 v) {
return vec4(vec3(v, 1.0) * mat2x3(push.transform.m00, push.transform.m01, push.transform.m02, push.transform.m10, push.transform.m11, push.transform.m12), 0.0, 1.0);
}
#endif
// Fragment shader push constant support.
#ifdef STAGE_FRAG
#define PUSH_CONSTANTS_IMPL(STATEMENT) \
layout(push_constant) uniform PushConstants { VKTransform _; VKCompositeConstants push_composite; STATEMENT }
#define DEFAULT_PUSH_CONSTANTS() PUSH_CONSTANTS_IMPL(STAGE_FRAG)
#define PUSH_CONSTANTS(TYPE) PUSH_CONSTANTS_IMPL(TYPE push;)
#endif
// Color conversion support.
#define ALPHA_TYPE_PRE_MULTIPLIED 0U
#define ALPHA_TYPE_STRAIGHT 1U
vec4 convertAlpha(vec4 color, uint inType, uint outType) {
if (inType == ALPHA_TYPE_STRAIGHT && outType == ALPHA_TYPE_PRE_MULTIPLIED) {
return vec4(color.rgb * color.a, color.a);
} else if (inType == ALPHA_TYPE_PRE_MULTIPLIED && outType == ALPHA_TYPE_STRAIGHT && color.a > 0.0) {
return vec4(color.rgb / color.a, color.a);
} else return color;
}
vec4 convertAlpha(vec4 color) {
return convertAlpha(color, const_InAlphaType, const_OutAlphaType);
}
// When applying alpha to a color, straight alpha only multiplies alpha,
// and pre-multiplied multiplies the whole color. Use this for convenience.
vec4 alphaMask(float alpha, uint alphaType) {
return alphaType == ALPHA_TYPE_PRE_MULTIPLIED ? vec4(alpha) : vec4(1.0, 1.0, 1.0, alpha);
}
// Decode color from uint-packed ARGB components.
vec4 decodeColor(uint srgb) {
return vec4((uvec4(srgb) >> uvec4(16, 8, 0, 24)) & 0xFFU) / 255.0;
}
#ifdef STAGE_FRAG
// Before outputting the color, some post-processing is needed:
// - For alpha composite, apply extra alpha.
// - For XOR composite, apply xor.
vec4 applyComposite(vec4 color, VKCompositeConstants composite) {
if ((const_ShaderModifier & SHADER_MOD_XOR) != 0) {
uvec4 xor = uvec4(composite.xorColor) >> uvec4(16, 8, 0, 24);
xor = (uvec4(color * 255.0) ^ xor) & 0xFFU;
return vec4(xor) / 255.0;
} else return color * alphaMask(composite.extraAlpha, const_OutAlphaType);
}
#define APPLY_COMPOSITE(COLOR) applyComposite(COLOR, push_composite)
// MASK_FILL / MASK_BLIT support.
int calculateMaskIndex(vec2 localCoord, ivec4 originOffsetAndScanline) {
ivec2 maskPos = ivec2(localCoord - vec2(originOffsetAndScanline.xy));
int offset = originOffsetAndScanline.z;
int scanline = originOffsetAndScanline.w;
return offset + scanline * maskPos.y + min(scanline, maskPos.x);
}
vec4 applyMaskOp(vec4 color, float mask) {
if ((const_ShaderModifier & SHADER_MOD_XOR) != 0) return color * float(mask > 0.0);
else return color * alphaMask(mask, const_OutAlphaType);
}
#define APPLY_MASK(COLOR) applyMaskOp(COLOR, imageLoad(u_Mask, calculateMaskIndex(gl_FragCoord.xy, in_OriginOffsetAndScanline)).r)
// Generic shader support.
#define GENERIC_INOUT() \
layout(location = 0) out vec4 out_Color; \
layout(location = 0) in vec2 in_Position; \
layout(location = 1) in flat uint in_Data; \
layout(location = 2) in flat ivec4 in_OriginOffsetAndScanline; \
layout(origin_upper_left) in vec4 gl_FragCoord; \
layout(set = 0, binding = 0, r8) uniform readonly restrict imageBuffer u_Mask
// Generic color output - handles composite and mask automatically.
#define OUTPUT(COLOR) out_Color = COLOR; out_Color = APPLY_COMPOSITE(out_Color); \
if ((const_ShaderModifier & SHADER_MOD_MASK) != 0) out_Color = APPLY_MASK(out_Color)
#endif

View File

@@ -1,21 +0,0 @@
#version 450
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
#define SHADER_VARIANT_GRADIENT_CLAMP 0
#define SHADER_VARIANT_GRADIENT_CYCLE 1
struct VKGradientPaintConstants {
vec4 c0, c1;
vec3 p;
};
PUSH_CONSTANTS(VKGradientPaintConstants);
GENERIC_INOUT();
void main() {
float t = dot(vec3(in_Position, 1.0), push.p);
t = const_ShaderVariant == SHADER_VARIANT_GRADIENT_CYCLE ?
abs(mod(t + 1.0, 2.0) - 1.0) : // Cycle
clamp(t, 0.0, 1.0); // Clamp
OUTPUT(convertAlpha(mix(push.c0, push.c1, t)));
}

View File

@@ -1,21 +0,0 @@
#version 450
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
layout(location = 0) in ivec4 in_PositionOffsetAndScanline;
layout(location = 1) in uint in_Data;
layout(location = 0) out vec2 out_Position;
layout(location = 1) out uint out_Data;
// This starts with "Origin" and not "Position" intentionally.
// When drawing, vertices are ordered in a such way, that provoking vertex is always the top-left one.
// This gives us an easy way to calculate offset within the rectangle without additional inputs.
layout(location = 2) out flat ivec4 out_OriginOffsetAndScanline;
void main() {
out_Position = in_PositionOffsetAndScanline.xy;
out_Data = in_Data;
out_OriginOffsetAndScanline = in_PositionOffsetAndScanline;
gl_Position = transformToDeviceSpace(in_PositionOffsetAndScanline.xy);
}

View File

@@ -1,6 +1,4 @@
#version 450
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
layout(set = 0, binding = 0, r8) uniform readonly restrict imageBuffer u_Mask;
@@ -12,5 +10,9 @@ layout(location = 1) in flat vec4 in_Color;
layout(location = 0) out vec4 out_Color;
void main() {
out_Color = APPLY_MASK(in_Color);
ivec2 maskPos = ivec2(gl_FragCoord.xy - vec2(in_OriginOffsetAndScanline.xy));
int offset = in_OriginOffsetAndScanline.z;
int scanline = in_OriginOffsetAndScanline.w;
int maskIndex = offset + scanline * maskPos.y + min(scanline, maskPos.x);
out_Color = in_Color * imageLoad(u_Mask, maskIndex).r;
}

View File

@@ -1,9 +1,11 @@
#version 450
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
layout(push_constant) uniform PushConstants {
mat2x3 transform;
} push;
layout(location = 0) in ivec4 in_PositionOffsetAndScanline;
layout(location = 1) in uint in_Color;
layout(location = 1) in vec4 in_Color;
// This starts with "Origin" and not "Position" intentionally.
// When drawing, vertices are ordered in a such way, that provoking vertex is always the top-left one.
@@ -12,7 +14,7 @@ layout(location = 0) out flat ivec4 out_OriginOffsetAndScanline;
layout(location = 1) out flat vec4 out_Color;
void main() {
gl_Position = transformToDeviceSpace(in_PositionOffsetAndScanline.xy);
gl_Position = vec4(vec3(in_PositionOffsetAndScanline.xy, 1)*push.transform, 0.0, 1.0);
out_OriginOffsetAndScanline = in_PositionOffsetAndScanline;
out_Color = convertAlpha(decodeColor(in_Color));
out_Color = in_Color;
}

View File

@@ -1,15 +0,0 @@
#version 450
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
layout(location = 0) in vec2 in_Position;
layout(location = 1) in uint in_Data;
layout(location = 0) out vec2 out_Position;
layout(location = 1) out uint out_Data;
layout(location = 2) out flat ivec4 _; // Unused output
void main() {
out_Position = in_Position;
out_Data = in_Data;
gl_Position = transformToDeviceSpace(in_Position);
}

View File

@@ -114,7 +114,7 @@ void VKBlitLoops_IsoBlit(VKSDOps* srcOps, jint filter,
VK_COMPONENT_SWIZZLE_ONE);
VKPackedSwizzle swizzle = srcOpaque ? OPAQUE_SWIZZLE : 0;
if (!VKRenderer_Validate(SHADER_BLIT, NO_SHADER_VARIANT, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, alphaType)) return;
if (!VKRenderer_Validate(SHADER_BLIT, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, alphaType)) return;
VKRenderer_DrawImage(srcOps->image, srcOps->image->format, swizzle, filter, SAMPLER_WRAP_BORDER,
(float)sx1, (float)sy1, (float)sx2, (float)sy2, (float)dx1, (float)dy1, (float)dx2, (float)dy2);
VKRenderer_AddSurfaceDependency(srcOps, context->surface);
@@ -170,12 +170,11 @@ void VKBlitLoops_Blit(JNIEnv *env,
// Need to validate render pass early, as image may not yet be configured.
AlphaType alphaType = getSrcAlphaType(srctype);
if (!VKRenderer_Validate(SHADER_BLIT, NO_SHADER_VARIANT, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, alphaType)) return;
if (!VKRenderer_Validate(SHADER_BLIT, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, alphaType)) return;
VKDevice* device = context->surface->device;
BlitSrcType type = decodeSrcType(device, srctype);
VKTexturePoolHandle* imageHandle =
VKTexturePool_GetTexture(VKRenderer_GetTexturePool(device->renderer), sw, sh, type.format);
VKTexturePoolHandle* imageHandle = VKTexturePool_GetTexture(device->texturePool, sw, sh, type.format);
VKImage* image = VKTexturePoolHandle_GetTexture(imageHandle);
VkDeviceSize dataSize = sh * sw * srcInfo.pixelStride;

View File

@@ -56,7 +56,7 @@ VKComposites VKComposites_Create() {
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT },
{ .logicOpEnable = VK_TRUE,
.logicOp = VK_LOGIC_OP_XOR }, ALPHA_TYPE_STRAIGHT });
.logicOp = VK_LOGIC_OP_XOR }, ALPHA_TYPE_PRE_MULTIPLIED });
// NAME | SRC_COLOR | DST_COLOR | SRC_ALPHA | DST_ALPHA ||
ALPHA_BLEND( CLEAR , ZERO , ZERO , ZERO , ZERO );
@@ -138,6 +138,7 @@ void VKComposites_AddState(VKComposites* composites, VKCompositeMode mode, VKCom
state.blendState.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
state.blendState.pNext = NULL;
state.blendState.attachmentCount = 1;
state.outAlphaType = ALPHA_TYPE_PRE_MULTIPLIED;
MAP_AT(composites->map, (VKCompositeDescriptor) { mode, VK_FALSE }) = state;
// Using pre-multiplied alpha is necessary for correct blending,

View File

@@ -272,6 +272,7 @@ void VKDevice_CheckAndAdd(VKEnv* vk, VkPhysicalDevice physicalDevice) {
void VKDevice_Reset(VKDevice* device) {
if (device == NULL) return;
VKRenderer_Destroy(device->renderer);
VKTexturePool_Dispose(device->texturePool);
VKAllocator_Destroy(device->allocator);
ARRAY_FREE(device->enabledExtensions);
ARRAY_FREE(device->enabledLayers);
@@ -391,4 +392,11 @@ Java_sun_java2d_vulkan_VKGPU_init(JNIEnv *env, jclass jClass, jlong jDevice) {
JNU_ThrowByName(env, "java/lang/RuntimeException", "Vulkan: Cannot create renderer");
return;
}
device->texturePool = VKTexturePool_InitWithDevice(device);
if (!device->texturePool) {
VKDevice_Reset(device);
JNU_ThrowByName(env, "java/lang/RuntimeException", "Vulkan: Cannot create texture pool");
return;
}
}

View File

@@ -59,6 +59,7 @@ struct VKDevice {
VKAllocator* allocator;
VKRenderer* renderer;
VKTexturePool* texturePool;
DEVICE_FUNCTION_TABLE(DECL_PFN)
SWAPCHAIN_DEVICE_FUNCTION_TABLE(DECL_PFN)

View File

@@ -49,7 +49,6 @@ static size_t pipelineDescriptorHash(const void* ptr) {
hash(&h, d->inAlphaType);
hash(&h, d->composite);
hash(&h, d->shader);
hash(&h, d->shaderVariant);
hash(&h, d->topology);
return (size_t) h;
}
@@ -60,7 +59,6 @@ static bool pipelineDescriptorEquals(const void* ap, const void* bp) {
a->inAlphaType == b->inAlphaType &&
a->composite == b->composite &&
a->shader == b->shader &&
a->shaderVariant == b->shaderVariant &&
a->topology == b->topology;
}
@@ -146,20 +144,12 @@ static VKPipelineInfo VKPipelines_CreatePipelines(VKRenderPassContext* renderPas
VkPipelineShaderStageCreateInfo createInfos[2]; // vert + frag
} ShaderStages;
ShaderStages stages[count];
typedef struct {
uint32_t inAlphaType, outAlphaType, shaderVariant, shaderModifier;
} SpecializationData;
const VkSpecializationMapEntry SPECIALIZATION_ENTRIES[] = {
{ 0, 0, 4 },
{ 1, 4, 4 },
{ 2, 8, 4 },
{ 3, 12, 4 }
};
typedef struct {
VkSpecializationInfo info;
SpecializationData data;
VkSpecializationMapEntry entries[2];
uint64_t data[1];
} Specialization;
Specialization specializations[count];
Specialization specializations[count][2];
VkPipelineInputAssemblyStateCreateInfo inputAssemblyStates[count];
VkPipelineDepthStencilStateCreateInfo depthStencilStates[count];
VkPipelineDynamicStateCreateInfo dynamicStates[count];
@@ -173,19 +163,14 @@ static VKPipelineInfo VKPipelines_CreatePipelines(VKRenderPassContext* renderPas
// - pStages (but stageCount is set to 2)
// - pVertexInputState
// - createInfo.layout
specializations[i] = (Specialization) {
.info = {
.mapEntryCount = SARRAY_COUNT_OF(SPECIALIZATION_ENTRIES),
.pMapEntries = SPECIALIZATION_ENTRIES,
.dataSize = sizeof(SpecializationData),
.pData = &specializations[i].data
},
.data = {
descriptors[i].inAlphaType, pipelineInfos[i].outAlphaType, (uint32_t) descriptors[i].shaderVariant,
(descriptors[i].composite == LOGIC_COMPOSITE_XOR ? 1 : 0) |
(descriptors[i].shader & SHADER_MASK ? 2 : 0)
}
};
for (uint32_t j = 0; j < SARRAY_COUNT_OF(specializations[i]); j++) {
specializations[i][j].info = (VkSpecializationInfo) {
.mapEntryCount = 0,
.pMapEntries = specializations[i][j].entries,
.dataSize = 0,
.pData = specializations[i][j].data
};
}
inputAssemblyStates[i] = (VkPipelineInputAssemblyStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = descriptors[i].topology
@@ -249,38 +234,37 @@ static VKPipelineInfo VKPipelines_CreatePipelines(VKRenderPassContext* renderPas
}
// Setup input states.
MAKE_INPUT_STATE(PRIMITIVE, VKVertex, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32_UINT);
MAKE_INPUT_STATE(MASK_FILL, VKMaskFillVertex, VK_FORMAT_R32G32B32A32_SINT, VK_FORMAT_R32_UINT);
MAKE_INPUT_STATE(COLOR, VKColorVertex, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32B32A32_SFLOAT);
MAKE_INPUT_STATE(MASK_FILL_COLOR, VKMaskFillColorVertex, VK_FORMAT_R32G32B32A32_SINT, VK_FORMAT_R32G32B32A32_SFLOAT);
MAKE_INPUT_STATE(BLIT, VKTxVertex, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32_SFLOAT);
MAKE_INPUT_STATE(CLIP, VKIntVertex, VK_FORMAT_R32G32_SINT);
for (uint32_t i = 0; i < count; i++) {
// Setup shader-specific pipeline parameters.
switch ((int) descriptors[i].shader) {
switch (descriptors[i].shader) {
case SHADER_COLOR:
createInfos[i].pVertexInputState = &INPUT_STATE_PRIMITIVE;
createInfos[i].layout = pipelineContext->commonPipelineLayout;
createInfos[i].pVertexInputState = &INPUT_STATE_COLOR;
createInfos[i].layout = pipelineContext->colorPipelineLayout;
stages[i] = (ShaderStages) {{ shaders->color_vert, shaders->color_frag }};
break;
case SHADER_COLOR | SHADER_MASK:
createInfos[i].pVertexInputState = &INPUT_STATE_MASK_FILL;
case SHADER_MASK_FILL_COLOR:
createInfos[i].pVertexInputState = &INPUT_STATE_MASK_FILL_COLOR;
createInfos[i].layout = pipelineContext->maskFillPipelineLayout;
stages[i] = (ShaderStages) {{ shaders->mask_fill_color_vert, shaders->mask_fill_color_frag }};
break;
case SHADER_GRADIENT:
createInfos[i].pVertexInputState = &INPUT_STATE_PRIMITIVE;
createInfos[i].layout = pipelineContext->maskFillPipelineLayout;
stages[i] = (ShaderStages) {{ shaders->primitive_vert, shaders->gradient_frag }};
break;
case SHADER_GRADIENT | SHADER_MASK:
createInfos[i].pVertexInputState = &INPUT_STATE_MASK_FILL;
createInfos[i].layout = pipelineContext->maskFillPipelineLayout;
stages[i] = (ShaderStages) {{ shaders->mask_fill_vert, shaders->gradient_frag }};
break;
case SHADER_BLIT:
createInfos[i].pVertexInputState = &INPUT_STATE_BLIT;
createInfos[i].layout = pipelineContext->texturePipelineLayout;
stages[i] = (ShaderStages) {{ shaders->blit_vert, shaders->blit_frag }};
// Alpha conversion specialization.
uint32_t* spec = (uint32_t*) specializations[i][1].data;
spec[0] = descriptors[i].inAlphaType;
spec[1] = pipelineInfos[i].outAlphaType;
specializations[i][1].info.dataSize = 8;
specializations[i][1].entries[0] = (VkSpecializationMapEntry) { 0, 0, 4 };
specializations[i][1].entries[1] = (VkSpecializationMapEntry) { 1, 4, 4 };
specializations[i][1].info.mapEntryCount = 2;
stages[i].createInfos[1].pSpecializationInfo = &specializations[i][1].info;
break;
case SHADER_CLIP:
createInfos[i].pVertexInputState = &INPUT_STATE_CLIP;
@@ -306,9 +290,6 @@ static VKPipelineInfo VKPipelines_CreatePipelines(VKRenderPassContext* renderPas
default:
VK_FATAL_ERROR("Cannot create pipeline, unknown shader requested!");
}
for (uint32_t j = 0; j < createInfos[i].stageCount; j++) {
stages[i].createInfos[j].pSpecializationInfo = &specializations[i].info;
}
assert(createInfos[i].pDynamicState->dynamicStateCount <= MAX_DYNAMIC_STATES);
J2dRlsTraceLn5(J2D_TRACE_INFO, "VKPipelines_CreatePipelines: stencilMode=%d, dstOpaque=%d, composite=%d, shader=%d, topology=%d",
descriptors[i].stencilMode, descriptors[i].dstOpaque, descriptors[i].composite, descriptors[i].shader, descriptors[i].topology);
@@ -322,7 +303,6 @@ static VKPipelineInfo VKPipelines_CreatePipelines(VKRenderPassContext* renderPas
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKPipelines_CreatePipelines: created %d pipelines", count);
for (uint32_t i = 0; i < count; i++) {
pipelineInfos[i].pipeline = pipelines[i];
pipelineInfos[i].layout = createInfos[i].layout;
MAP_AT(renderPassContext->pipelines, descriptors[i]) = pipelineInfos[i];
}
return pipelineInfos[0];
@@ -423,49 +403,21 @@ static VkResult VKPipelines_InitPipelineLayouts(VKDevice* device, VKPipelineCont
assert(device != NULL && pipelines != NULL);
VkResult result;
// We want all our pipelines to have the same push constant ranges to ensure a common state is compatible between pipelines.
VkPushConstantRange pushConstantRanges[] = {{
// We want all our pipelines to have same push constant range to ensure common state is compatible between pipelines.
VkPushConstantRange pushConstantRange = {
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.offset = 0,
.size = sizeof(VKTransform)
}, {
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
.offset = PUSH_CONSTANTS_OFFSET,
.size = PUSH_CONSTANTS_SIZE
}};
// Common pipeline.
};
VkPipelineLayoutCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 0,
.pushConstantRangeCount = SARRAY_COUNT_OF(pushConstantRanges),
.pPushConstantRanges = pushConstantRanges
.pushConstantRangeCount = 1,
.pPushConstantRanges = &pushConstantRange
};
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->commonPipelineLayout);
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->colorPipelineLayout);
VK_IF_ERROR(result) return result;
// Mask fill pipeline.
VkDescriptorSetLayoutBinding maskBufferLayoutBinding = {
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
.pImmutableSamplers = NULL
};
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = 1,
.pBindings = &maskBufferLayoutBinding
};
result = device->vkCreateDescriptorSetLayout(device->handle, &descriptorSetLayoutCreateInfo, NULL, &pipelines->maskFillDescriptorSetLayout);
VK_IF_ERROR(result) return result;
createInfo.setLayoutCount = 1;
createInfo.pSetLayouts = &pipelines->maskFillDescriptorSetLayout;
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->maskFillPipelineLayout);
VK_IF_ERROR(result) return result;
// Texture pipeline.
VkDescriptorSetLayoutBinding textureLayoutBinding = {
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
@@ -485,11 +437,31 @@ static VkResult VKPipelines_InitPipelineLayouts(VKDevice* device, VKPipelineCont
pipelines->textureDescriptorSetLayout,
pipelines->samplers.descriptorSetLayout
};
createInfo.setLayoutCount = SARRAY_COUNT_OF(textureDescriptorSetLayouts);
createInfo.setLayoutCount = 2;
createInfo.pSetLayouts = textureDescriptorSetLayouts;
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->texturePipelineLayout);
VK_IF_ERROR(result) return result;
VkDescriptorSetLayoutBinding maskBufferLayoutBinding = {
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
.pImmutableSamplers = NULL
};
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = 1,
.pBindings = &maskBufferLayoutBinding
};
result = device->vkCreateDescriptorSetLayout(device->handle, &descriptorSetLayoutCreateInfo, NULL, &pipelines->maskFillDescriptorSetLayout);
VK_IF_ERROR(result) return result;
createInfo.setLayoutCount = 1;
createInfo.pSetLayouts = &pipelines->maskFillDescriptorSetLayout;
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->maskFillPipelineLayout);
VK_IF_ERROR(result) return result;
return VK_SUCCESS;
}
@@ -532,7 +504,7 @@ void VKPipelines_DestroyContext(VKPipelineContext* pipelineContext) {
VKPipelines_DestroyShaders(device, pipelineContext->shaders);
device->vkDestroyPipelineLayout(device->handle, pipelineContext->commonPipelineLayout, NULL);
device->vkDestroyPipelineLayout(device->handle, pipelineContext->colorPipelineLayout, NULL);
device->vkDestroyPipelineLayout(device->handle, pipelineContext->texturePipelineLayout, NULL);
device->vkDestroyDescriptorSetLayout(device->handle, pipelineContext->textureDescriptorSetLayout, NULL);
device->vkDestroyPipelineLayout(device->handle, pipelineContext->maskFillPipelineLayout, NULL);

View File

@@ -36,26 +36,13 @@
* Shader programs.
*/
typedef enum {
// Base shaders.
SHADER_COLOR,
SHADER_GRADIENT,
SHADER_MASK_FILL_COLOR,
SHADER_BLIT,
SHADER_CLIP,
NO_SHADER = 0x7FFFFFFF,
// Mask modifier bit (MASK_FILL & MASK_BLIT).
SHADER_MASK = ~NO_SHADER
NO_SHADER = 0x7FFFFFFF
} VKShader;
/**
* Shader variant.
* It is used to specialize shader behavior, and its meaning varies with each particular shader.
*/
typedef enum {
SHADER_VARIANT_GRADIENT_CLAMP = 0,
SHADER_VARIANT_GRADIENT_CYCLE = 1,
NO_SHADER_VARIANT = 0x7FFFFFFF
} VKShaderVariant;
typedef enum {
STENCIL_MODE_NONE = 0, // No stencil attachment.
STENCIL_MODE_OFF = 1, // Has stencil attachment, stencil test disabled.
@@ -72,14 +59,12 @@ typedef struct {
AlphaType inAlphaType : 1;
VKCompositeMode composite;
VKShader shader;
VKShaderVariant shaderVariant;
VkPrimitiveTopology topology;
} VKPipelineDescriptor;
typedef struct {
VkPipeline pipeline;
VkPipelineLayout layout;
AlphaType outAlphaType;
VkPipeline pipeline;
AlphaType outAlphaType;
} VKPipelineInfo;
/**
@@ -87,7 +72,7 @@ typedef struct {
*/
struct VKPipelineContext {
VKDevice* device;
VkPipelineLayout commonPipelineLayout;
VkPipelineLayout colorPipelineLayout;
VkDescriptorSetLayout textureDescriptorSetLayout;
VkPipelineLayout texturePipelineLayout;
VkDescriptorSetLayout maskFillDescriptorSetLayout;
@@ -108,42 +93,14 @@ struct VKRenderPassContext {
MAP(VKPipelineDescriptor, VKPipelineInfo) pipelines;
};
typedef struct {
unsigned int xorColor;
float extraAlpha;
} VKCompositeConstants;
typedef struct {
RGBA c0, c1;
float p0, p1, p3;
} VKGradientPaintConstants;
typedef union {
// The minimum guaranteed size of push constants is 128 bytes.
alignas(32) // The maximum alignment for built-in glsl types is 32 bytes (dvec4).
char data[(128 - sizeof(VKTransform) - sizeof(VKCompositeConstants)) / 32 * 32];
VKGradientPaintConstants gradientPaint;
} VKShaderConstants;
#define MAX_SHADER_CONSTANTS_SIZE 96 // We expect 96 bytes
typedef char VKShaderConstantsCheckOffset[sizeof(VKShaderConstants) == MAX_SHADER_CONSTANTS_SIZE ? 1 : -1]; // Verify.
typedef struct {
VKTransform transform;
VKCompositeConstants composite;
VKShaderConstants shader;
} VKPushConstants;
typedef char VKPushConstantsCheckSize[sizeof(VKPushConstants) <= 128 ? 1 : -1]; // We should not exceed 128 bytes.
static const uint32_t PUSH_CONSTANTS_OFFSET = (uintptr_t) &((VKPushConstants*) NULL)->composite;
static const uint32_t PUSH_CONSTANTS_SIZE = sizeof(VKPushConstants) - PUSH_CONSTANTS_OFFSET;
typedef struct {
int x, y;
} VKIntVertex;
typedef struct {
float x, y;
unsigned int data;
} VKVertex;
RGBA color;
} VKColorVertex;
typedef struct {
float px, py;
@@ -152,8 +109,8 @@ typedef struct {
typedef struct {
int x, y, maskOffset, maskScanline;
unsigned int data;
} VKMaskFillVertex;
RGBA color;
} VKMaskFillColorVertex;
VKPipelineContext* VKPipelines_CreateContext(VKDevice* device);
void VKPipelines_DestroyContext(VKPipelineContext* pipelines);

View File

@@ -93,21 +93,6 @@
#define OFFSET_XFORM sun_java2d_vulkan_VKBlitLoops_OFFSET_XFORM
#define OFFSET_ISOBLIT sun_java2d_vulkan_VKBlitLoops_OFFSET_ISOBLIT
static void applyXor() {
if (VKRenderer_GetContext()->shader == SHADER_COLOR) {
VKRenderer_GetContext()->vertexData ^= VKRenderer_GetContext()->constants.composite.xorColor;
}
}
static void setComposite(VKCompositeMode comp, unsigned int xorColor, float extraAlpha) {
VKRenderer_GetContext()->composite = comp;
if (VKRenderer_GetContext()->constants.composite.xorColor != xorColor ||
VKRenderer_GetContext()->constants.composite.extraAlpha != extraAlpha) {
VKRenderer_GetContext()->constants.composite = (VKCompositeConstants) { xorColor, extraAlpha };
VKRenderer_GetContext()->constantsModCount++;
}
}
JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
(JNIEnv *env, jobject oglrq, jlong buf, jint limit)
{
@@ -496,26 +481,30 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jint flags = NEXT_INT(b);
J2dRlsTraceLn3(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_ALPHA_COMPOSITE(%d, %f, %d)", rule, extraAlpha, flags);
applyXor();
setComposite((VKCompositeMode) rule, 0, extraAlpha);
VKRenderer_GetContext()->renderColor = VKRenderer_GetContext()->color;
VKRenderer_GetContext()->composite = (VKCompositeMode) rule;
VKRenderer_GetContext()->extraAlpha = extraAlpha;
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_XOR_COMPOSITE:
{
jint xorPixel = NEXT_INT(b);
J2dRlsTraceLn1(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_XOR_COMPOSITE(0x%08x)", xorPixel);
applyXor();
setComposite(LOGIC_COMPOSITE_XOR, xorPixel, -1.0f);
applyXor();
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_XOR_COMPOSITE");
VKRenderer_GetContext()->renderColor = VKUtil_DecodeJavaColor(xorPixel, ALPHA_TYPE_STRAIGHT);
// TODO Fix XOR mode!
// VKRenderer_GetContext()->renderColor.a = 0.0f; // Alpha is left unchanged in XOR mode.
VKRenderer_GetContext()->composite = LOGIC_COMPOSITE_XOR;
VKRenderer_GetContext()->extraAlpha = 1.0f;
}
break;
case sun_java2d_pipe_BufferedOpCodes_RESET_COMPOSITE:
{
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: RESET_COMPOSITE");
applyXor();
setComposite(ALPHA_COMPOSITE_SRC, 0, 1.0f);
VKRenderer_GetContext()->renderColor = VKRenderer_GetContext()->color;
VKRenderer_GetContext()->composite = ALPHA_COMPOSITE_SRC;
VKRenderer_GetContext()->extraAlpha = 1.0f;
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_TRANSFORM:
@@ -538,8 +527,8 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
};
VKRenderingContext* context = VKRenderer_GetContext();
if (VK_IS_NEQ_TRANSFORM(&context->constants.transform, &transform)) {
context->constants.transform = transform;
if (VK_IS_NEQ_TRANSFORM(&context->transform, &transform)) {
context->transform = transform;
context->transformModCount++;
}
}
@@ -549,8 +538,8 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: RESET_TRANSFORM");
VKRenderingContext* context = VKRenderer_GetContext();
if (VK_IS_NEQ_TRANSFORM(&context->constants.transform, &VK_ID_TRANSFORM)) {
context->constants.transform = VK_ID_TRANSFORM;
if (VK_IS_NEQ_TRANSFORM(&context->transform, &VK_ID_TRANSFORM)) {
context->transform = VK_ID_TRANSFORM;
context->transformModCount++;
}
}
@@ -584,10 +573,9 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
break;
case sun_java2d_pipe_BufferedOpCodes_DISPOSE_SURFACE:
{
VKSDOps* surface = NEXT_VK_SURFACE(b);
jlong pData = NEXT_LONG(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: DISPOSE_SURFACE");
VKSD_ResetSurface(surface);
}
break;
case sun_java2d_pipe_BufferedOpCodes_DISPOSE_CONFIG:
@@ -660,34 +648,31 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
break;
case sun_java2d_pipe_BufferedOpCodes_SET_COLOR:
{
VKRenderer_GetContext()->inAlphaType = ALPHA_TYPE_STRAIGHT;
VKRenderer_GetContext()->shader = SHADER_COLOR;
VKRenderer_GetContext()->shaderVariant = NO_SHADER_VARIANT;
VKRenderer_GetContext()->vertexData = NEXT_INT(b);
applyXor();
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: SET_COLOR(0x%08x)", VKRenderer_GetContext()->vertexData);
jint javaColor = NEXT_INT(b);
VKRenderer_GetContext()->color = VKUtil_DecodeJavaColor(javaColor, ALPHA_TYPE_STRAIGHT);
if (COMPOSITE_GROUP(VKRenderer_GetContext()->composite) == ALPHA_COMPOSITE_GROUP) {
VKRenderer_GetContext()->renderColor = VKRenderer_GetContext()->color;
}
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: SET_COLOR(0x%08x)", javaColor);
J2dTraceLn4(J2D_TRACE_VERBOSE, // Print color values with straight alpha for convenience.
" srgb={%.3f, %.3f, %.3f, %.3f}",
VKUtil_GetRGBA(VKRenderer_GetContext()->color, ALPHA_TYPE_STRAIGHT).r,
VKUtil_GetRGBA(VKRenderer_GetContext()->color, ALPHA_TYPE_STRAIGHT).g,
VKUtil_GetRGBA(VKRenderer_GetContext()->color, ALPHA_TYPE_STRAIGHT).b,
VKUtil_GetRGBA(VKRenderer_GetContext()->color, ALPHA_TYPE_STRAIGHT).a);
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_GRADIENT_PAINT:
{
jboolean useMask = NEXT_BOOLEAN(b); // Unused.
jboolean cyclic = NEXT_BOOLEAN(b);
jdouble p0 = NEXT_DOUBLE(b);
jdouble p1 = NEXT_DOUBLE(b);
jdouble p3 = NEXT_DOUBLE(b);
jint pixel1 = NEXT_INT(b);
jint pixel2 = NEXT_INT(b);
jboolean useMask= NEXT_BOOLEAN(b);
jboolean cyclic = NEXT_BOOLEAN(b);
jdouble p0 = NEXT_DOUBLE(b);
jdouble p1 = NEXT_DOUBLE(b);
jdouble p3 = NEXT_DOUBLE(b);
jint pixel1 = NEXT_INT(b);
jint pixel2 = NEXT_INT(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_GRADIENT_PAINT");
VKRenderer_GetContext()->inAlphaType = ALPHA_TYPE_PRE_MULTIPLIED;
VKRenderer_GetContext()->shader = SHADER_GRADIENT;
VKRenderer_GetContext()->shaderVariant = cyclic ? SHADER_VARIANT_GRADIENT_CYCLE : SHADER_VARIANT_GRADIENT_CLAMP;
VKRenderer_GetContext()->constants.shader.gradientPaint = (VKGradientPaintConstants) {
VKUtil_GetRGBA(VKUtil_DecodeJavaColor(pixel1, ALPHA_TYPE_PRE_MULTIPLIED), ALPHA_TYPE_PRE_MULTIPLIED),
VKUtil_GetRGBA(VKUtil_DecodeJavaColor(pixel2, ALPHA_TYPE_PRE_MULTIPLIED), ALPHA_TYPE_PRE_MULTIPLIED),
p0*2.0, p1*2.0, p3*2.0-0.5
};
VKRenderer_GetContext()->constantsModCount++;
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_LINEAR_GRADIENT_PAINT:

View File

@@ -94,7 +94,6 @@ typedef struct {
struct VKRenderer {
VKDevice* device;
VKPipelineContext* pipelineContext;
VKTexturePool* texturePool;
POOL(VkCommandBuffer, commandBufferPool);
POOL(VkCommandBuffer, secondaryCommandBufferPool);
@@ -164,7 +163,6 @@ struct VKRenderPass {
BufferWritingState maskFillBufferWriting;
VKPipelineDescriptor state;
uint64_t constantsModCount; // Just a tag to detect when constants were changed.
uint64_t transformModCount; // Just a tag to detect when transform was changed.
uint64_t clipModCount; // Just a tag to detect when clip was changed.
VkBool32 pendingFlush : 1;
@@ -177,17 +175,12 @@ struct VKRenderPass {
// which is only called from queue flusher thread, no need for synchronization.
static VKRenderingContext context = {
.surface = NULL,
.composite = ALPHA_COMPOSITE_SRC_OVER,
.inAlphaType = ALPHA_TYPE_UNKNOWN,
.shader = NO_SHADER,
.shaderVariant = NO_SHADER_VARIANT,
.vertexData = 0,
.constantsModCount = 1,
.transform = VK_ID_TRANSFORM,
.transformModCount = 1,
.constants = {
.transform = VK_ID_TRANSFORM,
.composite = { 0, 1.0f }
},
.color = {},
.renderColor = {},
.composite = ALPHA_COMPOSITE_SRC_OVER,
.extraAlpha = 1.0f,
.clipModCount = 1,
.clipRect = NO_CLIP,
.clipSpanVertices = NULL
@@ -200,9 +193,6 @@ VKRenderingContext *VKRenderer_GetContext() {
return &context;
}
VKTexturePool *VKRenderer_GetTexturePool(VKRenderer* renderer) {
return renderer->texturePool;
}
/**
* Helper function for POOL_TAKE macro.
*/
@@ -227,7 +217,7 @@ static VkBool32 VKRenderer_CheckPoolDrain(void* pool, void* entry) {
return VK_FALSE;
}
#define VERTEX_BUFFER_SIZE (128 * 1024) // 128KiB - enough to draw 1820 quads (6 verts) with VKVertex.
#define VERTEX_BUFFER_SIZE (128 * 1024) // 128KiB - enough to draw 910 quads (6 verts) with VKColorVertex.
#define VERTEX_BUFFER_PAGE_SIZE (1 * 1024 * 1024) // 1MiB - fits 8 buffers.
static void VKRenderer_FindVertexBufferMemoryType(VKMemoryRequirements* requirements) {
VKAllocator_FindMemoryType(requirements, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT,
@@ -378,12 +368,6 @@ VKRenderer* VKRenderer_Create(VKDevice* device) {
return NULL;
}
renderer->texturePool = VKTexturePool_InitWithDevice(device);
if (!renderer->texturePool) {
VKRenderer_Destroy(renderer);
return NULL;
}
// Create command pool
// TODO we currently have single command pool with VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT
// we may need to consider having multiple pools to avoid resetting buffers one-by-one
@@ -454,9 +438,6 @@ void VKRenderer_Destroy(VKRenderer* renderer) {
device->vkDestroyDescriptorPool(device->handle, renderer->descriptorPools[i], NULL);
}
ARRAY_FREE(renderer->descriptorPools);
VKTexturePool_Dispose(renderer->texturePool);
for (uint32_t i = 0; i < ARRAY_SIZE(renderer->imageDescriptorPools); i++) {
device->vkDestroyDescriptorPool(device->handle, renderer->imageDescriptorPools[i], NULL);
}
@@ -603,47 +584,33 @@ inline void VKRenderer_FlushDraw(VKSDOps* surface) {
static void VKRenderer_ResetDrawing(VKSDOps* surface) {
assert(surface != NULL && surface->renderPass != NULL);
VKRenderer* renderer = surface->device->renderer;
VKRenderPass* renderPass = surface->renderPass;
renderPass->state.composite = NO_COMPOSITE;
renderPass->state.shader = NO_SHADER;
renderPass->constantsModCount = 0;
renderPass->transformModCount = 0;
renderPass->firstVertex = 0;
renderPass->vertexCount = 0;
renderPass->vertexBufferWriting = (BufferWritingState) { NULL, 0, VK_FALSE };
renderPass->maskFillBufferWriting = (BufferWritingState) { NULL, 0, VK_FALSE };
if (ARRAY_SIZE(renderPass->flushRanges) > 0) {
surface->renderPass->state.composite = NO_COMPOSITE;
surface->renderPass->state.shader = NO_SHADER;
surface->renderPass->transformModCount = 0;
surface->renderPass->firstVertex = 0;
surface->renderPass->vertexCount = 0;
surface->renderPass->vertexBufferWriting = (BufferWritingState) {NULL, 0, VK_FALSE};
surface->renderPass->maskFillBufferWriting = (BufferWritingState) {NULL, 0, VK_FALSE};
if (ARRAY_SIZE(surface->renderPass->flushRanges) > 0) {
VK_IF_ERROR(surface->device->vkFlushMappedMemoryRanges(surface->device->handle,
ARRAY_SIZE(renderPass->flushRanges), renderPass->flushRanges)) {}
ARRAY_RESIZE(renderPass->flushRanges, 0);
ARRAY_SIZE(surface->renderPass->flushRanges), surface->renderPass->flushRanges)) {}
ARRAY_RESIZE(surface->renderPass->flushRanges, 0);
}
size_t vertexBufferCount = ARRAY_SIZE(renderPass->vertexBuffers);
size_t maskFillBufferCount = ARRAY_SIZE(renderPass->maskFillBuffers);
size_t cleanupQueueCount = ARRAY_SIZE(renderPass->cleanupQueue);
size_t vertexBufferCount = ARRAY_SIZE(surface->renderPass->vertexBuffers);
size_t maskFillBufferCount = ARRAY_SIZE(surface->renderPass->maskFillBuffers);
size_t cleanupQueueCount = ARRAY_SIZE(surface->renderPass->cleanupQueue);
for (uint32_t i = 0; i < vertexBufferCount; i++) {
POOL_RETURN(renderer, vertexBufferPool, renderPass->vertexBuffers[i]);
POOL_RETURN(surface->device->renderer, vertexBufferPool, surface->renderPass->vertexBuffers[i]);
}
for (uint32_t i = 0; i < maskFillBufferCount; i++) {
POOL_RETURN(renderer, maskFillBufferPool, renderPass->maskFillBuffers[i]);
POOL_RETURN(surface->device->renderer, maskFillBufferPool, surface->renderPass->maskFillBuffers[i]);
}
for (uint32_t i = 0; i < cleanupQueueCount; i++) {
POOL_RETURN(renderer, cleanupQueue, renderPass->cleanupQueue[i]);
POOL_RETURN(surface->device->renderer, cleanupQueue, surface->renderPass->cleanupQueue[i]);
}
ARRAY_RESIZE(renderPass->vertexBuffers, 0);
ARRAY_RESIZE(renderPass->maskFillBuffers, 0);
ARRAY_RESIZE(renderPass->cleanupQueue, 0);
// Update dependencies on used surfaces.
for (uint32_t i = 0, surfaces = (uint32_t) ARRAY_SIZE(renderPass->usedSurfaces); i < surfaces; i++) {
VKSDOps* usedSurface = renderPass->usedSurfaces[i];
uint32_t newSize = 0, oldSize = (uint32_t) ARRAY_SIZE(usedSurface->dependentSurfaces);
for (uint32_t j = 0; j < oldSize; j++) {
VKSDOps* s = usedSurface->dependentSurfaces[j];
if (s != surface) usedSurface->dependentSurfaces[newSize++] = s;
}
if (newSize != oldSize) ARRAY_RESIZE(usedSurface->dependentSurfaces, newSize);
}
ARRAY_RESIZE(renderPass->usedSurfaces, 0);
ARRAY_RESIZE(surface->renderPass->vertexBuffers, 0);
ARRAY_RESIZE(surface->renderPass->maskFillBuffers, 0);
ARRAY_RESIZE(surface->renderPass->cleanupQueue, 0);
}
/**
@@ -731,7 +698,6 @@ static VkBool32 VKRenderer_InitRenderPass(VKSDOps* surface) {
.composite = NO_COMPOSITE,
.shader = NO_SHADER
},
.constantsModCount = 0,
.transformModCount = 0,
.clipModCount = 0,
.pendingFlush = VK_FALSE,
@@ -898,11 +864,19 @@ VkBool32 VKRenderer_FlushRenderPass(VKSDOps* surface) {
VKRenderer* renderer = device->renderer;
VkCommandBuffer cb = VKRenderer_Record(renderer);
// Update timestamps on used surfaces.
// Update dependencies on used surfaces.
surface->lastTimestamp = renderer->writeTimestamp;
for (uint32_t i = 0, surfaces = (uint32_t) ARRAY_SIZE(surface->renderPass->usedSurfaces); i < surfaces; i++) {
surface->renderPass->usedSurfaces[i]->lastTimestamp = renderer->writeTimestamp;
VKSDOps* usedSurface = surface->renderPass->usedSurfaces[i];
usedSurface->lastTimestamp = renderer->writeTimestamp;
uint32_t newSize = 0, oldSize = (uint32_t) ARRAY_SIZE(usedSurface->dependentSurfaces);
for (uint32_t j = 0; j < oldSize; j++) {
VKSDOps* s = usedSurface->dependentSurfaces[j];
if (s != surface) usedSurface->dependentSurfaces[newSize++] = s;
}
if (newSize != oldSize) ARRAY_RESIZE(usedSurface->dependentSurfaces, newSize);
}
ARRAY_RESIZE(surface->renderPass->usedSurfaces, 0);
// Insert barriers to prepare surface for rendering.
VkImageMemoryBarrier barriers[2];
@@ -1135,7 +1109,7 @@ static uint32_t VKRenderer_AllocateVertices(uint32_t primitives, uint32_t vertic
* This function can invalidate drawing state, always call it before VK_DRAW.
*/
static BufferWritingState VKRenderer_AllocateMaskFillBytes(uint32_t size) {
// assert(size > 0); // size can be 0 when binding the buffer without allocating any data.
assert(size > 0);
assert(size <= MASK_FILL_BUFFER_SIZE);
VKSDOps* surface = VKRenderer_GetContext()->surface;
BufferWritingState state = VKRenderer_AllocateBufferData(
@@ -1171,35 +1145,16 @@ static void VKRenderer_ValidateTransform() {
0.0f, 2.0f / (float) surface->image->extent.height, -1.0f
};
// Combine it with user transform.
VKUtil_ConcatenateTransform(&transform, &context->constants.transform);
VKUtil_ConcatenateTransform(&transform, &context->transform);
// Push the transform into shader.
surface->device->vkCmdPushConstants(
renderPass->commandBuffer,
surface->device->renderer->pipelineContext->commonPipelineLayout,
surface->device->renderer->pipelineContext->colorPipelineLayout, // TODO what if our pipeline layout differs?
VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(VKTransform), &transform
);
}
}
static void VKRenderer_ValidateConstants() {
VKRenderingContext* context = VKRenderer_GetContext();
assert(context->surface != NULL);
VKSDOps* surface = context->surface;
VKRenderPass* renderPass = surface->renderPass;
// Update constants, ignoring clip and color shaders.
if (renderPass->constantsModCount != context->constantsModCount &&
renderPass->state.shader != SHADER_CLIP && renderPass->state.shader != SHADER_COLOR) {
J2dTraceLn(J2D_TRACE_VERBOSE, "VKRenderer_ValidateConstants: updating constants");
VKRenderer_FlushDraw(surface);
renderPass->constantsModCount = context->constantsModCount;
surface->device->vkCmdPushConstants(
renderPass->commandBuffer,
surface->device->renderer->pipelineContext->commonPipelineLayout,
VK_SHADER_STAGE_FRAGMENT_BIT, PUSH_CONSTANTS_OFFSET, PUSH_CONSTANTS_SIZE, &context->constants.composite
);
}
}
/**
* Setup stencil attachment according to the context clip state.
* If there is a clip shape, attachment is cleared with "fail" value and then
@@ -1236,7 +1191,6 @@ static void VKRenderer_SetupStencil() {
.inAlphaType = ALPHA_TYPE_UNKNOWN,
.composite = NO_COMPOSITE,
.shader = SHADER_CLIP,
.shaderVariant = NO_SHADER_VARIANT,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
}).pipeline);
// Reset vertex buffer binding.
@@ -1280,7 +1234,7 @@ void VKRenderer_RecordBarriers(VKRenderer* renderer,
/**
* Setup pipeline for drawing. Returns FALSE if surface is not yet ready for drawing.
*/
VkBool32 VKRenderer_Validate(VKShader shader, VKShaderVariant shaderVariant, VkPrimitiveTopology topology, AlphaType inAlphaType) {
VkBool32 VKRenderer_Validate(VKShader shader, VkPrimitiveTopology topology, AlphaType inAlphaType) {
assert(context.surface != NULL);
VKSDOps* surface = context.surface;
@@ -1322,18 +1276,10 @@ VkBool32 VKRenderer_Validate(VKShader shader, VKShaderVariant shaderVariant, VkP
J2dTraceLn(J2D_TRACE_VERBOSE, "VKRenderer_Validate: updating clip");
surface->device->vkCmdSetScissor(renderPass->commandBuffer, 0, 1, &context.clipRect);
if (clipChanged) {
VKStencilMode stencilMode = STENCIL_MODE_NONE;
if (ARRAY_SIZE(context.clipSpanVertices) > 0) {
VKRenderer_SetupStencil();
stencilMode = STENCIL_MODE_ON;
} else if (surface->stencil != NULL) {
stencilMode = STENCIL_MODE_OFF;
}
// Reset the pipeline when changing stencil mode.
if (renderPass->state.stencilMode != stencilMode) {
renderPass->state.shader = NO_SHADER;
}
renderPass->state.stencilMode = stencilMode;
renderPass->state.stencilMode = STENCIL_MODE_ON;
} else renderPass->state.stencilMode = surface->stencil != NULL ? STENCIL_MODE_OFF : STENCIL_MODE_NONE;
}
}
// Validate current composite.
@@ -1349,7 +1295,6 @@ VkBool32 VKRenderer_Validate(VKShader shader, VKShaderVariant shaderVariant, VkP
// Validate current pipeline.
if (renderPass->state.shader != shader ||
renderPass->state.shaderVariant != shaderVariant ||
renderPass->state.topology != topology ||
renderPass->state.inAlphaType != inAlphaType) {
J2dTraceLn2(J2D_TRACE_VERBOSE, "VKRenderer_Validate: updating pipeline, old=%d, new=%d",
@@ -1357,7 +1302,6 @@ VkBool32 VKRenderer_Validate(VKShader shader, VKShaderVariant shaderVariant, VkP
VKRenderer_FlushDraw(surface);
VkCommandBuffer cb = renderPass->commandBuffer;
renderPass->state.shader = shader;
renderPass->state.shaderVariant = shaderVariant;
renderPass->state.topology = topology;
renderPass->state.inAlphaType = inAlphaType;
VKPipelineInfo pipelineInfo = VKPipelines_GetPipelineInfo(renderPass->context, renderPass->state);
@@ -1365,26 +1309,10 @@ VkBool32 VKRenderer_Validate(VKShader shader, VKShaderVariant shaderVariant, VkP
surface->device->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineInfo.pipeline);
renderPass->vertexBufferWriting.bound = VK_FALSE;
renderPass->maskFillBufferWriting.bound = VK_FALSE;
// If pipeline uses mask fill layout, but the shader is not actually a MASK one, that must be a generic-layout pipeline.
// In that case, we need to bind a mask buffer, even if we won't ever use it.
// We could use VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT or nullDescriptor, but those require
// optional features or extensions, so don't bother for now...
// TODO this is ugly, do something with it.
if (pipelineInfo.layout == surface->device->renderer->pipelineContext->maskFillPipelineLayout && !(shader & SHADER_MASK)) {
VKRenderer_AllocateMaskFillBytes(0);
}
}
VKRenderer_ValidateConstants();
return VK_TRUE;
}
static VkBool32 VKRenderer_ValidatePaint(VKShader shaderModifier, VkBool32 fill) {
return VKRenderer_Validate(context.shader | shaderModifier, context.shaderVariant,
fill ? VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST : VK_PRIMITIVE_TOPOLOGY_LINE_LIST, context.inAlphaType);
}
// Drawing operations.
void VKRenderer_RenderRect(VkBool32 fill, jint x, jint y, jint w, jint h) {
@@ -1395,9 +1323,12 @@ void VKRenderer_RenderRect(VkBool32 fill, jint x, jint y, jint w, jint h) {
void VKRenderer_RenderParallelogram(VkBool32 fill,
jfloat x11, jfloat y11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12) {
if (!VKRenderer_ValidatePaint(0, fill)) return; // Not ready.
jfloat dx12, jfloat dy12)
{
if (!VKRenderer_Validate(SHADER_COLOR,
fill ? VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
: VK_PRIMITIVE_TOPOLOGY_LINE_LIST, ALPHA_TYPE_UNKNOWN)) return; // Not ready.
RGBA c = VKRenderer_GetRGBA(context.surface, context.renderColor);
/* dx21
* (p1)---------(p2) | (p1)------
* |\ \ | | \ dy21
@@ -1408,12 +1339,12 @@ void VKRenderer_RenderParallelogram(VkBool32 fill,
* dy21 \ |
* -----(p3)
*/
VKVertex p1 = {x11, y11, context.vertexData};
VKVertex p2 = {x11 + dx21, y11 + dy21, context.vertexData};
VKVertex p3 = {x11 + dx21 + dx12, y11 + dy21 + dy12, context.vertexData};
VKVertex p4 = {x11 + dx12, y11 + dy12, context.vertexData};
VKColorVertex p1 = {x11, y11, c};
VKColorVertex p2 = {x11 + dx21, y11 + dy21, c};
VKColorVertex p3 = {x11 + dx21 + dx12, y11 + dy21 + dy12, c};
VKColorVertex p4 = {x11 + dx12, y11 + dy12, c};
VKVertex* vs;
VKColorVertex* vs;
VK_DRAW(vs, 1, fill ? 6 : 8);
uint32_t i = 0;
vs[i++] = p1;
@@ -1430,18 +1361,19 @@ void VKRenderer_RenderParallelogram(VkBool32 fill,
void VKRenderer_FillSpans(jint spanCount, jint *spans) {
if (spanCount == 0) return;
if (!VKRenderer_ValidatePaint(0, VK_TRUE)) return; // Not ready.
if (!VKRenderer_Validate(SHADER_COLOR, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, ALPHA_TYPE_UNKNOWN)) return; // Not ready.
RGBA c = VKRenderer_GetRGBA(context.surface, context.renderColor);
jfloat x1 = (float)*(spans++);
jfloat y1 = (float)*(spans++);
jfloat x2 = (float)*(spans++);
jfloat y2 = (float)*(spans++);
VKVertex p1 = {x1, y1, context.vertexData};
VKVertex p2 = {x2, y1, context.vertexData};
VKVertex p3 = {x2, y2, context.vertexData};
VKVertex p4 = {x1, y2, context.vertexData};
VKColorVertex p1 = {x1, y1, c};
VKColorVertex p2 = {x2, y1, c};
VKColorVertex p3 = {x2, y2, c};
VKColorVertex p4 = {x1, y2, c};
VKVertex* vs;
VKColorVertex* vs;
VK_DRAW(vs, 1, 6);
vs[0] = p1; vs[1] = p2; vs[2] = p3; vs[3] = p3; vs[4] = p4; vs[5] = p1;
@@ -1458,8 +1390,8 @@ void VKRenderer_FillSpans(jint spanCount, jint *spans) {
void VKRenderer_MaskFill(jint x, jint y, jint w, jint h,
jint maskoff, jint maskscan, jint masklen, uint8_t *mask) {
if (!VKRenderer_ValidatePaint(SHADER_MASK, VK_TRUE)) return; // Not ready.
if (!VKRenderer_Validate(SHADER_MASK_FILL_COLOR,
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, ALPHA_TYPE_UNKNOWN)) return; // Not ready.
// maskoff is the offset from the beginning of mask,
// it's the same as x and y offset within a tile (maskoff % maskscan, maskoff / maskscan).
// maskscan is the number of bytes in a row/
@@ -1477,13 +1409,14 @@ void VKRenderer_MaskFill(jint x, jint y, jint w, jint h,
*((char *)maskState.data) = (char)0xFF;
}
VKMaskFillVertex* vs;
VKMaskFillColorVertex* vs;
VK_DRAW(vs, 1, 6);
RGBA c = VKRenderer_GetRGBA(context.surface, context.renderColor);
int offset = (int) maskState.offset;
VKMaskFillVertex p1 = {x, y, offset, maskscan, context.vertexData};
VKMaskFillVertex p2 = {x + w, y, offset, maskscan, context.vertexData};
VKMaskFillVertex p3 = {x + w, y + h, offset, maskscan, context.vertexData};
VKMaskFillVertex p4 = {x, y + h, offset, maskscan, context.vertexData};
VKMaskFillColorVertex p1 = {x, y, offset, maskscan, c};
VKMaskFillColorVertex p2 = {x + w, y, offset, maskscan, c};
VKMaskFillColorVertex p3 = {x + w, y + h, offset, maskscan, c};
VKMaskFillColorVertex p4 = {x, y + h, offset, maskscan, c};
// Always keep p1 as provoking vertex for correct origin calculation in vertex shader.
vs[0] = p1; vs[1] = p3; vs[2] = p2;
vs[3] = p1; vs[4] = p3; vs[5] = p4;

View File

@@ -29,22 +29,27 @@
#include "VKTypes.h"
#include "VKPipelines.h"
#include "VKTexturePool.h"
#define NO_CLIP ((VkRect2D) {{0, 0}, {0x7FFFFFFFU, 0x7FFFFFFFU}})
struct VKRenderingContext {
VKSDOps* surface;
VKCompositeMode composite;
AlphaType inAlphaType;
VKShader shader;
VKShaderVariant shaderVariant;
unsigned int vertexData;
VKPushConstants constants;
uint64_t constantsModCount;
VKTransform transform;
uint64_t transformModCount;
// We keep this color separately from renderColor,
// because we need consistent state when switching between XOR and alpha
// composite modes. This variable holds last value set by SET_COLOR, while
// renderColor holds color, currently used for drawing, which may have
// also been provided by SET_XOR_COMPOSITE.
Color color;
Color renderColor;
VKCompositeMode composite;
// Extra alpha is not used when painting with plain color,
// in this case color.a already includes it.
float extraAlpha;
uint64_t clipModCount; // Used to track changes to the clip.
VkRect2D clipRect;
ARRAY(VKIntVertex) clipSpanVertices;
@@ -57,7 +62,7 @@ VKRenderer* VKRenderer_Create(VKDevice* device);
/**
* Setup pipeline for drawing. Returns FALSE if the surface is not yet ready for drawing.
*/
VkBool32 VKRenderer_Validate(VKShader shader, VKShaderVariant shaderVariant, VkPrimitiveTopology topology, AlphaType inAlphaType);
VkBool32 VKRenderer_Validate(VKShader shader, VkPrimitiveTopology topology, AlphaType inAlphaType);
/**
* Record commands into the primary command buffer (outside of a render pass).
@@ -142,6 +147,5 @@ void VKRenderer_DrawImage(VKImage* image, VkFormat format,
float dx1, float dy1, float dx2, float dy2);
VKRenderingContext* VKRenderer_GetContext();
VKTexturePool* VKRenderer_GetTexturePool(VKRenderer* );
#endif //VKRenderer_h_Included

View File

@@ -292,7 +292,15 @@ VkBool32 VKSD_ConfigureWindowSurface(VKWinSDOps* vkwinsdo) {
}
static void VKSD_OnDispose(JNIEnv* env, SurfaceDataOps* ops) {
JNU_CallStaticMethodByName(env, NULL, "sun/java2d/vulkan/VKSurfaceData", "dispose", "(J)V", ptr_to_jlong(ops));
// We are being called from the disposer thread, RQ might be working in parallel.
// VKRenderQueue.lock/unlock is equivalent to AWT_LOCK/AWT_UNLOCK,
// but those are only available in the toolkit-specific part of AWT, so we call RQ there.
jobject rq = JNU_CallStaticMethodByName(env, NULL,
"sun/java2d/vulkan/VKRenderQueue", "getInstance", "()Lsun/java2d/vulkan/VKRenderQueue;").l;
JNU_CallMethodByName(env, NULL, rq, "lock", "()V");
VKSD_ResetSurface((VKSDOps*) ops);
JNU_CallMethodByName(env, NULL, rq, "unlock", "()V");
(*env)->DeleteLocalRef(env, rq);
}
JNIEXPORT VKSDOps* VKSD_CreateSurface(JNIEnv* env, jobject vksd, jint type, jint format, jint backgroundRGB,

View File

@@ -62,7 +62,7 @@ typedef uint16_t VKPackedSwizzle;
*/
typedef struct {
float m00, m01, m02;
float m10, m11, m12;
float m10 __attribute__((aligned(16))), m11, m12;
} VKTransform;
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VKMemory);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -48,7 +48,7 @@
// restore the area overwritten by the graphic with
// what was there prior to rendering the graphic.
static const char szNetscape20ext[] = "NETSCAPE2.0";
static const char szNetscape20ext[11] = "NETSCAPE2.0";
#define NSEXT_LOOP 0x01 // Loop Count field code
@@ -181,7 +181,7 @@ SplashDecodeGif(Splash * splash, GifFileType * gif)
}
case APPLICATION_EXT_FUNC_CODE:
{
if (size == strlen(szNetscape20ext)
if (size == sizeof(szNetscape20ext)
&& memcmp(pExtension, szNetscape20ext, size) == 0) {
int iSubCode;

View File

@@ -47,7 +47,6 @@ import java.awt.image.WritableRaster;
import sun.awt.image.OffScreenImage;
import sun.awt.image.SunVolatileImage;
import sun.awt.image.SurfaceManager;
import sun.awt.image.VolatileSurfaceManager;
import sun.java2d.Disposer;
import sun.java2d.DisposerRecord;
import sun.java2d.SurfaceData;
@@ -56,7 +55,6 @@ import sun.java2d.loops.RenderLoops;
import sun.java2d.loops.SurfaceType;
import sun.java2d.pipe.Region;
import sun.java2d.x11.X11SurfaceData;
import sun.java2d.x11.X11VolatileSurfaceManager;
/**
* This is an implementation of a GraphicsConfiguration object for a
@@ -66,7 +64,7 @@ import sun.java2d.x11.X11VolatileSurfaceManager;
* @see GraphicsDevice
*/
public class X11GraphicsConfig extends GraphicsConfiguration
implements SurfaceManager.ProxiedGraphicsConfig, SurfaceManager.Factory
implements SurfaceManager.ProxiedGraphicsConfig
{
private final X11GraphicsDevice device;
protected int visual;
@@ -502,10 +500,4 @@ public class X11GraphicsConfig extends GraphicsConfiguration
}
private native boolean isTranslucencyCapable(long x11ConfigData);
@Override
public VolatileSurfaceManager createVolatileManager(SunVolatileImage image,
Object context) {
return new X11VolatileSurfaceManager(image, context);
}
}

View File

@@ -67,8 +67,7 @@ public final class X11GraphicsDevice extends GraphicsDevice
* therefore methods, which is using this id should be ready to it.
*/
private volatile int screen;
Map<SurfaceType, SurfaceManager.ProxyCache> x11ProxyCacheMap =
Collections.synchronizedMap(new HashMap<>());
Map<SurfaceType, SurfaceManager.ProxyCache> x11ProxyCacheMap = Collections.synchronizedMap(new HashMap<>());
private static AWTPermission fullScreenExclusivePermission;
private static Boolean xrandrExtSupported;
@@ -119,8 +118,7 @@ public final class X11GraphicsDevice extends GraphicsDevice
}
public SurfaceManager.ProxyCache getProxyCacheFor(SurfaceType st) {
return x11ProxyCacheMap.computeIfAbsent(st,
unused -> new SurfaceManager.ProxyCache());
return x11ProxyCacheMap.computeIfAbsent(st, unused -> new SurfaceManager.ProxyCache());
}
/**

View File

@@ -42,6 +42,8 @@ import java.util.Map;
import sun.awt.X11.XToolkit;
import sun.java2d.SunGraphicsEnvironment;
import sun.java2d.SurfaceManagerFactory;
import sun.java2d.UnixSurfaceManagerFactory;
import sun.java2d.xr.XRSurfaceData;
/**
@@ -134,6 +136,10 @@ public final class X11GraphicsEnvironment extends SunGraphicsEnvironment impleme
return null;
}
});
// Install the correct surface manager factory.
SurfaceManagerFactory.setInstance(new UnixSurfaceManagerFactory());
}
private static boolean isVMWare() {

View File

@@ -114,8 +114,6 @@ public class DefaultFrameDecoration extends FullFrameDecorationHelper {
@Override
protected Rectangle getMaximizeButtonBounds() {
if (!hasMaximizeButton()) return null;
int x = peer.getWidth() - BUTTON_SIZE * 2 - BUTTONS_RIGHT_PADDING
- BUTTONS_PADDING - BORDER_SIZE;
int y = (int) Math.floor((HEIGHT - BUTTON_SIZE + 1f) / 2);
@@ -124,8 +122,6 @@ public class DefaultFrameDecoration extends FullFrameDecorationHelper {
@Override
protected Rectangle getMinimizeButtonBounds() {
if (!hasMinimizeButton()) return null;
int x = peer.getWidth() - BUTTON_SIZE * 3 - BUTTONS_RIGHT_PADDING
- BUTTONS_PADDING * 2 - BORDER_SIZE;
int y = (int) Math.floor((HEIGHT - BUTTON_SIZE + 1f) / 2);

View File

@@ -268,7 +268,7 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
return false;
}
public static Window getToplevelFor(Component component) {
private static Window getToplevelFor(Component component) {
Container container = component instanceof Container c ? c : component.getParent();
for (Container p = container; p != null; p = p.getParent()) {
if (p instanceof Window window && !isWlPopup(window)) {
@@ -285,7 +285,7 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
: c.getParent();
}
public static Point getRelativeLocation(Component c, Window toplevel) {
static Point getRelativeLocation(Component c, Window toplevel) {
Objects.requireNonNull(c);
if (toplevel == null) {
@@ -1590,7 +1590,7 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
* Converts a value in the Java coordinate system into the Wayland
* surface-local coordinate system.
*/
public final int javaUnitsToSurfaceUnits(int value) {
int javaUnitsToSurfaceUnits(int value) {
if (!WLGraphicsEnvironment.isDebugScaleEnabled()) {
return value;
} else {
@@ -1600,7 +1600,7 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
}
}
public final int javaUnitsToSurfaceSize(int value) {
int javaUnitsToSurfaceSize(int value) {
if (!WLGraphicsEnvironment.isDebugScaleEnabled()) {
return value;
} else {

View File

@@ -47,7 +47,7 @@ public class WLDataSource {
private static native void setDnDActionsImpl(long nativePtr, int actions);
private static native void setDnDIconImpl(long nativePtr, int scale, int width, int height, int offsetX, int offsetY, int[] pixels);
private static native void setDnDIconImpl(long nativePtr, int width, int height, int offsetX, int offsetY, int[] pixels);
WLDataSource(WLDataDevice dataDevice, int protocol, Transferable data) {
var wlDataTransferer = (WLDataTransferer) WLDataTransferer.getInstance();
@@ -96,7 +96,7 @@ public class WLDataSource {
setDnDActionsImpl(nativePtr, actions);
}
public void setDnDIcon(Image image, int scale, int offsetX, int offsetY) {
public void setDnDIcon(Image image, int offsetX, int offsetY) {
if (nativePtr == 0) {
throw new IllegalStateException("Native pointer is null");
}
@@ -118,7 +118,7 @@ public class WLDataSource {
bufferedImage.getRGB(0, 0, width, height, pixels, 0, width);
}
setDnDIconImpl(nativePtr, scale, width, height, offsetX, offsetY, pixels);
setDnDIconImpl(nativePtr, width, height, offsetX, offsetY, pixels);
}
public synchronized void destroy() {

View File

@@ -92,33 +92,21 @@ public class WLDragSourceContextPeer extends SunDragSourceContextPeer {
this.dataDevice = dataDevice;
}
private WLComponentPeer getPeer() {
private long getComponentWlSurfacePtr() {
var comp = getComponent();
while (comp != null) {
var peer = AWTAccessor.getComponentAccessor().getPeer(comp);
if (peer instanceof WLComponentPeer wlPeer) {
return wlPeer;
return wlPeer.getSurface().getWlSurfacePtr();
}
comp = comp.getParent();
}
return null;
}
private WLMainSurface getSurface() {
WLComponentPeer peer = getPeer();
if (peer != null) {
return peer.getSurface();
}
return null;
return 0;
}
@Override
protected void startDrag(Transferable trans, long[] formats, Map<Long, DataFlavor> formatMap) {
var mainSurface = getSurface();
if (mainSurface == null) {
return;
}
// formats and formatMap are unused, because WLDataSource already references the same DataTransferer singleton
var source = new WLDragSource(trans);
@@ -130,14 +118,13 @@ public class WLDragSourceContextPeer extends SunDragSourceContextPeer {
var dragImage = getDragImage();
if (dragImage != null) {
var dragImageOffset = getDragImageOffset();
source.setDnDIcon(dragImage,
mainSurface.getGraphicsDevice().getDisplayScale(),
dragImageOffset.x, dragImageOffset.y);
source.setDnDIcon(dragImage, dragImageOffset.x, dragImageOffset.y);
}
long eventSerial = WLToolkit.getInputState().pointerButtonSerial();
dataDevice.startDrag(source, mainSurface.getWlSurfacePtr(), eventSerial);
var wlSurface = getComponentWlSurfacePtr();
dataDevice.startDrag(source, wlSurface, eventSerial);
}
@Override

View File

@@ -26,7 +26,6 @@
package sun.awt.wl;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.peer.FramePeer;
import sun.awt.AWTAccessor;
@@ -43,12 +42,6 @@ public class WLFramePeer extends WLDecoratedPeer implements FramePeer {
super(target, target.isUndecorated(),
Toolkit.getDefaultToolkit().isFrameStateSupported(Frame.ICONIFIED),
Toolkit.getDefaultToolkit().isFrameStateSupported(Frame.MAXIMIZED_BOTH));
AWTAccessor.getWindowAccessor().addWindowListener(target, new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
getFrame().removeNotify();
}
});
}
@Override

View File

@@ -38,9 +38,35 @@ import sun.java2d.wl.WLSurfaceSizeListener;
public abstract class WLGraphicsConfig extends GraphicsConfiguration {
private final WLGraphicsDevice device;
private final int x;
private final int y;
private final int xLogical; // logical (scaled) horizontal location; optional, could be zero
private final int yLogical; // logical (scaled) vertical location; optional, could be zero
private final int width;
private final int height;
private final int widthLogical; // logical (scaled) width; optional, could be zero
private final int heightLogical;// logical (scaled) height; optional, could be zero
private final int displayScale; // as reported by Wayland
private final double effectiveScale; // as enforced by Java
protected WLGraphicsConfig(WLGraphicsDevice device) {
protected WLGraphicsConfig(WLGraphicsDevice device, int x, int y, int xLogical, int yLogical,
int width, int height, int widthLogical, int heightLogical,
int displayScale) {
this.device = device;
this.x = x;
this.y = y;
this.xLogical = xLogical;
this.yLogical = yLogical;
this.width = width;
this.height = height;
this.widthLogical = widthLogical;
this.heightLogical = heightLogical;
this.displayScale = displayScale;
this.effectiveScale = WLGraphicsEnvironment.effectiveScaleFrom(displayScale);
}
boolean differsFrom(int width, int height, int scale) {
return width != this.width || height != this.height || scale != this.displayScale;
}
@Override
@@ -58,7 +84,7 @@ public abstract class WLGraphicsConfig extends GraphicsConfiguration {
@Override
public AffineTransform getDefaultTransform() {
double scale = device.getEffectiveScale();
double scale = effectiveScale;
return AffineTransform.getScaleInstance(scale, scale);
}
@@ -71,18 +97,20 @@ public abstract class WLGraphicsConfig extends GraphicsConfiguration {
@Override
public Rectangle getBounds() {
return device.getBounds();
return (widthLogical > 0 && heightLogical > 0)
? new Rectangle(xLogical, yLogical, widthLogical, heightLogical)
: new Rectangle(x, y, width, height);
}
public Rectangle getRealBounds() {
return device.getRealBounds();
return new Rectangle(x, y, width, height);
}
/**
* Returns the preferred Wayland buffer scale for this display configuration.
*/
public int getDisplayScale() {
return device.getDisplayScale();
return displayScale;
}
/**
@@ -90,7 +118,7 @@ public abstract class WLGraphicsConfig extends GraphicsConfiguration {
* if overridden with the sun.java2d.uiScale system property.
*/
public double getEffectiveScale() {
return device.getEffectiveScale();
return effectiveScale;
}
public abstract SurfaceType getSurfaceType();
@@ -99,7 +127,6 @@ public abstract class WLGraphicsConfig extends GraphicsConfiguration {
@Override
public String toString() {
Rectangle bounds = getBounds();
return String.format("%dx%d@(%d, %d) %dx scale", bounds.width, bounds.height, bounds.x, bounds.y, getDisplayScale());
return String.format("%dx%d@(%d, %d) %dx scale", width, height, x, y, displayScale);
}
}

View File

@@ -39,21 +39,15 @@ import java.util.HashSet;
import java.util.Set;
/**
* Corresponds to a Wayland output and is identified by its wlID.
* Owns all graphics configurations associated with this device.
* Encapsulates all the other properties of the output, such as its size
* and location in a multi-monitor configuration.
* Whenever any of these properties change, they are updated and
* the GraphicsConfiguration objects get re-created to let referents know of the change.
* Corresponds to Wayland's output and is identified by its wlID and x, y coordinates
* in the multi-monitor setup. Whenever those change, this device is re-created.
*/
public class WLGraphicsDevice extends GraphicsDevice {
private static final double MM_IN_INCH = 25.4;
/**
* ID of the corresponding wl_output object received from Wayland.
* Only changes when the device gets invalidated.
*/
private volatile int wlID;
private volatile int wlID; // only changes when the device gets invalidated
/**
* Some human-readable name of the device that came from Wayland.
@@ -64,142 +58,75 @@ public class WLGraphicsDevice extends GraphicsDevice {
/**
* The horizontal location of this device in the multi-monitor configuration.
*/
private volatile int x;
private volatile int x; // only changes when the device gets invalidated
/**
* The vertical location of this device in the multi-monitor configuration.
*/
private volatile int y;
private volatile int y; // only changes when the device gets invalidated
/**
* Pixel width, mostly for accounting and reporting.
*/
private volatile int width;
private volatile int xLogical; // logical (scaled) horizontal location; optional, could be zero
private volatile int yLogical; // logical (scaled) vertical location; optional, could be zero
/**
* Pixel height, mostly for accounting and reporting.
*/
private volatile int height;
private final int widthMm;
private final int heightMm;
/**
* Logical (scaled) width in Java units.
*/
private volatile int widthLogical;
// Configs are always the same in size and scale
private volatile GraphicsConfiguration[] configs = null;
/**
* Logical (scaled) height in Java units.
*/
private volatile int heightLogical;
// The default config is an object from the configs array
private volatile WLGraphicsConfig defaultConfig = null;
/**
* Width in millimeters.
*/
private volatile int widthMm;
/**
* Height in millimeters.
*/
private volatile int heightMm;
/**
* The device's scale factor as reported by Wayland.
* Since it is an integer, it's usually higher than the real fraction scale.
*/
private volatile int displayScale;
/**
* The effective scale factor as determined by Java.
*/
private volatile double effectiveScale;
private volatile GraphicsConfiguration[] configs;
private volatile WLGraphicsConfig defaultConfig; // A reference to the configs array
/**
* Top-level window peers that consider this device as their primary one
* and get their graphics configuration from it
*/
// Top-level window peers that consider this device as their primary one
// and get their graphics configuration from it
private final Set<WLComponentPeer> toplevels = new HashSet<>(); // guarded by 'this'
private WLGraphicsDevice(int id,
String name,
int x, int y,
int width, int height,
int widthLogical, int heightLogical,
int widthMm, int heightMm,
int displayScale) {
assert width > 0 && height > 0;
assert widthLogical > 0 && heightLogical > 0;
assert widthMm > 0 && heightMm > 0;
assert displayScale > 0;
private WLGraphicsDevice(int id, int x, int y, int xLogical, int yLogical, int widthMm, int heightMm) {
this.wlID = id;
this.name = name;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.widthLogical = widthLogical;
this.heightLogical = heightLogical;
this.xLogical = xLogical;
this.yLogical = yLogical;
this.widthMm = widthMm;
this.heightMm = heightMm;
this.displayScale = displayScale;
this.effectiveScale = WLGraphicsEnvironment.effectiveScaleFrom(displayScale);
makeGC();
}
private void makeGC() {
GraphicsConfiguration[] newConfigs;
WLGraphicsConfig newDefaultConfig;
if (VKEnv.isPresentationEnabled()) {
newConfigs = VKEnv.getDevices().flatMap(d -> d.getPresentableGraphicsConfigs().map(
gc -> WLVKGraphicsConfig.getConfig(gc, this)))
.toArray(WLGraphicsConfig[]::new);
newDefaultConfig = (WLGraphicsConfig) newConfigs[0];
} 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, true);
newConfigs = new GraphicsConfiguration[2];
newConfigs[0] = newDefaultConfig;
newConfigs[1] = WLSMGraphicsConfig.getConfig(this, false);
int getID() {
return wlID;
}
void updateConfiguration(String name, int width, int height, int widthLogical, int heightLogical, int scale) {
this.name = name;
WLGraphicsConfig config = defaultConfig;
// Note that all configs are of equal size and scale
if (config == null || config.differsFrom(width, height, scale)) {
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 (VKEnv.isPresentationEnabled()) {
newConfigs = VKEnv.getDevices().flatMap(d -> d.getPresentableGraphicsConfigs().map(
gc -> WLVKGraphicsConfig.getConfig(
gc, this, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale)))
.toArray(WLGraphicsConfig[]::new);
newDefaultConfig = (WLGraphicsConfig) newConfigs[0];
} 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, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale, true);
newConfigs = new GraphicsConfiguration[2];
newConfigs[0] = newDefaultConfig;
newConfigs[1] = WLSMGraphicsConfig.getConfig(this, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale, false);
}
configs = newConfigs;
defaultConfig = newDefaultConfig;
}
configs = newConfigs;
defaultConfig = newDefaultConfig;
}
void updateConfiguration(String name,
int x, int y,
int width, int height,
int widthLogical, int heightLogical,
int widthMm, int heightMm,
int scale) {
assert width > 0 && height > 0;
assert widthLogical > 0 && heightLogical > 0;
assert widthMm > 0 && heightMm > 0;
assert scale > 0;
this.name = name;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.widthLogical = widthLogical;
this.heightLogical = heightLogical;
this.widthMm = widthMm;
this.heightMm = heightMm;
this.displayScale = scale;
this.effectiveScale = WLGraphicsEnvironment.effectiveScaleFrom(scale);
// It is necessary to create new config objects whenever this device changes
// as GraphicsConfiguration identity is used to detect changes in scale, among other things.
makeGC();
// It is important that by the time displayChanged() events are delivered,
// all the peers on this device had their graphics configuration updated
// to refer to the new ones with, perhaps, a different scale or resolution.
// to refer to the new ones with, perhaps, different scale or resolution.
// This affects various BufferStrategy that use volatile images as their buffers.
notifyToplevels();
}
@@ -209,36 +136,47 @@ public class WLGraphicsDevice extends GraphicsDevice {
synchronized (this) {
toplevelsCopy.addAll(toplevels);
}
toplevelsCopy.forEach(WLComponentPeer::checkIfOnNewScreen);
int wlOutputID = this.wlID;
// NB: each of those peers will likely receive another such notification
// from Wayland when it gets the wl_surface::enter event, but the second one
// will effectively be a no-op.
toplevelsCopy.forEach((peer) -> peer.notifyEnteredOutput(wlOutputID));
}
/**
* Changes all aspects of this device, including its identity to be that of the given
* Changes all aspects of this device including its identity to be that of the given
* device. Only used for devices that are no longer physically present, but references
* to which may still exist in the program.
*/
void invalidate(WLGraphicsDevice similarDevice) {
// Note: It is expected that all the surface this device used to host have already received
// the 'leave' event and updated their device/graphics configurations accordingly.
this.wlID = similarDevice.wlID;
updateConfiguration(similarDevice.name,
similarDevice.x,
similarDevice.y,
similarDevice.width,
similarDevice.height,
similarDevice.widthLogical,
similarDevice.heightLogical,
similarDevice.widthMm,
similarDevice.heightMm,
similarDevice.displayScale);
this.x = similarDevice.x;
this.y = similarDevice.y;
this.xLogical = similarDevice.xLogical;
this.yLogical = similarDevice.yLogical;
int newScale = similarDevice.getDisplayScale();
Rectangle newBounds = similarDevice.defaultConfig.getBounds();
Rectangle newRealBounds = similarDevice.defaultConfig.getRealBounds();
updateConfiguration(similarDevice.name, newRealBounds.width, newRealBounds.height, newBounds.width, newBounds.height, newScale);
}
public static WLGraphicsDevice createWithConfiguration(int id, String name,
int x, int y,
int x, int y, int xLogical, int yLogical,
int width, int height, int widthLogical, int heightLogical,
int widthMm, int heightMm,
int scale) {
return new WLGraphicsDevice(id, name, x, y, width, height, widthLogical, heightLogical, widthMm, heightMm, scale);
WLGraphicsDevice device = new WLGraphicsDevice(id, x, y, xLogical, yLogical, widthMm, heightMm);
device.updateConfiguration(name, width, height, widthLogical, heightLogical, scale);
return device;
}
/**
* Compares the identity of this device with the given attributes
* and returns true iff the attributes identify the same device.
*/
boolean isSameDeviceAs(int wlID, int x, int y, int xLogical, int yLogical) {
return this.wlID == wlID && this.x == x && this.y == y && this.xLogical == xLogical && this.yLogical == yLogical;
}
boolean hasSameNameAs(WLGraphicsDevice otherDevice) {
@@ -269,8 +207,8 @@ public class WLGraphicsDevice extends GraphicsDevice {
public GraphicsConfiguration[] getConfigurations() {
// From wayland.xml, wl_output.mode event:
// "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."
// advertise the current mode and never send other modes. Clients
// should not rely on non-current modes."
// So there's always the same set of configs.
return configs.clone();
}
@@ -280,24 +218,8 @@ public class WLGraphicsDevice extends GraphicsDevice {
return defaultConfig;
}
int getID() {
return wlID;
}
Rectangle getBounds() {
return new Rectangle(x, y, widthLogical, heightLogical);
}
Rectangle getRealBounds() {
return new Rectangle(x, y, width, height);
}
int getDisplayScale() {
return displayScale;
}
double getEffectiveScale() {
return effectiveScale;
return defaultConfig.getDisplayScale();
}
int getResolution() {
@@ -305,6 +227,13 @@ public class WLGraphicsDevice extends GraphicsDevice {
return getResolutionX(defaultConfig);
}
double getPhysicalResolution() {
Rectangle bounds = defaultConfig.getRealBounds();
double daigInPixels = Math.sqrt(bounds.width * bounds.width + bounds.height * bounds.height);
double diagInMm = Math.sqrt(widthMm * widthMm + heightMm * heightMm);
return daigInPixels * MM_IN_INCH / diagInMm;
}
double getPhysicalScale() {
Rectangle bounds = defaultConfig.getRealBounds();
double daigInPixels = Math.sqrt(bounds.width * bounds.width + bounds.height * bounds.height);
@@ -338,7 +267,7 @@ public class WLGraphicsDevice extends GraphicsDevice {
boolean fsSupported = isFullScreenSupported();
if (fsSupported && old != null) {
// enter windowed mode and restore the original display mode
// enter windowed mode and restore original display mode
exitFullScreenExclusive(old);
}
@@ -382,8 +311,9 @@ public class WLGraphicsDevice extends GraphicsDevice {
@Override
public String toString() {
var config = defaultConfig;
return String.format("WLGraphicsDevice: '%s' id=%d at (%d, %d) with %s",
name, wlID, x, y, config != null ? config : "<no configs>");
return String.format("WLGraphicsDevice: '%s' id=%d at (%d, %d) ((%d, %d) logical) with %s",
name, wlID, x, y, xLogical, yLogical,
config != null ? config : "<no configs>");
}
public Insets getInsets() {

View File

@@ -26,7 +26,6 @@
package sun.awt.wl;
import java.awt.AWTError;
import java.awt.Dimension;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
@@ -37,6 +36,8 @@ import java.util.Optional;
import sun.awt.HiDPIInfoProvider;
import sun.java2d.SunGraphicsEnvironment;
import sun.java2d.SurfaceManagerFactory;
import sun.java2d.UnixSurfaceManagerFactory;
import sun.util.logging.PlatformLogger;
import sun.util.logging.PlatformLogger.Level;
@@ -55,10 +56,9 @@ public class WLGraphicsEnvironment extends SunGraphicsEnvironment implements HiD
private static final boolean debugScaleEnabled;
private final Dimension totalDisplayBounds = new Dimension();
private final List<WLGraphicsDevice> devices = new ArrayList<>(5);
static {
System.loadLibrary("awt");
SurfaceManagerFactory.setInstance(new UnixSurfaceManagerFactory());
debugScaleEnabled = SunGraphicsEnvironment.isUIScaleEnabled() && SunGraphicsEnvironment.getDebugScale() >= 1;
@@ -92,30 +92,15 @@ public class WLGraphicsEnvironment extends SunGraphicsEnvironment implements HiD
}
}
@Override
public GraphicsDevice getDefaultScreenDevice() {
synchronized (devices) {
if (devices.isEmpty()) {
throw new AWTError("no screen devices");
}
return devices.getFirst();
}
}
@Override
public synchronized GraphicsDevice[] getScreenDevices() {
synchronized (devices) {
return devices.toArray(new GraphicsDevice[0]);
}
}
@Override
public boolean isDisplayLocal() {
return true;
}
private final List<WLGraphicsDevice> devices = new ArrayList<>(5);
private void notifyOutputConfigured(String name, String make, String model, int wlID,
int x, int y,
int x, int y, int xLogical, int yLogical,
int width, int height,
int widthLogical, int heightLogical,
int widthMm, int heightMm,
@@ -123,33 +108,44 @@ public class WLGraphicsEnvironment extends SunGraphicsEnvironment implements HiD
// 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%d logical) %dx scale",
wlID, x, y, width, height, widthLogical, heightLogical, scale));
log.fine(String.format("Output configured id=%d at (%d, %d) (%d, %d logical) %dx%d (%dx%d logical) %dx scale",
wlID, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale));
}
// Logical size comes from an optional protocol, so take the data from the main one, if absent
if (widthLogical == 0) widthLogical = width;
if (heightLogical == 0) heightLogical = height;
String humanID = deviceNameFrom(name, make, model);
WLGraphicsDevice gd = deviceWithID(wlID);
if (gd != null) {
// Some properties of an existing device have changed; update the existing device and
// let all the windows it hosts know about the change.
gd.updateConfiguration(humanID, x, y, width, height, widthLogical, heightLogical, widthMm, heightMm, scale);
} else {
WLGraphicsDevice newGD = WLGraphicsDevice.createWithConfiguration(wlID, humanID,
x, y, width, height, widthLogical, heightLogical,
widthMm, heightMm, scale);
synchronized (devices) {
devices.add(newGD);
String humanID =
(name != null ? name + " " : "")
+ (make != null ? make + " " : "")
+ (model != null ? model : "");
synchronized (devices) {
boolean newOutput = true;
for (int i = 0; i < devices.size(); i++) {
final WLGraphicsDevice gd = devices.get(i);
if (gd.getID() == wlID) {
newOutput = false;
if (gd.isSameDeviceAs(wlID, x, y, xLogical, yLogical)) {
// These coordinates and the size are not scaled.
gd.updateConfiguration(humanID, width, height, widthLogical, heightLogical, scale);
} else {
final WLGraphicsDevice updatedDevice = WLGraphicsDevice.createWithConfiguration(wlID, humanID,
x, y, xLogical, yLogical, width, height, widthLogical, heightLogical,
widthMm, heightMm, scale);
devices.set(i, updatedDevice);
gd.invalidate(updatedDevice);
}
break;
}
}
if (newOutput) {
final WLGraphicsDevice gd = WLGraphicsDevice.createWithConfiguration(wlID, humanID,
x, y, xLogical, yLogical, width, height, widthLogical, heightLogical,
widthMm, heightMm, scale);
devices.add(gd);
}
if (LogDisplay.ENABLED) {
double effectiveScale = effectiveScaleFrom(scale);
LogDisplay log = newOutput ? LogDisplay.ADDED : LogDisplay.CHANGED;
log.log(wlID, (int) (width / effectiveScale) + "x" + (int) (height / effectiveScale), effectiveScale);
}
}
if (LogDisplay.ENABLED) {
double effectiveScale = effectiveScaleFrom(scale);
LogDisplay log = (gd == null) ? LogDisplay.ADDED : LogDisplay.CHANGED;
log.log(wlID, (int) (width / effectiveScale) + "x" + (int) (height / effectiveScale), effectiveScale);
}
updateTotalDisplayBounds();
@@ -160,12 +156,6 @@ public class WLGraphicsEnvironment extends SunGraphicsEnvironment implements HiD
}
}
private static String deviceNameFrom(String name, String make, String model) {
return (name != null ? name + " " : "")
+ (make != null ? make + " " : "")
+ (model != null ? model : "");
}
private WLGraphicsDevice getSimilarDevice(WLGraphicsDevice modelDevice) {
WLGraphicsDevice similarDevice = devices.isEmpty() ? null : devices.getFirst();
for (WLGraphicsDevice device : devices) {
@@ -189,33 +179,47 @@ public class WLGraphicsEnvironment extends SunGraphicsEnvironment implements HiD
log.fine(String.format("Output destroyed id=%d", wlID));
}
// NB: id may *not* be that of any output; if so, just ignore this event.
WLGraphicsDevice gd = deviceWithID(wlID);
if (gd != null) {
if (LogDisplay.ENABLED) {
WLGraphicsConfig config = (WLGraphicsConfig) gd.getDefaultConfiguration();
Rectangle bounds = config.getBounds();
LogDisplay.REMOVED.log(wlID, bounds.width + "x" + bounds.height, config.getEffectiveScale());
synchronized (devices) {
final Optional<WLGraphicsDevice> deviceOptional = devices.stream()
.filter(device -> device.getID() == wlID)
.findFirst();
if (deviceOptional.isPresent()) {
final WLGraphicsDevice destroyedDevice = deviceOptional.get();
if (LogDisplay.ENABLED) {
WLGraphicsConfig config = (WLGraphicsConfig) destroyedDevice.getDefaultConfiguration();
Rectangle bounds = config.getBounds();
LogDisplay.REMOVED.log(wlID, bounds.width + "x" + bounds.height, config.getEffectiveScale());
}
devices.remove(destroyedDevice);
final WLGraphicsDevice similarDevice = getSimilarDevice(destroyedDevice);
if (similarDevice != null) destroyedDevice.invalidate(similarDevice);
}
synchronized (devices) {
devices.remove(gd);
}
final WLGraphicsDevice similarDevice = getSimilarDevice(gd);
if (similarDevice != null) gd.invalidate(similarDevice);
}
updateTotalDisplayBounds();
displayChanged();
}
WLGraphicsDevice deviceWithID(int wlOutputID) {
WLGraphicsDevice notifySurfaceEnteredOutput(WLComponentPeer wlComponentPeer, int wlOutputID) {
synchronized (devices) {
for (WLGraphicsDevice gd : devices) {
if (gd.getID() == wlOutputID) {
return gd;
}
}
return null;
}
}
WLGraphicsDevice notifySurfaceLeftOutput(WLComponentPeer wlComponentPeer, int wlOutputID) {
synchronized (devices) {
for (WLGraphicsDevice gd : devices) {
if (gd.getID() == wlOutputID) {
return gd;
}
}
return null;
}
return null;
}
public Dimension getTotalDisplayBounds() {
@@ -225,14 +229,16 @@ public class WLGraphicsEnvironment extends SunGraphicsEnvironment implements HiD
}
private void updateTotalDisplayBounds() {
Rectangle virtualBounds = new Rectangle();
synchronized (devices) {
for (var gd : devices) {
virtualBounds = virtualBounds.union(gd.getBounds());
Rectangle virtualBounds = new Rectangle();
for (GraphicsDevice gd : devices) {
for (GraphicsConfiguration gc : gd.getConfigurations()) {
virtualBounds = virtualBounds.union(gc.getBounds());
}
}
synchronized (totalDisplayBounds) {
totalDisplayBounds.setSize(virtualBounds.getSize());
}
}
synchronized (totalDisplayBounds) {
totalDisplayBounds.setSize(virtualBounds.getSize());
}
}

View File

@@ -67,7 +67,7 @@ public class WLMainSurface extends WLSurface {
// Called from native code whenever the corresponding wl_surface enters an output (monitor)
synchronized (devices) {
final WLGraphicsEnvironment ge = (WLGraphicsEnvironment)WLGraphicsEnvironment.getLocalGraphicsEnvironment();
final WLGraphicsDevice gd = ge.deviceWithID(wlOutputID);
final WLGraphicsDevice gd = ge.notifySurfaceEnteredOutput(peer, wlOutputID);
if (gd != null) {
devices.add(gd);
}
@@ -81,7 +81,7 @@ public class WLMainSurface extends WLSurface {
// Called from native code whenever the corresponding wl_surface leaves an output (monitor)
synchronized (devices) {
final WLGraphicsEnvironment ge = (WLGraphicsEnvironment)WLGraphicsEnvironment.getLocalGraphicsEnvironment();
final WLGraphicsDevice gd = ge.deviceWithID(wlOutputID);
final WLGraphicsDevice gd = ge.notifySurfaceLeftOutput(peer, wlOutputID);
if (gd != null) {
devices.remove(gd);
}

View File

@@ -24,16 +24,11 @@
*/
package sun.awt.wl;
import sun.awt.SunToolkit;
import java.awt.Point;
import java.awt.Window;
import java.awt.peer.MouseInfoPeer;
public class WLMouseInfoPeer implements MouseInfoPeer, SunToolkit.RelativePointerMovementInfoProvider {
private final Object mouseDeltaLock = new Object();
private double mouseDeltaX = 0.0;
private double mouseDeltaY = 0.0;
public class WLMouseInfoPeer implements MouseInfoPeer {
@Override
public int fillPointWithCoords(Point point) {
@@ -60,23 +55,4 @@ public class WLMouseInfoPeer implements MouseInfoPeer, SunToolkit.RelativePointe
return HOLDER.instance;
}
void accumulatePointerDelta(double dx, double dy) {
synchronized (mouseDeltaLock) {
mouseDeltaX += dx;
mouseDeltaY += dy;
}
}
@Override
public Point getAccumulatedMouseDeltaAndReset() {
var p = new Point();
synchronized (mouseDeltaLock) {
int idx = (int) mouseDeltaX;
int idy = (int) mouseDeltaY;
p.setLocation(idx, idy);
mouseDeltaX = mouseDeltaX - idx;
mouseDeltaY = mouseDeltaY - idy;
}
return p;
}
}

View File

@@ -53,16 +53,36 @@ public class WLSMGraphicsConfig extends WLGraphicsConfig {
private final ColorModel colorModel;
private final SurfaceType surfaceType;
private WLSMGraphicsConfig(WLGraphicsDevice device, boolean translucencyCapable) {
super(device);
private WLSMGraphicsConfig(WLGraphicsDevice device,
int x,
int y,
int xLogical,
int yLogical,
int width,
int height,
int widthLogical,
int heightLogical,
int scale,
boolean translucencyCapable) {
super(device, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale);
this.translucencyCapable = translucencyCapable;
this.colorModel = colorModelFor(translucencyCapable ? Transparency.TRANSLUCENT : Transparency.OPAQUE);
// Note: GNOME Shell definitely expects alpha values to be pre-multiplied
this.surfaceType = translucencyCapable ? SurfaceType.IntArgbPre : SurfaceType.IntRgb;
}
public static WLSMGraphicsConfig getConfig(WLGraphicsDevice device, boolean translucencyCapable) {
var newConfig = new WLSMGraphicsConfig(device, translucencyCapable);
public static WLSMGraphicsConfig getConfig(WLGraphicsDevice device,
int x,
int y,
int xLogical,
int yLogical,
int width,
int height,
int widthLogical,
int heightLogical,
int scale,
boolean translucencyCapable) {
var newConfig = new WLSMGraphicsConfig(device, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale, translucencyCapable);
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("New shared memory config " + newConfig);
}

View File

@@ -36,7 +36,6 @@ import sun.awt.PeerEvent;
import sun.awt.SunToolkit;
import sun.awt.UNIXToolkit;
import sun.awt.datatransfer.DataTransferer;
import sun.awt.wl.im.WLInputMethodMetaDescriptor;
import sun.java2d.vulkan.VKEnv;
import sun.java2d.vulkan.VKRenderQueue;
import sun.util.logging.PlatformLogger;
@@ -300,10 +299,6 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
private static WLInputState inputState = WLInputState.initialState();
private static WLKeyboard keyboard;
private static void dispatchRelativePointerEvent(double dx, double dy) {
WLMouseInfoPeer.getInstance().accumulatePointerDelta(dx, dy);
}
private static void dispatchPointerEvent(WLPointerEvent e) {
// Invoked from the native code
assert EventQueue.isDispatchThread();
@@ -448,6 +443,7 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
final WLInputState newInputState = inputState.updatedFromKeyboardLeaveEvent(serial, surfacePtr);
final WLWindowPeer peer = peerFromSurface(surfacePtr);
if (peer != null && peer.getTarget() instanceof Window window) {
((WLToolkit) Toolkit.getDefaultToolkit()).ungrab(window);
final WindowEvent winLostFocusEvent = new WindowEvent(window, WindowEvent.WINDOW_LOST_FOCUS);
WLKeyboardFocusManagerPeer.getInstance().setCurrentFocusedWindow(null);
WLKeyboardFocusManagerPeer.getInstance().setCurrentFocusOwner(null);
@@ -782,27 +778,13 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
return 16777216; // 24 bits per pixel, 8 bits per channel
}
/**
* {@link java.awt.event.InputMethodEvent#getText()} can contain attributes which provide AWT/Swing with additional useful information
* (e.g. a language of the text).
*
* One kind of the possible attributes is {@link InputMethodHighlight}. It informs AWT/Swing that some parts of
* the text are in different states of the text composing process, hence they should look differently from the others.
* However, it doesn't tell how exactly they should look; this choice is left to Toolkit's implementations,
* or more precisely implementations of this method.
*
* @param highlight a state of a part of InputMethodEvent's text
*
* @return a collection of {@link TextAttribute}s (with their corresponding values as documented) informing how exactly
* such text should look or {@code null} if a mapping can't be provided.
*
* @see Toolkit#mapInputMethodHighlight(InputMethodHighlight)
*/
@Override
public Map<TextAttribute, ?> mapInputMethodHighlight(InputMethodHighlight highlight) {
return WLInputMethodMetaDescriptor.mapInputMethodHighlight(highlight);
public Map<TextAttribute, ?> mapInputMethodHighlight( InputMethodHighlight highlight) {
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Not implemented: WLToolkit.mapInputMethodHighlight()");
}
return null;
}
@Override
public boolean getLockingKeyState(int key) {
return switch (key) {
@@ -873,7 +855,10 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
*/
@Override
public InputMethodDescriptor getInputMethodAdapterDescriptor() {
return WLInputMethodMetaDescriptor.getInstanceIfAvailableOnPlatform();
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Not implemented: WLToolkit.getInputMethodAdapterDescriptor()");
}
return null;
}
/**
@@ -882,6 +867,9 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
*/
@Override
public boolean enableInputMethodsForTextComponent() {
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Not implemented: WLToolkit.enableInputMethodsForTextComponent()");
}
return true;
}
@@ -996,10 +984,9 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
var peer = AWTAccessor.getComponentAccessor().getPeer(w);
if (peer instanceof WLWindowPeer windowPeer) {
windowPeer.ungrab(false);
windowPeer.ungrab();
}
}
/**
* Returns if the java.awt.Desktop class is supported on the current
* desktop.

View File

@@ -33,7 +33,6 @@ import sun.java2d.wl.WLSMSurfaceData;
import javax.swing.JRootPane;
import javax.swing.RootPaneContainer;
import java.awt.AWTEvent;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Component;
@@ -101,7 +100,7 @@ public class WLWindowPeer extends WLComponentPeer implements WindowPeer, Surface
@Override
protected void wlSetVisible(boolean v) {
if (!v) ungrab(true);
if (!v) ungrab();
if (v && targetIsWlPopup() && shouldBeFocusedOnShowing()) {
requestWindowFocus();
@@ -205,7 +204,7 @@ public class WLWindowPeer extends WLComponentPeer implements WindowPeer, Surface
@Override
public void dispose() {
ungrab(true);
ungrab();
resetCornerMasks();
super.dispose();
}
@@ -247,17 +246,15 @@ public class WLWindowPeer extends WLComponentPeer implements WindowPeer, Surface
public void grab() {
if (grabbingWindow != null && !isGrabbing()) {
grabbingWindow.ungrab(false);
grabbingWindow.ungrab();
}
grabbingWindow = this;
}
public void ungrab(boolean externalUngrab) {
public void ungrab() {
if (isGrabbing()) {
grabbingWindow = null;
if (externalUngrab) {
WLToolkit.postEvent(new UngrabEvent(getTarget()));
}
WLToolkit.postEvent(new UngrabEvent(getTarget()));
}
}
@@ -265,13 +262,6 @@ public class WLWindowPeer extends WLComponentPeer implements WindowPeer, Surface
return this == grabbingWindow;
}
@Override
void handleJavaWindowFocusEvent(AWTEvent e) {
if (e.getID() == WindowEvent.WINDOW_LOST_FOCUS) {
ungrab(true);
}
}
@Override
public BufferedImage getClientAreaSnapshot(int x, int y, int width, int height) {
// Move the coordinate system to the client area

View File

@@ -1,220 +0,0 @@
/*
* Copyright 2025 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.im;
import sun.awt.wl.im.text_input_unstable_v3.WLInputMethodDescriptorZwpTextInputV3;
import sun.security.action.GetPropertyAction;
import sun.util.logging.PlatformLogger;
import java.awt.AWTException;
import java.awt.Image;
import java.awt.font.TextAttribute;
import java.awt.im.InputMethodHighlight;
import java.awt.im.spi.InputMethod;
import java.awt.im.spi.InputMethodDescriptor;
import java.security.AccessController;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
/**
* Since Wayland compositors may support multiple IM protocols,
* this class is responsible for choosing one specific among them,
* and the corresponding "real" implementation of {@code InputMethodDescriptor} from a subpackage.
*/
public final class WLInputMethodMetaDescriptor implements InputMethodDescriptor {
// NB: the class loading routine has to be as fast and not demanding on resources as possible.
// Also, it has to succeed in any practically possible situation.
// E.g. if the Wayland compositor doesn't support any known IM protocol,
// it mustn't prevent this class from being loaded successfully.
// Ideally, nothing additional should happen at the loading time.
//
// This class is directly used by WLToolkit to find and instantiate an InputMethod implementation.
// See java.text.MessageFormat for the formatting syntax
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.wl.im.WLInputMethodMetaDescriptor");
/** @see sun.awt.wl.WLToolkit#mapInputMethodHighlight(InputMethodHighlight) */
public static Map<TextAttribute, ?> mapInputMethodHighlight(final InputMethodHighlight highlight) {
// NB: The implementation is supposed to produce results exactly equal to XToolkit's implementation
// for better visual consistency.
if (highlight == null)
return null;
switch (highlight.getState()) {
case InputMethodHighlight.RAW_TEXT -> {
if (highlight.isSelected())
return imHighlightMapSelectedRawText;
else
return imHighlightMapUnselectedRawText;
}
case InputMethodHighlight.CONVERTED_TEXT -> {
if (highlight.isSelected())
return imHighlightMapSelectedConvertedText;
else
return imHighlightMapUnselectedConvertedText;
}
}
return null;
}
public static WLInputMethodMetaDescriptor getInstanceIfAvailableOnPlatform() {
final WLInputMethodMetaDescriptor result;
if (!ENABLE_NATIVE_IM_SUPPORT) {
result = null;
} else {
// For now there's only 1 possible implementation of IM,
// but if/when there are more, this method will have to choose one of them.
// It'll be good if the preferable engine can be chosen via a system property.
final InputMethodDescriptor realImDescriptor = WLInputMethodDescriptorZwpTextInputV3.getInstanceIfAvailableOnPlatform();
if (realImDescriptor != null) {
result = new WLInputMethodMetaDescriptor(realImDescriptor);
} else {
result = null;
}
}
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("getInstanceIfAvailableOnPlatform(): result={0}, ENABLE_NATIVE_IM_SUPPORT={1}.", result, ENABLE_NATIVE_IM_SUPPORT);
}
return result;
}
/* java.awt.im.spi.InputMethodDescriptor methods section */
@Override
public Locale[] getAvailableLocales() throws AWTException {
final Locale[] result = realImDescriptor.getAvailableLocales();
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("getAvailableLocales(): result={0}, this={1}.", Arrays.toString(result), this);
}
return result;
}
@Override
public boolean hasDynamicLocaleList() {
final boolean result = realImDescriptor.hasDynamicLocaleList();
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("hasDynamicLocaleList(): result={0}, this={1}.", result, this);
}
return result;
}
@Override
public String getInputMethodDisplayName(Locale inputLocale, Locale displayLanguage) {
return realImDescriptor.getInputMethodDisplayName(inputLocale, displayLanguage);
}
@Override
public Image getInputMethodIcon(Locale inputLocale) {
return realImDescriptor.getInputMethodIcon(inputLocale);
}
@Override
public InputMethod createInputMethod() throws Exception {
final InputMethod result = realImDescriptor.createInputMethod();
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("createInputMethod(): result={0}, this={1}.", result, this);
}
return result;
}
/* java.lang.Object methods section */
@Override
public String toString() {
return String.format("WLInputMethodMetaDescriptor@%d[realImDescriptor=%s]", System.identityHashCode(this), realImDescriptor);
}
/* Implementation details section */
/**
* The values are copied from XToolkit's implementation for better visual consistency with AWT on X11.
*
* @see #mapInputMethodHighlight(InputMethodHighlight)
* @see sun.awt.X11InputMethodBase
*/
private final static Map<TextAttribute, ?> imHighlightMapUnselectedRawText = Map.of(
TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD
);
private final static Map<TextAttribute, ?> imHighlightMapUnselectedConvertedText = Map.of(
TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_ONE_PIXEL
);
private final static Map<TextAttribute, ?> imHighlightMapSelectedRawText = Map.of(
TextAttribute.SWAP_COLORS, TextAttribute.SWAP_COLORS_ON
);
private final static Map<TextAttribute, ?> imHighlightMapSelectedConvertedText = Map.of(
TextAttribute.SWAP_COLORS, TextAttribute.SWAP_COLORS_ON
);
/**
* This flag allows disabling ALL integrations with native IMs. The idea is to allow users to disable
* unnecessary for them functionality if they face any problems because of it.
* Therefore, if it's {@code false}, the Toolkit code shouldn't use (directly or indirectly)
* any of Wayland's input methods-related APIs (e.g. the "text-input" protocol).
*/
private final static boolean ENABLE_NATIVE_IM_SUPPORT;
static {
boolean enableNativeImSupportInitializer = true;
try {
@SuppressWarnings("removal") // still needed for JBR21
final boolean enableNativeImSupportPropertyValue = Boolean.parseBoolean(
AccessController.doPrivileged(new GetPropertyAction("sun.awt.wl.im.enabled", "true"))
);
enableNativeImSupportInitializer = enableNativeImSupportPropertyValue;
} catch (Exception err) {
log.severe("Failed to read the value of the system property \"sun.awt.wl.im.enabled\". Assuming the default value(=true).", err);
}
ENABLE_NATIVE_IM_SUPPORT = enableNativeImSupportInitializer;
}
private final InputMethodDescriptor realImDescriptor;
private WLInputMethodMetaDescriptor(InputMethodDescriptor realImDescriptor) {
this.realImDescriptor = Objects.requireNonNull(realImDescriptor, "realImDescriptor");
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright 2025 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.im.text_input_unstable_v3;
/** Reason for the change of surrounding text or cursor position */
enum ChangeCause {
/** input method caused the change */
INPUT_METHOD(0),
/** something else than the input method caused the change */
OTHER (1);
public final int intValue;
ChangeCause(int intValue) {
this.intValue = intValue;
}
}

View File

@@ -1,371 +0,0 @@
/*
* Copyright 2025 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.im.text_input_unstable_v3;
import sun.util.logging.PlatformLogger;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.JTextComponent;
import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Rectangle;
import java.awt.TextComponent;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.TextEvent;
import java.awt.event.TextListener;
import java.lang.ref.WeakReference;
import java.util.Objects;
/**
* This class is intended to track all the cases when a new {@code zwp_text_input_v3::set_cursor_rectangle} request
* may have to be issued. Here are the examples of such cases:
* <ul>
* <li>The caret position has changed ;
* <li>The component has been moved/resized ;
* <li>The component's window has been moved/resized ;
* <li>The component's text has been changed ;
* </ul>
*/
class ClientComponentCaretPositionTracker implements ComponentListener, CaretListener, TextListener
{
// See java.text.MessageFormat for the formatting syntax
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.wl.im.text_input_unstable_v3.ClientComponentCaretPositionTracker");
public ClientComponentCaretPositionTracker(WLInputMethodZwpTextInputV3 im) {
this.im = new WeakReference<>(Objects.requireNonNull(im, "im"));
}
public void startTracking(final Component component) {
assert(EventQueue.isDispatchThread());
if (log.isLoggable(PlatformLogger.Level.FINER)) {
log.finer(
String.format("startTracking(component=%s): im=%s, this=%s.", component, getOwnerIm(), this),
new Throwable("Stacktrace")
);
}
stopTrackingCurrentComponent();
if (component == null) {
return;
}
trackedComponent = new WeakReference<>(component);
lastKnownClientWindowBounds = null;
try {
// Moving and changing the size causes a possible change of caret position
component.addComponentListener(this);
if (component instanceof JTextComponent jtc) {
jtc.addCaretListener(this);
isCaretListenerInstalled = true;
} else if (component instanceof TextComponent tc) {
tc.addTextListener(this);
isTextListenerInstalled = true;
}
if (log.isLoggable(PlatformLogger.Level.FINEST)) {
log.finest("startTracking(...): updated this={0}.", this);
}
} catch (Exception err) {
stopTrackingCurrentComponent();
throw err;
}
}
public void stopTrackingCurrentComponent() {
assert(EventQueue.isDispatchThread());
if (log.isLoggable(PlatformLogger.Level.FINER)) {
log.finer(String.format("stopTrackingCurrentComponent(): this=%s.", this), new Throwable("Stacktrace"));
}
final Component trackedComponentStrong = getTrackedComponentIfTracking();
if (trackedComponentStrong == null) {
return;
}
if (isTextListenerInstalled) {
isTextListenerInstalled = false;
try {
((TextComponent)trackedComponentStrong).removeTextListener(this);
} catch (Exception err) {
if (log.isLoggable(PlatformLogger.Level.WARNING)) {
log.warning(String.format("stopTrackingCurrentComponent(): exception occurred while removing the text listener from %s.", trackedComponentStrong), err);
}
}
}
if (isCaretListenerInstalled) {
isCaretListenerInstalled = false;
try {
((JTextComponent)trackedComponentStrong).removeCaretListener(this);
} catch (Exception err) {
if (log.isLoggable(PlatformLogger.Level.WARNING)) {
log.warning(String.format("stopTrackingCurrentComponent(): exception occurred while removing the caret listener from %s.", trackedComponentStrong), err);
}
}
}
try {
trackedComponentStrong.removeComponentListener(this);
} catch (Exception err) {
if (log.isLoggable(PlatformLogger.Level.WARNING)) {
log.warning(String.format("stopTrackingCurrentComponent(): exception occurred while removing the component listener from %s.", trackedComponentStrong), err);
}
}
lastKnownClientWindowBounds = null;
updatesAreDeferred = false;
if (trackedComponent != null) {
trackedComponent.clear();
trackedComponent = null;
}
}
public Component getTrackedComponentIfTracking() {
assert(EventQueue.isDispatchThread());
final Component trackedComponentStrong;
if (trackedComponent == null) {
trackedComponentStrong = null;
} else {
trackedComponentStrong = trackedComponent.get();
}
if (trackedComponentStrong == null) {
isTextListenerInstalled = false;
isCaretListenerInstalled = false;
lastKnownClientWindowBounds = null;
updatesAreDeferred = false;
if (trackedComponent != null) {
trackedComponent.clear();
trackedComponent = null;
}
}
return trackedComponentStrong;
}
public void deferUpdates() {
assert(EventQueue.isDispatchThread());
if (log.isLoggable(PlatformLogger.Level.FINER)) {
log.finer(String.format("deferUpdates(): this=%s.", this), new Throwable("Stacktrace"));
}
updatesAreDeferred = true;
}
public void resumeUpdates(final boolean discardDeferredUpdates) {
assert(EventQueue.isDispatchThread());
if (log.isLoggable(PlatformLogger.Level.FINER)) {
log.finer(String.format("resumeUpdates(%b): this=%s.", discardDeferredUpdates, this), new Throwable("Stacktrace"));
}
if (getTrackedComponentIfTracking() == null) return;
updatesAreDeferred = false;
hasDeferredUpdates = hasDeferredUpdates && !discardDeferredUpdates;
if (hasDeferredUpdates) {
updateNotify();
}
}
public boolean areUpdatesDeferred() {
assert(EventQueue.isDispatchThread());
return updatesAreDeferred;
}
/* Listening callbacks */
/** This method is intended to be called from the owning IM's {@link java.awt.im.spi.InputMethod#dispatchEvent(AWTEvent)}. */
public void onIMDispatchEvent(AWTEvent event) {
assert(EventQueue.isDispatchThread());
if (log.isLoggable(PlatformLogger.Level.FINER)) {
log.finer("onIMDispatchEvent(event={0}): this={1}.", event, this);
}
final int eventId = event.getID();
if (eventId >= MouseEvent.MOUSE_FIRST && eventId <= MouseEvent.MOUSE_LAST) {
// MouseEvent or MouseWheelEvent
if (!isCaretListenerInstalled || eventId == MouseEvent.MOUSE_WHEEL) {
// We expect no mouse events except MouseWheelEvent can change the physical position of the caret
// without changing its logical position inside the document. The logical position is handled by caretUpdate.
// The event hasn't been handled by the component yet, so the caret position couldn't have been changed yet.
// Hence, we have to postpone the updating request.
EventQueue.invokeLater(this::updateNotify);
}
}
if (eventId >= KeyEvent.KEY_FIRST && eventId <= KeyEvent.KEY_LAST) {
if ( !isCaretListenerInstalled && (!isTextListenerInstalled || eventId != KeyEvent.KEY_TYPED) ) {
EventQueue.invokeLater(this::updateNotify);
}
}
}
/** This method is intended to be called from the owning IM's {@link java.awt.im.spi.InputMethod#notifyClientWindowChange(Rectangle)}. */
public void onIMNotifyClientWindowChange(Rectangle location) {
assert(EventQueue.isDispatchThread());
if (log.isLoggable(PlatformLogger.Level.FINER)) {
log.finer("onIMNotifyClientWindowChange(location={0}): this={1}.", location, this);
}
if (location != null) {
// null means the window has become iconified or invisible, so no need to try to update the caret position.
lastKnownClientWindowBounds = location;
updateNotify();
}
}
// ComponentListener
@Override
public void componentHidden(ComponentEvent e) {}
@Override
public void componentMoved(ComponentEvent e) {
updateNotify();
}
@Override
public void componentResized(ComponentEvent e) {
updateNotify();
}
@Override
public void componentShown(ComponentEvent e) {
updateNotify();
}
// CaretListener
@Override
public void caretUpdate(CaretEvent e) {
updateNotify();
}
// TextListener
@Override
public void textValueChanged(TextEvent e) {
updateNotify();
}
/* java.lang.Object methods section */
@Override
public String toString() {
final StringBuilder sb = new StringBuilder(1024);
sb.append("ClientComponentCaretPositionTracker@").append(System.identityHashCode(this));
sb.append('{');
sb.append("isCaretListenerInstalled=").append(isCaretListenerInstalled);
sb.append(", isTextListenerInstalled=").append(isTextListenerInstalled);
sb.append(", lastKnownClientWindowBounds=").append(lastKnownClientWindowBounds);
sb.append(", updatesAreDeferred=").append(updatesAreDeferred);
sb.append(", hasDeferredUpdates=").append(hasDeferredUpdates);
sb.append(", trackedComponent=").append(trackedComponent == null ? "null" : trackedComponent.get());
sb.append('}');
return sb.toString();
}
/* Implementation details */
private final WeakReference<WLInputMethodZwpTextInputV3> im;
private WeakReference<Component> trackedComponent = null;
private boolean isCaretListenerInstalled = false;
private boolean isTextListenerInstalled = false;
private Rectangle lastKnownClientWindowBounds = null;
private boolean updatesAreDeferred = false;
private boolean hasDeferredUpdates = false;
private WLInputMethodZwpTextInputV3 getOwnerIm() {
assert(EventQueue.isDispatchThread());
final WLInputMethodZwpTextInputV3 thisImStrong;
if (this.im == null) {
thisImStrong = null;
} else {
thisImStrong = this.im.get();
}
if (thisImStrong == null) {
stopTrackingCurrentComponent();
}
return thisImStrong;
}
private void updateNotify() {
if (log.isLoggable(PlatformLogger.Level.FINEST)) {
log.finest(String.format("updateNotify(): this=%s.", this), new Throwable("Stacktrace"));
}
if (getTrackedComponentIfTracking() == null) return;
if (updatesAreDeferred) {
hasDeferredUpdates = true;
return;
}
hasDeferredUpdates = false;
final var imToNotify = getOwnerIm();
if (imToNotify != null) {
imToNotify.wlUpdateCursorRectangle(false);
}
}
}

View File

@@ -1,58 +0,0 @@
/*
* Copyright 2025 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.im.text_input_unstable_v3;
/** Content hint is a bitmask to allow to modify the behavior of the text input */
enum ContentHint {
/** no special behavior */
NONE (0x0),
/** suggest word completions */
COMPLETION (0x1),
/** suggest word corrections */
SPELLCHECK (0x2),
/** switch to uppercase letters at the start of a sentence */
AUTO_CAPITALIZATION(0x4),
/** prefer lowercase letters */
LOWERCASE (0x8),
/** prefer uppercase letters */
UPPERCASE (0x10),
/** prefer casing for titles and headings (can be language dependent) */
TITLECASE (0x20),
/** characters should be hidden */
HIDDEN_TEXT (0x40),
/** typed text should not be stored */
SENSITIVE_DATA (0x80),
/** just Latin characters should be entered */
LATIN (0x100),
/** the text input is multiline */
MULTILINE (0x200);
public final int intMask;
ContentHint(int intMask) {
this.intMask = intMask;
}
}

View File

@@ -1,67 +0,0 @@
/*
* Copyright 2025 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.im.text_input_unstable_v3;
/**
* The content purpose allows to specify the primary purpose of a text input.
* This allows an input method to show special purpose input panels with extra characters or to disallow some characters.
*/
enum ContentPurpose {
/** default input, allowing all characters */
NORMAL (0),
/** allow only alphabetic characters */
ALPHA (1),
/** allow only digits */
DIGITS (2),
/** input a number (including decimal separator and sign) */
NUMBER (3),
/** input a phone number */
PHONE (4),
/** input an URL */
URL (5),
/** input an email address */
EMAIL (6),
/** input a name of a person */
NAME (7),
/** input a password (combine with sensitive_data hint) */
PASSWORD(8),
/** input is a numeric password (combine with sensitive_data hint) */
PIN (9),
/** input a date */
DATE (10),
/** input a time */
TIME (11),
/** input a date and time */
DATETIME(12),
/** input for a terminal */
TERMINAL(13);
public final int intValue;
ContentPurpose(int intValue) {
this.intValue = intValue;
}
}

View File

@@ -1,131 +0,0 @@
/*
* Copyright 2025 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.im.text_input_unstable_v3;
import java.util.Arrays;
import java.util.Objects;
/**
* This class accumulates changes received as
* {@code zwp_text_input_v3::preedit_string}, {@code zwp_text_input_v3::commit_string} events until
* a {@code zwp_text_input_v3::done} event is received.
*/
final class IncomingChanges
{
public IncomingChanges updatePreeditString(byte[] newPreeditStringUtf8, int newPreeditStringCursorBeginUtf8Byte, int newPreeditStringCursorEndUtf8Byte) {
this.doUpdatePreeditString = true;
this.newPreeditStringUtf8 = newPreeditStringUtf8;
this.newPreeditStringCursorBeginUtf8Byte = newPreeditStringCursorBeginUtf8Byte;
this.newPreeditStringCursorEndUtf8Byte = newPreeditStringCursorEndUtf8Byte;
this.cachedResultPreeditString = null;
return this;
}
/**
* @return {@code null} if there are no changes in the preedit string
* (i.e. {@link #updatePreeditString(byte[], int, int)} hasn't been called);
* an instance of JavaPreeditString otherwise.
* @see JavaPreeditString
*/
public JavaPreeditString getPreeditString() {
if (cachedResultPreeditString != null) {
return cachedResultPreeditString;
}
cachedResultPreeditString = doUpdatePreeditString
? JavaPreeditString.fromWaylandPreeditString(newPreeditStringUtf8, newPreeditStringCursorBeginUtf8Byte, newPreeditStringCursorEndUtf8Byte)
: null;
return cachedResultPreeditString;
}
public IncomingChanges updateCommitString(byte[] newCommitStringUtf8) {
this.doUpdateCommitString = true;
this.newCommitStringUtf8 = newCommitStringUtf8;
this.cachedResultCommitString = null;
return this;
}
/**
* @return {@code null} if there are no changes in the commit string
* (i.e. {@link #updateCommitString(byte[])} hasn't been called);
* an instance of JavaCommitString otherwise.
* @see JavaCommitString
*/
public JavaCommitString getCommitString() {
if (cachedResultCommitString != null) {
return cachedResultCommitString;
}
cachedResultCommitString = doUpdateCommitString
? JavaCommitString.fromWaylandCommitString(newCommitStringUtf8)
: null;
return cachedResultCommitString;
}
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
IncomingChanges that = (IncomingChanges) o;
return doUpdatePreeditString == that.doUpdatePreeditString &&
newPreeditStringCursorBeginUtf8Byte == that.newPreeditStringCursorBeginUtf8Byte &&
newPreeditStringCursorEndUtf8Byte == that.newPreeditStringCursorEndUtf8Byte &&
doUpdateCommitString == that.doUpdateCommitString &&
Arrays.equals(newPreeditStringUtf8, that.newPreeditStringUtf8) &&
Arrays.equals(newCommitStringUtf8, that.newCommitStringUtf8);
}
@Override
public int hashCode() {
return Objects.hash(
doUpdatePreeditString,
Arrays.hashCode(newPreeditStringUtf8),
newPreeditStringCursorBeginUtf8Byte,
newPreeditStringCursorEndUtf8Byte,
doUpdateCommitString,
Arrays.hashCode(newCommitStringUtf8)
);
}
// zwp_text_input_v3::preedit_string
private boolean doUpdatePreeditString = false;
private byte[] newPreeditStringUtf8 = null;
private int newPreeditStringCursorBeginUtf8Byte = 0;
private int newPreeditStringCursorEndUtf8Byte = 0;
private JavaPreeditString cachedResultPreeditString = null;
// zwp_text_input_v3::commit_string
private boolean doUpdateCommitString = false;
private byte[] newCommitStringUtf8 = null;
private JavaCommitString cachedResultCommitString = null;
}

View File

@@ -1,199 +0,0 @@
/*
* Copyright 2025 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.im.text_input_unstable_v3;
import java.awt.Rectangle;
import java.util.Objects;
/**
* This class encapsulates the entire state of an input context represented by an instance of {@code zwp_text_input_v3}.
*
* @see StateOfEnabled
*/
final class InputContextState {
/** pointer to a native context {@code zwp_text_input_v3} */
public final long nativeContextPtr;
public InputContextState(long nativeContextPtr) {
assert(nativeContextPtr != 0);
this.nativeContextPtr = nativeContextPtr;
}
/** @return 0 if the input context hasn't entered a surface yet. Otherwise, the native pointer to the surface. */
public long getCurrentWlSurfacePtr() {
return currentWlSurfacePtr;
}
public void setCurrentWlSurfacePtr(long currentWlSurfacePtr) {
this.currentWlSurfacePtr = currentWlSurfacePtr;
}
/**
* Notifies the InputContext that a set of changes has been sent and committed to the compositor
* via a {@code zwp_text_input_v3::commit} request. The InputContext reacts by incrementing its commit counter.
*
* @param changes represents the set of changes that have been sent and followed by a 'commit' request.
* Must not be {@code null} (but can be empty, which means only the 'commit' request has been issued).
*
* @return a new instance of {@link OutgoingBeingCommittedChanges} consisting of
* the passed changes and the new value of the commit counter.
*
* @throws NullPointerException if {@code changes} is {@code null}.
*
* @see OutgoingChanges
*/
public OutgoingBeingCommittedChanges syncWithCommittedOutgoingChanges(final OutgoingChanges changes) {
Objects.requireNonNull(changes, "changes");
// zwp_text_input_v3::done natively uses uint32_t for the serial,
// so it can't get greater than 0xFFFFFFFF.
this.commitCounter = (this.commitCounter + 1) % 0x100000000L;
return new OutgoingBeingCommittedChanges(changes, this.commitCounter);
}
public long getCommitCounter() {
return commitCounter;
}
/**
* This class represents the extended state of an {@code InputContextState} that only exists when the context
* is enabled.
*
* @param textChangeCause the property set via a {@code zwp_text_input_v3::set_text_change_cause} request. Must not be {@code null}.
* @param contentHint the property set via a {@code zwp_text_input_v3::set_content_type} request.
* @param contentPurpose the property set via a {@code zwp_text_input_v3::set_content_type} request. Must not be {@code null}.
* @param cursorRectangle the property set via a {@code zwp_text_input_v3::set_cursor_rectangle} request.
* {@code null} means "the text input does not support describing the cursor area".
*/
public record StateOfEnabled(
// zwp_text_input_v3::set_text_change_cause
ChangeCause textChangeCause,
// zwp_text_input_v3::set_content_type.hint
int contentHint,
// zwp_text_input_v3::set_content_type.purpose
ContentPurpose contentPurpose,
// zwp_text_input_v3::set_cursor_rectangle
Rectangle cursorRectangle
) {
public StateOfEnabled {
Objects.requireNonNull(textChangeCause, "textChangeCause");
Objects.requireNonNull(contentPurpose, "contentPurpose");
}
}
public StateOfEnabled getCurrentStateOfEnabled() {
return stateOfEnabled;
}
public boolean isEnabled() {
return getCurrentStateOfEnabled() != null;
}
/**
* NB: if you want to call setEnabledState(null), consider using {@code wlHandleContextGotDisabled()} of
* the owning {@link WLInputMethodZwpTextInputV3}.
*
* @param newState {@code null} to mark the InputContext as disabled,
* otherwise the InputContext will be marked as enabled and having the state as
* specified in the parameter.
*/
public void setEnabledState(StateOfEnabled newState) {
this.stateOfEnabled = newState;
}
public void syncWithAppliedIncomingChanges(final JavaPreeditString appliedPreeditString, final JavaCommitString appliedCommitString, final long doneSerial) {
this.latestAppliedPreeditString = Objects.requireNonNull(appliedPreeditString, "appliedPreeditString");
this.latestAppliedCommitString = Objects.requireNonNull(appliedCommitString, "appliedCommitString");
this.latestDoneSerial = doneSerial;
}
public JavaPreeditString getLatestAppliedPreeditString() {
return latestAppliedPreeditString;
}
public JavaCommitString getLatestAppliedCommitString() {
return latestAppliedCommitString;
}
public long getLatestDoneSerial() {
return latestDoneSerial;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder(512);
sb.append("InputContextState@").append(System.identityHashCode(this));
sb.append('{');
sb.append("nativeContextPtr=0x").append(Long.toHexString(nativeContextPtr));
sb.append(", currentWlSurfacePtr=0x").append(Long.toHexString(currentWlSurfacePtr));
sb.append(", commitCounter=").append(commitCounter);
sb.append(", latestDoneSerial=").append(latestDoneSerial);
sb.append(", stateOfEnabled=").append(stateOfEnabled);
sb.append(", latestAppliedPreeditString=").append(latestAppliedPreeditString);
sb.append(", latestAppliedCommitString=").append(latestAppliedCommitString);
sb.append('}');
return sb.toString();
}
// zwp_text_input_v3::enter.surface / zwp_text_input_v3::leave.surface
private long currentWlSurfacePtr = 0;
// zwp_text_input_v3::commit
/**
* How many times changes to this context have been committed (through {@code zwp_text_input_v3::commit}).
* Essentially, it means the most actual version of the context's state.
*/
private long commitCounter = 0;
// zwp_text_input_v3::done.serial
/**
* The {@code serial} parameter of the latest {@code zwp_text_input_v3::done} event received.
* Essentially, it means the latest version of the context's state known/confirmed by the compositor.
*/
private long latestDoneSerial = 0;
/** {@code null} if the InputContextState is disabled. */
private StateOfEnabled stateOfEnabled = null;
/**
* The latest preedit string applied as a result of the latest {@code zwp_text_input_v3::done} event received.
* Must never be {@code null} ; if a {@code zwp_text_input_v3::done} event wasn't preceded by a
* {@code zwp_text_input_v3::preedit_string} event, the field should be set to {@link PropertiesInitials#PREEDIT_STRING}.
*/
private JavaPreeditString latestAppliedPreeditString = PropertiesInitials.PREEDIT_STRING;
/**
* The latest commit string applied as a result of the latest {@code zwp_text_input_v3::done} event received.
* Must never be {@code null} ; if a {@code zwp_text_input_v3::done} event wasn't preceded by a
* {@code zwp_text_input_v3::commit_string} event, the field should be set to {@link PropertiesInitials#COMMIT_STRING}.
*/
private JavaCommitString latestAppliedCommitString = PropertiesInitials.COMMIT_STRING;
}

View File

@@ -1,41 +0,0 @@
/*
* Copyright 2025 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.im.text_input_unstable_v3;
import java.util.Objects;
record JavaCommitString(String text) {
public JavaCommitString {
Objects.requireNonNull(text, "text");
}
public static final JavaCommitString EMPTY = new JavaCommitString("");
/** Never returns {@code null}. */
public static JavaCommitString fromWaylandCommitString(byte[] utf8Bytes) {
return new JavaCommitString(Utilities.utf8BytesToJavaString(utf8Bytes));
}
}

View File

@@ -1,105 +0,0 @@
/*
* Copyright 2025 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.im.text_input_unstable_v3;
import java.util.Objects;
/**
* This class represents the result of a conversion of a UTF-8 preedit string received in a
* {@code zwp_text_input_v3::preedit_string} event to a Java UTF-16 string.
* If {@link #cursorBeginCodeUnit} and/or {@link #cursorEndCodeUnit} point at UTF-16 surrogate pairs,
* they're guaranteed to point at the very beginning of them as long as {@link #fromWaylandPreeditString} is
* used to perform the conversion.
* <p>
* {@link #fromWaylandPreeditString} never returns {@code null}.
* <p>
* See the specification of {@code zwp_text_input_v3::preedit_string} event for more info about
* cursor_begin, cursor_end values.
*
* @param text The preedit text string. Mustn't be {@code null} (use an empty string instead).
* @param cursorBeginCodeUnit UTF-16 equivalent of {@code preedit_string.cursor_begin}.
* @param cursorEndCodeUnit UTF-16 equivalent of {@code preedit_string.cursor_end}.
* It's not explicitly stated in the protocol specification, but it seems to be a valid
* situation when cursor_end < cursor_begin, which means
* the highlight extends to the right from the caret
* (e.g., when the text gets selected with Shift + Left Arrow).
*/
record JavaPreeditString(String text, int cursorBeginCodeUnit, int cursorEndCodeUnit) {
public JavaPreeditString {
Objects.requireNonNull(text, "text");
}
public static final JavaPreeditString EMPTY = new JavaPreeditString("", 0, 0);
public static final JavaPreeditString EMPTY_NO_CARET = new JavaPreeditString("", -1, -1);
public static JavaPreeditString fromWaylandPreeditString(
final byte[] utf8Bytes,
final int cursorBeginUtf8Byte,
final int cursorEndUtf8Byte
) {
// Java's UTF-8 -> UTF-16 conversion doesn't like trailing NUL codepoints, so let's trim them
final int utf8BytesWithoutNulLength = Utilities.getLengthOfUtf8BytesWithoutTrailingNULs(utf8Bytes);
// cursorBeginUtf8Byte, cursorEndUtf8Byte normalized relatively to the valid values range.
final int fixedCursorBeginUtf8Byte;
final int fixedCursorEndUtf8Byte;
if (cursorBeginUtf8Byte < 0 || cursorEndUtf8Byte < 0) {
fixedCursorBeginUtf8Byte = fixedCursorEndUtf8Byte = -1;
} else {
// 0 <= cursorBeginUtf8Byte <= fixedCursorBeginUtf8Byte <= utf8BytesWithoutNulLength
fixedCursorBeginUtf8Byte = Math.min(cursorBeginUtf8Byte, utf8BytesWithoutNulLength);
// 0 <= cursorEndUtf8Byte <= fixedCursorEndUtf8Byte <= utf8BytesWithoutNulLength
fixedCursorEndUtf8Byte = Math.min(cursorEndUtf8Byte, utf8BytesWithoutNulLength);
}
final var resultText = Utilities.utf8BytesToJavaString(utf8Bytes, 0, utf8BytesWithoutNulLength);
if (fixedCursorBeginUtf8Byte < 0 || fixedCursorEndUtf8Byte < 0) {
return new JavaPreeditString(resultText, -1, -1);
}
if (resultText == null) {
assert(fixedCursorBeginUtf8Byte == 0);
assert(fixedCursorEndUtf8Byte == 0);
return JavaPreeditString.EMPTY;
}
final String javaPrefixBeforeCursorBegin = (fixedCursorBeginUtf8Byte == 0)
? ""
: Utilities.utf8BytesToJavaString(utf8Bytes, 0, fixedCursorBeginUtf8Byte);
final String javaPrefixBeforeCursorEnd = (fixedCursorEndUtf8Byte == 0)
? ""
: Utilities.utf8BytesToJavaString(utf8Bytes, 0, fixedCursorEndUtf8Byte);
return new JavaPreeditString(
resultText,
javaPrefixBeforeCursorBegin.length(),
javaPrefixBeforeCursorEnd.length()
);
}
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright 2025 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.im.text_input_unstable_v3;
import java.util.Objects;
/**
* This class is intended to keep changes after they get committed and until they actually get applied.
*
* @param changeSet changes that have been sent and committed to the compositor,
* but not yet confirmed by it (via a {@code zwp_text_input_v3::done} event).
* Must not be {@code null}.
* @param commitCounter the number of times a {@code zwp_text_input_v3::commit} request has been issued to
* the corresponding {@link InputContextState}.
*
* @see OutgoingChanges
*/
record OutgoingBeingCommittedChanges(OutgoingChanges changeSet, long commitCounter) {
public OutgoingBeingCommittedChanges {
Objects.requireNonNull(changeSet, "changeSet");
}
}

View File

@@ -1,166 +0,0 @@
/*
* Copyright 2025 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.im.text_input_unstable_v3;
import java.awt.Rectangle;
import java.util.Objects;
/**
* This class is intended to accumulate changes for an {@link InputContextState} until
* they're sent via the set of requests
* {@code zwp_text_input_v3::enable}, {@code zwp_text_input_v3::disable}, {@code zwp_text_input_v3::set_*}
* and commited via {@code zwp_text_input_v3::commit}.
* <p>
* The reason of having to accumulate changes instead of applying them as soon as they appear is the following
* part of the {@code zpw_text_input_v3::done(serial)} event specification:
* {@code
* When the client receives a done event with a serial different than the number of past commit requests,
* it must proceed with evaluating and applying the changes as normal, except it should not change the
* current state of the zwp_text_input_v3 object. All pending state requests [...]
* on the zwp_text_input_v3 object should be sent and committed after receiving a
* zwp_text_input_v3.done event with a matching serial.
* }
*<p>
* All the properties this class includes are nullable where {@code null} means absence of this property change.
* In other words, if a property is null, the corresponding {@code zwp_text_input_v3::set_...} shouldn't be
* called when processing this instance of OutgoingChanges.
* <p>
* The modifier methods return {@code this} for method chaining.
*/
final class OutgoingChanges
{
// zwp_text_input_v3::enable / zwp_text_input_v3::disable
private Boolean newEnabled = null;
// zwp_text_input_v3::set_text_change_cause
private ChangeCause newTextChangeCause = null;
// zwp_text_input_v3::set_content_type
private Integer newContentTypeHint = null;
private ContentPurpose newContentTypePurpose = null;
// zwp_text_input_v3::set_cursor_rectangle
private Rectangle newCursorRectangle = null;
@Override
public String toString() {
final StringBuilder sb = new StringBuilder(256);
sb.append("OutgoingChanges@").append(System.identityHashCode(this));
sb.append('[');
sb.append("newEnabled=").append(newEnabled);
sb.append(", newTextChangeCause=").append(newTextChangeCause);
sb.append(", newContentTypeHint=").append(newContentTypeHint);
sb.append(", newContentTypePurpose=").append(newContentTypePurpose);
sb.append(", newCursorRectangle=").append(newCursorRectangle);
sb.append(']');
return sb.toString();
}
public OutgoingChanges setEnabledState(Boolean newEnabled) {
this.newEnabled = newEnabled;
return this;
}
public Boolean getEnabledState() { return newEnabled; }
public OutgoingChanges setTextChangeCause(ChangeCause newTextChangeCause) {
this.newTextChangeCause = newTextChangeCause;
return this;
}
public ChangeCause getTextChangeCause() { return newTextChangeCause; }
/**
* Both parameters have to be {@code null} or not null simultaneously.
*
* @throws NullPointerException if one of the parameters is {@code null} while the other one is not.
*/
public OutgoingChanges setContentType(Integer newContentTypeHint, ContentPurpose newContentTypePurpose) {
if (newContentTypeHint == null && newContentTypePurpose == null) {
this.newContentTypeHint = null;
this.newContentTypePurpose = null;
} else {
final var contentHintAllMask =
ContentHint.NONE.intMask |
ContentHint.COMPLETION.intMask |
ContentHint.SPELLCHECK.intMask |
ContentHint.AUTO_CAPITALIZATION.intMask |
ContentHint.LOWERCASE.intMask |
ContentHint.UPPERCASE.intMask |
ContentHint.TITLECASE.intMask |
ContentHint.HIDDEN_TEXT.intMask |
ContentHint.SENSITIVE_DATA.intMask |
ContentHint.LATIN.intMask |
ContentHint.MULTILINE.intMask;
if ( (Objects.requireNonNull(newContentTypeHint, "newContentTypeHint") & ~contentHintAllMask) != 0 ) {
throw new IllegalArgumentException(String.format("newContentTypeHint=%d has invalid bits set", newContentTypeHint));
}
this.newContentTypeHint = newContentTypeHint;
this.newContentTypePurpose = Objects.requireNonNull(newContentTypePurpose, "newContentTypePurpose");
}
return this;
}
public Integer getContentTypeHint() { return newContentTypeHint; }
public ContentPurpose getContentTypePurpose() { return newContentTypePurpose; }
public OutgoingChanges setCursorRectangle(Rectangle newCursorRectangle) {
this.newCursorRectangle = newCursorRectangle;
return this;
}
public Rectangle getCursorRectangle() { return newCursorRectangle; }
public OutgoingChanges appendChangesFrom(OutgoingChanges src) {
if (src == null) return this;
if (getTextChangeCause() == null) {
setTextChangeCause(src.getTextChangeCause());
}
if (getContentTypeHint() == null) {
setContentType(src.getContentTypeHint(), src.getContentTypePurpose());
}
if (getCursorRectangle() == null) {
setCursorRectangle(src.getCursorRectangle());
}
return this;
}
public boolean isEmpty() {
return (getEnabledState() == null && getTextChangeCause() == null && getContentTypeHint() == null && getCursorRectangle() == null);
}
}

View File

@@ -1,50 +0,0 @@
/*
* Copyright 2025 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.im.text_input_unstable_v3;
import java.awt.Rectangle;
interface PropertiesInitials {
/** {@code zwp_text_input_v3::set_text_change_cause} */
ChangeCause TEXT_CHANGE_CAUSE = ChangeCause.INPUT_METHOD;
/** {@code zwp_text_input_v3::set_content_type} (hint) */
int CONTENT_HINT = ContentHint.NONE.intMask;
/** {@code zwp_text_input_v3::set_content_type} (purpose) */
ContentPurpose CONTENT_PURPOSE = ContentPurpose.NORMAL;
/**
* {@code zwp_text_input_v3::set_cursor_rectangle}.
* <p>
* The initial values describing a cursor rectangle are empty.
* That means the text input does not support describing the cursor area.
* If the empty values get applied, subsequent attempts to change them may have no effect.
*/
Rectangle CURSOR_RECTANGLE = null;
/** {@code zwp_text_input_v3::preedit_string} */
JavaPreeditString PREEDIT_STRING = new JavaPreeditString("", 0, 0);
/** {@code zwp_text_input_v3::commit_string} */
JavaCommitString COMMIT_STRING = new JavaCommitString("");
}

View File

@@ -1,59 +0,0 @@
/*
* Copyright 2025 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.im.text_input_unstable_v3;
import java.nio.charset.StandardCharsets;
interface Utilities {
static int getLengthOfUtf8BytesWithoutTrailingNULs(final byte[] utf8Bytes) {
int lastNonNulIndex = (utf8Bytes == null) ? -1 : utf8Bytes.length - 1;
for (; lastNonNulIndex >= 0; --lastNonNulIndex) {
if (utf8Bytes[lastNonNulIndex] != 0) {
break;
}
}
return (lastNonNulIndex < 0) ? 0 : lastNonNulIndex + 1;
}
static String utf8BytesToJavaString(final byte[] utf8Bytes) {
if (utf8Bytes == null) {
return "";
}
return utf8BytesToJavaString(
utf8Bytes,
0,
// Java's UTF-8 -> UTF-16 conversion doesn't like trailing NUL codepoints, so let's trim them
getLengthOfUtf8BytesWithoutTrailingNULs(utf8Bytes)
);
}
static String utf8BytesToJavaString(final byte[] utf8Bytes, final int offset, final int length) {
return utf8Bytes == null ? "" : new String(utf8Bytes, offset, length, StandardCharsets.UTF_8);
}
}

View File

@@ -1,203 +0,0 @@
/*
* Copyright 2025 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.im.text_input_unstable_v3;
import sun.awt.wl.WLToolkit;
import sun.util.logging.PlatformLogger;
import java.awt.AWTException;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.im.spi.InputMethod;
import java.awt.im.spi.InputMethodDescriptor;
import java.util.Locale;
public final class WLInputMethodDescriptorZwpTextInputV3 implements InputMethodDescriptor {
// See java.text.MessageFormat for the formatting syntax
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.wl.im.text_input_unstable_v3.WLInputMethodDescriptorZwpTextInputV3");
public static boolean isAvailableOnPlatform() {
return initAndGetIsAvailableOnPlatform();
}
public static WLInputMethodDescriptorZwpTextInputV3 getInstanceIfAvailableOnPlatform() {
if (!isAvailableOnPlatform()) {
return null;
}
return new WLInputMethodDescriptorZwpTextInputV3();
}
/* java.awt.im.spi.InputMethodDescriptor methods section */
@Override
public Locale[] getAvailableLocales() throws AWTException {
ensureIsAvailableOnPlatform();
return getAvailableLocalesInternal();
}
@Override
public boolean hasDynamicLocaleList() {
// Since the return value of {@link #getAvailableLocales()} doesn't currently change over time,
// it doesn't make sense to return true here.
return false;
}
@Override
public String getInputMethodDisplayName(Locale inputLocale, Locale displayLanguage) {
assert isAvailableOnPlatform();
// This is how it's implemented in all other Toolkits.
//
// We ignore the input locale.
// When displaying for the default locale, rely on the localized AWT properties;
// for any other locale, fall back to English.
String name = "System Input Methods";
if (Locale.getDefault().equals(displayLanguage)) {
name = Toolkit.getProperty("AWT.HostInputMethodDisplayName", name);
}
return name;
}
@Override
public Image getInputMethodIcon(Locale inputLocale) {
return null;
}
@Override
public InputMethod createInputMethod() throws Exception {
// NB: we should avoid returning null from this method because the calling code isn't really ready to get null
ensureIsAvailableOnPlatform();
final var result = new WLInputMethodZwpTextInputV3();
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("createInputMethod(): result={0}.", result);
}
return result;
}
/* java.lang.Object methods section */
@Override
public String toString() {
return String.format("WLInputMethodDescriptorZwpTextInputV3@%d", System.identityHashCode(this));
}
/* Implementation details section */
/** Used as the return value for {@link #getAvailableLocales()}. */
private volatile static Locale toolkitStartupLocale = null;
private volatile static Boolean isAvailableOnPlatform = null;
static Locale[] getAvailableLocalesInternal() {
// This is how it's implemented in XToolkit.
//
// A better implementation would be obtaining all currently installed and enabled input sources
// (like on GNOME Settings -> Keyboard -> Input Sources) and mapping them to locales.
// However, there seem no universal Wayland API for that, so it seems can't be implemented reliably.
//
// So leaving as is at the moment.
//
// TODO: research how to implement this better along with {@link #hasDynamicLocaleList}
return new Locale[]{ (Locale)initAndGetToolkitStartupLocale().clone() };
}
private static Locale initAndGetToolkitStartupLocale() {
if (toolkitStartupLocale == null) {
synchronized (WLInputMethodDescriptorZwpTextInputV3.class) {
if (toolkitStartupLocale == null) {
toolkitStartupLocale = WLToolkit.getStartupLocale();
}
}
}
if (log.isLoggable(PlatformLogger.Level.CONFIG)) {
log.config("initAndGetToolkitStartupLocale(): toolkitStartupLocale={0}.", toolkitStartupLocale);
}
return toolkitStartupLocale;
}
private static boolean initAndGetIsAvailableOnPlatform() {
if (isAvailableOnPlatform == null) {
synchronized (WLInputMethodDescriptorZwpTextInputV3.class) {
try {
if (isAvailableOnPlatform == null) {
isAvailableOnPlatform = checkIfAvailableOnPlatform();
}
} catch (Exception err) {
if (log.isLoggable(PlatformLogger.Level.WARNING)) {
log.warning("Failed to check whether the IM protocol is supported on the system", err);
}
isAvailableOnPlatform = false;
}
}
}
if (log.isLoggable(PlatformLogger.Level.CONFIG)) {
log.config("initAndGetIsAvailableOnPlatform(): isAvailableOnPlatform={0}.", isAvailableOnPlatform);
}
return isAvailableOnPlatform;
}
private static void ensureIsAvailableOnPlatform() throws AWTException {
if (!isAvailableOnPlatform()) {
throw new AWTException("sun.awt.wl.im.text_input_unstable_v3.WLInputMethodZwpTextInputV3 is not supported on this system");
}
}
private WLInputMethodDescriptorZwpTextInputV3() {
assert isAvailableOnPlatform();
initAndGetToolkitStartupLocale();
}
/* JNI downcalls section */
/**
* This method checks if {@link WLInputMethodZwpTextInputV3} can function on this system.
* Basically, it means the Wayland compositor supports a minimal sufficient subset of the required protocols
* (currently the set only includes the "text-input-unstable-v3" protocol).
*
* @return true if {@link WLInputMethodZwpTextInputV3} can function on this system, false otherwise.
* @see <a href="https://wayland.app/protocols/text-input-unstable-v3">text-input-unstable-v3</a>
*/
private static native boolean checkIfAvailableOnPlatform();
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
* 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.java2d;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
import sun.awt.X11GraphicsConfig;
import sun.awt.image.SunVolatileImage;
import sun.awt.image.SurfaceManager;
import sun.awt.image.VolatileSurfaceManager;
import sun.java2d.opengl.GLXGraphicsConfig;
import sun.java2d.opengl.GLXVolatileSurfaceManager;
import sun.java2d.vulkan.VKOffscreenGraphicsConfig;
import sun.java2d.vulkan.WLVKGraphicsConfig;
import sun.java2d.vulkan.VKVolatileSurfaceManager;
import sun.java2d.wl.WLVolatileSurfaceManager;
import sun.java2d.x11.X11VolatileSurfaceManager;
import sun.java2d.xr.*;
/**
* The SurfaceManagerFactory that creates VolatileSurfaceManager
* implementations for the Unix volatile images.
*/
public class UnixSurfaceManagerFactory extends SurfaceManagerFactory {
/**
* Creates a new instance of a VolatileSurfaceManager given any
* arbitrary SunVolatileImage. An optional context Object can be supplied
* as a way for the caller to pass pipeline-specific context data to
* the VolatileSurfaceManager (such as a backbuffer handle, for example).
*
* For Unix platforms, this method returns either an X11- or a GLX-
* specific VolatileSurfaceManager based on the GraphicsConfiguration
* under which the SunVolatileImage was created.
*/
public VolatileSurfaceManager createVolatileManager(SunVolatileImage vImg,
Object context)
{
GraphicsConfiguration gc = vImg.getGraphicsConfig();
if (gc instanceof GLXGraphicsConfig) {
return new GLXVolatileSurfaceManager(vImg, context);
} else if(gc instanceof XRGraphicsConfig) {
return new XRVolatileSurfaceManager(vImg, context);
} else if (gc instanceof X11GraphicsConfig){
return new X11VolatileSurfaceManager(vImg, context);
} else if (gc instanceof WLVKGraphicsConfig) {
return new VKVolatileSurfaceManager(vImg, context);
} else if (gc instanceof VKOffscreenGraphicsConfig) {
return new VKVolatileSurfaceManager(vImg, context);
} else {
return new WLVolatileSurfaceManager(vImg, context);
}
}
@Override
public SurfaceManager createTextureWrapperSurfaceManager(GraphicsConfiguration gc, Image image, long texture) {
throw new UnsupportedOperationException();
}
}

View File

@@ -49,7 +49,6 @@ import sun.awt.X11GraphicsEnvironment;
import sun.awt.image.OffScreenImage;
import sun.awt.image.SunVolatileImage;
import sun.awt.image.SurfaceManager;
import sun.awt.image.VolatileSurfaceManager;
import sun.java2d.SunGraphics2D;
import sun.java2d.Surface;
import sun.java2d.SurfaceData;
@@ -73,8 +72,7 @@ public final class GLXGraphicsConfig
private long pConfigInfo;
private ContextCapabilities oglCaps;
private final OGLContext context;
private final SurfaceManager.ProxyCache surfaceDataProxyCache =
new SurfaceManager.ProxyCache();
private final SurfaceManager.ProxyCache surfaceDataProxyCache = new SurfaceManager.ProxyCache();
private static native long getGLXConfigInfo(int screennum, int visualnum);
private static native int getOGLCapabilities(long configInfo);
@@ -415,10 +413,4 @@ public final class GLXGraphicsConfig
public ContextCapabilities getContextCapabilities() {
return oglCaps;
}
@Override
public VolatileSurfaceManager createVolatileManager(SunVolatileImage image,
Object context) {
return new GLXVolatileSurfaceManager(image, context);
}
}

View File

@@ -47,8 +47,11 @@ public final class WLVKGraphicsConfig extends WLGraphicsConfig
private final VKGraphicsConfig offscreenConfig;
public WLVKGraphicsConfig(VKGraphicsConfig offscreenConfig, WLGraphicsDevice device) {
super(device);
public WLVKGraphicsConfig(VKGraphicsConfig offscreenConfig, WLGraphicsDevice device,
int x, int y, int xLogical, int yLogical,
int width, int height, int widthLogical, int heightLogical,
int scale) {
super(device, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale);
this.offscreenConfig = offscreenConfig;
}
@@ -62,8 +65,11 @@ public final class WLVKGraphicsConfig extends WLGraphicsConfig
return getEffectiveScale();
}
public static WLVKGraphicsConfig getConfig(VKGraphicsConfig offscreenConfig, WLGraphicsDevice device) {
return new WLVKGraphicsConfig(offscreenConfig, device);
public static WLVKGraphicsConfig getConfig(VKGraphicsConfig offscreenConfig, WLGraphicsDevice device,
int x, int y, int xLogical, int yLogical,
int width, int height, int widthLogical, int heightLogical,
int scale) {
return new WLVKGraphicsConfig(offscreenConfig, device, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale);
}
@Override

View File

@@ -30,14 +30,11 @@ import sun.awt.X11GraphicsConfig;
import sun.awt.X11GraphicsDevice;
import sun.awt.X11GraphicsEnvironment;
import sun.awt.image.SurfaceManager;
import sun.awt.image.SunVolatileImage;
import sun.awt.image.VolatileSurfaceManager;
import sun.java2d.SurfaceData;
public class XRGraphicsConfig extends X11GraphicsConfig implements
SurfaceManager.ProxiedGraphicsConfig {
private final SurfaceManager.ProxyCache surfaceDataProxyCache =
new SurfaceManager.ProxyCache();
private final SurfaceManager.ProxyCache surfaceDataProxyCache = new SurfaceManager.ProxyCache();
private XRGraphicsConfig(X11GraphicsDevice device, int visualnum,
int depth, int colormap, boolean doubleBuffer) {
@@ -62,10 +59,4 @@ public class XRGraphicsConfig extends X11GraphicsConfig implements
public SurfaceManager.ProxyCache getSurfaceDataProxyCache() {
return surfaceDataProxyCache;
}
@Override
public VolatileSurfaceManager createVolatileManager(SunVolatileImage image,
Object context) {
return new XRVolatileSurfaceManager(image, context);
}
}

View File

@@ -643,19 +643,8 @@ JNIEXPORT void JNICALL Java_sun_awt_wl_GtkFrameDecoration_nativePaintTitleBar
jint pixel_width = ceil(width * scale);
jint pixel_height = ceil(height * scale);
jboolean is_copy = JNI_FALSE;
const char *title_c_str = "";
if (title) {
title_c_str = JNU_GetStringPlatformChars(env, title, &is_copy);
if (!title_c_str)
return;
}
unsigned char *buffer = (*env)->GetPrimitiveArrayCritical(env, dest, 0);
if (!buffer) {
if (is_copy) {
JNU_ReleaseStringPlatformChars(env, title, title_c_str);
}
JNU_ThrowOutOfMemoryError(env, "Could not get image buffer");
return;
}
@@ -671,19 +660,25 @@ JNIEXPORT void JNICALL Java_sun_awt_wl_GtkFrameDecoration_nativePaintTitleBar
cairo_t *cr = p_cairo_create(surface);
jboolean is_copy = JNI_FALSE;
const char *title_c_str = "";
if (title) {
title_c_str = JNU_GetStringPlatformChars(env, title, &is_copy);
if (!title_c_str)
return;
}
draw_title_bar(decor, surface, cr, width, height, scale, title_c_str, buttonsState);
if (is_copy) {
JNU_ReleaseStringPlatformChars(env, title, title_c_str);
}
// Make sure pixels have been flush into the underlying buffer
p_cairo_surface_flush(surface);
p_gdk_threads_leave();
(*env)->ReleasePrimitiveArrayCritical(env, dest, buffer, 0);
if (is_copy) {
JNU_ReleaseStringPlatformChars(env, title, title_c_str);
}
p_cairo_destroy(cr);
p_cairo_surface_destroy(surface);
}

View File

@@ -34,6 +34,7 @@
#include "sun_awt_wl_WLDataOffer.h"
#include "wayland-client-protocol.h"
// Types
enum DataTransferProtocol
@@ -270,6 +271,9 @@ DataSource_setDnDActions(const struct DataSource *source, uint32_t actions);
static struct DataOffer *
DataOffer_create(struct DataDevice *dataDevice, enum DataTransferProtocol protocol, void *waylandObject);
static void
DataOffer_destroy(struct DataOffer *offer);
static void
DataOffer_receive(struct DataOffer *offer, const char *mime, int fd);
@@ -330,7 +334,7 @@ DataOffer_create(struct DataDevice *dataDevice, enum DataTransferProtocol protoc
return NULL;
}
// Cleared in DataOffer.destroy()
// Cleared in DataOffer_destroy
jobject globalRef = (*env)->NewGlobalRef(env, obj);
EXCEPTION_CLEAR(env);
@@ -341,23 +345,50 @@ DataOffer_create(struct DataDevice *dataDevice, enum DataTransferProtocol protoc
}
offer->javaObject = globalRef;
offer->protocol = protocol;
if (protocol == DATA_TRANSFER_PROTOCOL_WAYLAND) {
struct wl_data_offer *wlDataOffer = waylandObject;
offer->wlDataOffer = wlDataOffer;
wl_data_offer_add_listener(wlDataOffer, &wlDataOfferListener, offer);
offer->wlDataOffer = wlDataOffer;
offer->protocol = DATA_TRANSFER_PROTOCOL_WAYLAND;
}
if (protocol == DATA_TRANSFER_PROTOCOL_PRIMARY_SELECTION) {
struct zwp_primary_selection_offer_v1 *zwpPrimarySelectionOffer = waylandObject;
offer->zwpPrimarySelectionOffer = zwpPrimarySelectionOffer;
zwp_primary_selection_offer_v1_add_listener(zwpPrimarySelectionOffer, &zwpPrimarySelectionOfferListener, offer);
offer->zwpPrimarySelectionOffer = zwpPrimarySelectionOffer;
offer->protocol = DATA_TRANSFER_PROTOCOL_PRIMARY_SELECTION;
}
return offer;
}
static void
DataOffer_destroy(struct DataOffer *offer)
{
if (offer == NULL) {
return;
}
if (offer->javaObject != NULL) {
JNIEnv *env = getEnv();
assert(env != NULL);
(*env)->DeleteGlobalRef(env, offer->javaObject);
offer->javaObject = NULL;
}
if (offer->protocol == DATA_TRANSFER_PROTOCOL_WAYLAND) {
wl_data_offer_destroy(offer->wlDataOffer);
} else if (offer->protocol == DATA_TRANSFER_PROTOCOL_PRIMARY_SELECTION) {
zwp_primary_selection_offer_v1_destroy(offer->zwpPrimarySelectionOffer);
}
free(offer);
}
static void
DataOffer_receive(struct DataOffer *offer, const char *mime, int fd)
{
@@ -953,53 +984,36 @@ Java_sun_awt_wl_WLDataSource_initNative(JNIEnv *env, jobject javaObject, jlong d
protocol = DATA_TRANSFER_PROTOCOL_WAYLAND;
}
dataSource->protocol = protocol;
if (protocol == DATA_TRANSFER_PROTOCOL_WAYLAND) {
// To avoid race conditions when setting the dispatch queue,
// it's necessary to do this dance with wl_proxy_create_wrapper.
// See the docs for wl_proxy_create_wrapper for more details
struct wl_data_device_manager* dmWrapper = wl_proxy_create_wrapper(wl_ddm);
struct wl_data_source *wlDataSource = NULL;
if (dmWrapper != NULL) {
wl_proxy_set_queue((struct wl_proxy *) dmWrapper, dataDevice->dataSourceQueue);
wlDataSource = wl_data_device_manager_create_data_source(dmWrapper);
wl_proxy_wrapper_destroy(dmWrapper);
}
struct wl_data_source *wlDataSource = wl_data_device_manager_create_data_source(wl_ddm);
if (wlDataSource == NULL) {
free(dataSource);
JNU_ThrowByName(env, "java/awt/AWTError", "Wayland error creating wl_data_source proxy");
return 0;
}
dataSource->wlDataSource = wlDataSource;
wl_proxy_set_queue((struct wl_proxy *) wlDataSource, dataDevice->dataSourceQueue);
wl_data_source_add_listener(wlDataSource, &wl_data_source_listener, dataSource);
dataSource->protocol = DATA_TRANSFER_PROTOCOL_WAYLAND;
dataSource->wlDataSource = wlDataSource;
}
if (protocol == DATA_TRANSFER_PROTOCOL_PRIMARY_SELECTION) {
struct zwp_primary_selection_device_manager_v1 *dmWrapper = wl_proxy_create_wrapper(zwp_selection_dm);
struct zwp_primary_selection_source_v1 *zwpPrimarySelectionSource = NULL;
if (dmWrapper != NULL) {
wl_proxy_set_queue((struct wl_proxy *) dmWrapper, dataDevice->dataSourceQueue);
zwpPrimarySelectionSource = zwp_primary_selection_device_manager_v1_create_source(dmWrapper);
wl_proxy_wrapper_destroy(dmWrapper);
}
struct zwp_primary_selection_source_v1 *zwpPrimarySelectionSource =
zwp_primary_selection_device_manager_v1_create_source(zwp_selection_dm);
if (zwpPrimarySelectionSource == NULL) {
free(dataSource);
JNU_ThrowByName(env, "java/awt/AWTError", "Wayland error creating zwp_primary_selection_source_v1 proxy");
return 0;
}
dataSource->zwpPrimarySelectionSource = zwpPrimarySelectionSource;
wl_proxy_set_queue((struct wl_proxy *) zwpPrimarySelectionSource, dataDevice->dataSourceQueue);
zwp_primary_selection_source_v1_add_listener(zwpPrimarySelectionSource,
&zwp_primary_selection_source_v1_listener, dataSource);
dataSource->protocol = DATA_TRANSFER_PROTOCOL_PRIMARY_SELECTION;
dataSource->zwpPrimarySelectionSource = zwpPrimarySelectionSource;
}
return ptr_to_jlong(dataSource);
@@ -1026,6 +1040,11 @@ Java_sun_awt_wl_WLDataSource_destroyImpl(JNIEnv *env, jclass clazz, jlong native
return;
}
if (source->javaObject != NULL) {
(*env)->DeleteGlobalRef(env, source->javaObject);
source->javaObject = NULL;
}
if (source->protocol == DATA_TRANSFER_PROTOCOL_WAYLAND) {
wl_data_source_destroy(source->wlDataSource);
} else if (source->protocol == DATA_TRANSFER_PROTOCOL_PRIMARY_SELECTION) {
@@ -1040,11 +1059,6 @@ Java_sun_awt_wl_WLDataSource_destroyImpl(JNIEnv *env, jclass clazz, jlong native
wl_surface_destroy(source->dragIcon);
}
if (source->javaObject != NULL) {
(*env)->DeleteGlobalRef(env, source->javaObject);
source->javaObject = NULL;
}
free(source);
}
@@ -1059,8 +1073,7 @@ Java_sun_awt_wl_WLDataSource_setDnDActionsImpl(JNIEnv *env,
}
JNIEXPORT void JNICALL Java_sun_awt_wl_WLDataSource_setDnDIconImpl
(JNIEnv * env, jclass clazz, jlong nativePtr, jint scale,
jint width, jint height, jint offsetX, jint offsetY, jintArray pixels)
(JNIEnv * env, jclass clazz, jlong nativePtr, jint width, jint height, jint offsetX, jint offsetY, jintArray pixels)
{
struct DataSource *source = jlong_to_ptr(nativePtr);
@@ -1113,10 +1126,6 @@ JNIEXPORT void JNICALL Java_sun_awt_wl_WLDataSource_setDnDIconImpl
wl_surface_attach(source->dragIcon, source->dragIconBuffer, offsetX, offsetY);
#endif
if (scale >= 1) {
wl_surface_set_buffer_scale(source->dragIcon, scale);
}
wl_surface_damage_buffer(source->dragIcon, 0, 0, width, height);
// NOTE: we still need to commit the surface, this is done immediately after start_drag
@@ -1125,25 +1134,8 @@ JNIEXPORT void JNICALL Java_sun_awt_wl_WLDataSource_setDnDIconImpl
JNIEXPORT void JNICALL
Java_sun_awt_wl_WLDataOffer_destroyImpl(JNIEnv *env, jclass clazz, jlong nativePtr)
{
assert(env != NULL);
struct DataOffer *offer = jlong_to_ptr(nativePtr);
if (offer == NULL) {
return;
}
if (offer->protocol == DATA_TRANSFER_PROTOCOL_WAYLAND) {
wl_data_offer_destroy(offer->wlDataOffer);
} else if (offer->protocol == DATA_TRANSFER_PROTOCOL_PRIMARY_SELECTION) {
zwp_primary_selection_offer_v1_destroy(offer->zwpPrimarySelectionOffer);
}
if (offer->javaObject != NULL) {
(*env)->DeleteGlobalRef(env, offer->javaObject);
offer->javaObject = NULL;
}
free(offer);
DataOffer_destroy(offer);
}
JNIEXPORT void JNICALL

View File

@@ -43,6 +43,8 @@ typedef struct WLOutput {
int32_t x;
int32_t y;
int32_t x_logical;
int32_t y_logical;
int32_t width;
int32_t height;
int32_t width_logical;
@@ -57,8 +59,6 @@ typedef struct WLOutput {
char * make;
char * model;
char * name;
bool offset_known; // whether x and y were set by xdg_output
} WLOutput;
static jclass geClass;
@@ -83,13 +83,10 @@ wl_output_geometry(
{
WLOutput *output = data;
if (!output->offset_known) {
// Ubuntu 22.04 has a bug that prevent Mutter from sending out updates to (x, y) in this
// geometry event. So we prefer to learn offset from xdg_output that will override
// (x, y) from this event.
output->x = x;
output->y = y;
}
// TODO: there's also a recommended, but unstable interface xdg_output;
// we may want to switch to that one day.
output->x = x;
output->y = y;
output->subpixel = subpixel;
output->transform = transform;
output->width_mm = physical_width;
@@ -164,6 +161,8 @@ NotifyOutputConfigured(WLOutput* output)
output->id,
output->x,
output->y,
output->x_logical,
output->y_logical,
output->width,
output->height,
output->width_logical,
@@ -174,8 +173,6 @@ NotifyOutputConfigured(WLOutput* output)
(jint)output->transform,
(jint)output->scale);
JNU_CHECK_EXCEPTION(env);
output->offset_known = false;
}
static void
@@ -221,12 +218,8 @@ static void
zxdg_output_logical_position(void *data, struct zxdg_output_v1 *zxdg_output_v1, int32_t x, int32_t y)
{
WLOutput * output = data;
output->x = x;
output->y = y;
// Prevent the geometry event from overriding these values with potentially incorrect ones;
// see wl_output_geometry().
output->offset_known = true;
output->x_logical = x;
output->y_logical = y;
}
static void
@@ -264,7 +257,7 @@ WLGraphicsEnvironment_initIDs
CHECK_NULL_RETURN(
notifyOutputConfiguredMID = (*env)->GetMethodID(env, clazz,
"notifyOutputConfigured",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIIIIIIIIII)V"),
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIIIIIIIIIIII)V"),
JNI_FALSE);
CHECK_NULL_RETURN(
notifyOutputDestroyedMID = (*env)->GetMethodID(env, clazz,

View File

@@ -75,7 +75,6 @@ struct wl_seat *wl_seat = NULL;
struct wl_keyboard *wl_keyboard; // optional, check for NULL before use
struct wl_pointer *wl_pointer; // optional, check for NULL before use
struct zwp_relative_pointer_manager_v1* relative_pointer_manager; // optional, check for NULL before use
#define MAX_CURSOR_SCALE 100
struct wl_cursor_theme *cursor_themes[MAX_CURSOR_SCALE] = {NULL};
@@ -84,8 +83,6 @@ struct wl_data_device_manager *wl_ddm = NULL;
struct zwp_primary_selection_device_manager_v1 *zwp_selection_dm = NULL; // optional, check for NULL before use
struct zxdg_output_manager_v1 *zxdg_output_manager_v1 = NULL; // optional, check for NULL before use
struct zwp_text_input_manager_v3 *zwp_text_input_manager = NULL; // optional, check for NULL before use
static uint32_t num_of_outstanding_sync = 0;
// This group of definitions corresponds to declarations from awt.h
@@ -126,7 +123,6 @@ static jmethodID dispatchKeyboardKeyEventMID;
static jmethodID dispatchKeyboardModifiersEventMID;
static jmethodID dispatchKeyboardEnterEventMID;
static jmethodID dispatchKeyboardLeaveEventMID;
static jmethodID dispatchRelativePointerEventMID;
JNIEnv *getEnv() {
JNIEnv *env;
@@ -463,31 +459,6 @@ static const struct wl_keyboard_listener wl_keyboard_listener = {
.key = wl_keyboard_key
};
static void
wl_relative_motion(void *data,
struct zwp_relative_pointer_v1 *zwp_relative_pointer_v1,
uint32_t utime_hi,
uint32_t utime_lo,
wl_fixed_t dx,
wl_fixed_t dy,
wl_fixed_t dx_unaccel,
wl_fixed_t dy_unaccel)
{
double ddx = wl_fixed_to_double(dx);
double ddy = wl_fixed_to_double(dy);
JNIEnv* env = getEnv();
(*env)->CallStaticVoidMethod(env,
tkClass,
dispatchRelativePointerEventMID,
ddx, ddy);
JNU_CHECK_EXCEPTION(env);
}
static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = {
.relative_motion = wl_relative_motion
};
static void
wl_seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities)
{
@@ -498,14 +469,6 @@ wl_seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities)
wl_pointer = wl_seat_get_pointer(wl_seat);
if (wl_pointer != NULL) {
wl_pointer_add_listener(wl_pointer, &wl_pointer_listener, NULL);
if (relative_pointer_manager != NULL) {
struct zwp_relative_pointer_v1* rptr
= zwp_relative_pointer_manager_v1_get_relative_pointer(relative_pointer_manager,
wl_pointer);
if (rptr != NULL) {
zwp_relative_pointer_v1_add_listener(rptr, &relative_pointer_listener, NULL);
}
}
}
} else if (!has_pointer && wl_pointer != NULL) {
wl_pointer_release(wl_pointer);
@@ -598,8 +561,6 @@ registry_global(void *data, struct wl_registry *wl_registry,
xdg_activation_v1 = wl_registry_bind(wl_registry, name, &xdg_activation_v1_interface, 1);
} else if (strcmp(interface, gtk_shell1_interface.name) == 0) {
gtk_shell1 = wl_registry_bind(wl_registry, name, &gtk_shell1_interface, 1);
} else if (strcmp(interface, zwp_relative_pointer_manager_v1_interface.name) == 0) {
relative_pointer_manager = wl_registry_bind(wl_registry, name, &zwp_relative_pointer_manager_v1_interface, 1);
} else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) {
wl_ddm = wl_registry_bind(wl_registry, name,&wl_data_device_manager_interface, 3);
} else if (strcmp(interface, zwp_primary_selection_device_manager_v1_interface.name) == 0) {
@@ -612,14 +573,6 @@ registry_global(void *data, struct wl_registry *wl_registry,
WLOutputXdgOutputManagerBecameAvailable();
process_new_listener_before_end_of_init();
}
} else if (strcmp(interface, zwp_text_input_manager_v3_interface.name) == 0) {
// If the requested version is higher than the provided one by the compositor,
// the event loop may shut down as soon as it gets launched (wl_display_dispatch will return -1),
// so let's protect from this since the component being obtained is not vital for work.
const uint32_t versionToBind = 1;
if (versionToBind <= version) {
zwp_text_input_manager = wl_registry_bind(wl_registry, name, &zwp_text_input_manager_v3_interface, versionToBind);
}
}
#ifdef WAKEFIELD_ROBOT
@@ -753,10 +706,6 @@ initJavaRefs(JNIEnv *env, jclass clazz)
"dispatchKeyboardModifiersEvent",
"(J)V"),
JNI_FALSE);
CHECK_NULL_RETURN(dispatchRelativePointerEventMID = (*env)->GetStaticMethodID(env, tkClass,
"dispatchRelativePointerEvent",
"(DD)V"),
JNI_FALSE);
jclass wlgeClass = (*env)->FindClass(env, "sun/awt/wl/WLGraphicsEnvironment");
CHECK_NULL_RETURN(wlgeClass, JNI_FALSE);

View File

@@ -25,14 +25,11 @@
#include <wayland-client.h>
#include <wayland-cursor.h>
#include "xdg-shell-client-protocol.h"
#include "xdg-activation-v1-client-protocol.h"
#include "xdg-output-unstable-v1-client-protocol.h"
#include "primary-selection-client-protocol.h"
#include "viewporter-client-protocol.h"
#include "relative-pointer-unstable-v1-client-protocol.h"
#include "im/text-input-unstable-v3/text-input-unstable-v3-client-protocol.h"
#include "jvm_md.h"
#include "jni_util.h"
@@ -68,8 +65,6 @@ extern struct wl_cursor_theme *wl_cursor_theme;
extern struct wl_data_device_manager *wl_ddm;
extern struct zwp_primary_selection_device_manager_v1 *zwp_selection_dm; // optional, check for NULL before use
extern struct zxdg_output_manager_v1 *zxdg_output_manager_v1; // optional, check for NULL before use
extern struct zwp_relative_pointer_manager_v1* relative_pointer_manager;
extern struct zwp_text_input_manager_v3 *zwp_text_input_manager; // optional, check for NULL before use
JNIEnv *getEnv();

View File

@@ -1,687 +0,0 @@
/*
* Copyright 2025 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.
*/
#include "sun_awt_wl_im_text_input_unstable_v3_WLInputMethodDescriptorZwpTextInputV3.h"
#include "sun_awt_wl_im_text_input_unstable_v3_WLInputMethodZwpTextInputV3.h"
#include "WLToolkit.h" // wl_seat, zwp_text_input_*, uint[...]_t, int[...]_t
#include "JNIUtilities.h"
#include <stdbool.h> // bool, true, false
#include <string.h> // memset, strlen
#include <stdlib.h> // malloc, free
#include <assert.h> // assert
static bool checkIfTheImplementationIsAvailable() {
return (zwp_text_input_manager == NULL) ? false : true;
}
static struct {
jclass wlInputMethodClass;
/// `sun.awt.wl.im.text_input_unstable_v3.WLInputMethodZwpTextInputV3#zwp_text_input_v3_onEnter`
jmethodID mID_tiOnEnter;
/// `sun.awt.wl.im.text_input_unstable_v3.WLInputMethodZwpTextInputV3#zwp_text_input_v3_onLeave`
jmethodID mID_tiOnLeave;
/// `sun.awt.wl.im.text_input_unstable_v3.WLInputMethodZwpTextInputV3#zwp_text_input_v3_onPreeditString`
jmethodID mID_tiOnPreeditString;
/// `sun.awt.wl.im.text_input_unstable_v3.WLInputMethodZwpTextInputV3#zwp_text_input_v3_onCommitString`
jmethodID mID_tiOnCommitString;
/// `sun.awt.wl.im.text_input_unstable_v3.WLInputMethodZwpTextInputV3#zwp_text_input_v3_onDeleteSurroundingText`
jmethodID mID_tiOnDeleteSurroundingText;
/// `sun.awt.wl.im.text_input_unstable_v3.WLInputMethodZwpTextInputV3#zwp_text_input_v3_onDone`
jmethodID mID_tiOnDone;
} jniIDs = {0};
// ================================================= IMContext section ================================================
static void IMContext_zwp_text_input_v3_onEnter(void*, struct zwp_text_input_v3*, struct wl_surface*);
static void IMContext_zwp_text_input_v3_onLeave(void*, struct zwp_text_input_v3*, struct wl_surface*);
static void IMContext_zwp_text_input_v3_onPreeditString(void*, struct zwp_text_input_v3*, const char*, int32_t, int32_t);
static void IMContext_zwp_text_input_v3_onCommitString(void*, struct zwp_text_input_v3*, const char*);
static void IMContext_zwp_text_input_v3_onDeleteSurroundingText(void*, struct zwp_text_input_v3*, uint32_t, uint32_t);
static void IMContext_zwp_text_input_v3_onDone(void*, struct zwp_text_input_v3*, uint32_t);
static const struct zwp_text_input_v3_listener IMContext_zwp_text_input_v3_listener = {
.enter = &IMContext_zwp_text_input_v3_onEnter,
.leave = &IMContext_zwp_text_input_v3_onLeave,
.preedit_string = &IMContext_zwp_text_input_v3_onPreeditString,
.commit_string = &IMContext_zwp_text_input_v3_onCommitString,
.delete_surrounding_text = &IMContext_zwp_text_input_v3_onDeleteSurroundingText,
.done = &IMContext_zwp_text_input_v3_onDone,
};
/**
* The native-side counterpart of `sun.awt.wl.im.text_input_unstable_v3.WLInputMethodZwpTextInputV3`.
* On Java side these contexts are created and destroyed through
* `WLInputMethod#createNativeContext` and `WLInputMethod#destroyNativeContext` respectively.
*
* `IMContext` and `WLInputMethodZwpTextInputV3` are related in a 1:1 ratio - an instance of `WLInputMethodZwpTextInputV3` holds no more than 1
* instance of `IMContext` and an instance of `IMContext` only belongs to 1 instance of `WLInputMethodZwpTextInputV3`.
*/
struct IMContext {
/// A global reference to the instance of `sun.awt.wl.im.text_input_unstable_v3.WLInputMethodZwpTextInputV3` owning this instance of `IMContext`.
jobject wlInputMethodOwner;
/// Represents an input context for the entire `text-input-unstable-v3` protocol.
struct zwp_text_input_v3 *textInput;
};
/**
* Creates and fully initializes an instance of `struct IMContext`.
*
* @param wlInputMethodOwnerRefToCopy a reference to the owning instance of `sun.awt.wl.im.text_input_unstable_v3.WLInputMethodZwpTextInputV3`.
* NB: the reference will be copied via `NewGlobalRef` and no longer used.
* @return `null` if it has failed to create or completely initialize a new instance.
* In this case a corresponding exception of class `java.awt.AWTException` or of an unchecked exception class
* will be raised in @p env
*/
static struct IMContext* IMContext_Create(JNIEnv * const env, jobject wlInputMethodOwnerRefToCopy) {
struct wl_seat * const wlSeat = wl_seat;
struct zwp_text_input_manager_v3 * const textInputManager = zwp_text_input_manager;
struct IMContext *result = NULL;
jobject wlInputMethodOwner = NULL;
struct zwp_text_input_v3 *textInput = NULL;
if (wlSeat == NULL) {
JNU_ThrowByName(env, "java/awt/AWTException", "IMContext_Create: no wl_seat is available");
goto failure;
}
if (textInputManager == NULL) {
JNU_ThrowNullPointerException(env, "IMContext_Create: textInputManager is NULL");
goto failure;
}
wlInputMethodOwner = (*env)->NewGlobalRef(env, wlInputMethodOwnerRefToCopy);
if (wlInputMethodOwner == NULL) {
if ((*env)->ExceptionCheck(env) == JNI_FALSE) {
JNU_ThrowOutOfMemoryError(env, "IMContext_Create: NewGlobalRef(wlInputMethodOwnerRefToCopy) failed");
}
goto failure;
}
wlInputMethodOwnerRefToCopy = NULL; // To avoid misusages
result = malloc(sizeof(struct IMContext));
if (result == NULL) {
JNU_ThrowOutOfMemoryError(env, "IMContext_Create: malloc(sizeof(struct IMContext)) failed");
goto failure;
}
textInput = zwp_text_input_manager_v3_get_text_input(textInputManager, wlSeat);
if (textInput == NULL) {
JNU_ThrowByName(env, "java/awt/AWTException", "IMContext_Create: failed to obtain a new instance of zwp_text_input_v3");
goto failure;
}
// WLToolkit dispatches (almost) all native Wayland events on EDT, not on its thread.
// If it didn't, the callbacks being set here might be called even before this function finishes, hence even before
// the constructor of `sun.awt.wl.im.text_input_unstable_v3.WLInputMethodZwpTextInputV3` finishes (because the constructor gets called on the
// EDT rather than on the toolkit thread).
// In that case we would have to take it into account while implementing the callbacks and
// `WLInputMethodZwpTextInputV3` class in general.
zwp_text_input_v3_add_listener(textInput, &IMContext_zwp_text_input_v3_listener, result);
(void)memset(result, 0, sizeof(struct IMContext));
result->wlInputMethodOwner = wlInputMethodOwner;
result->textInput = textInput;
return result;
failure:
if (textInput != NULL) {
zwp_text_input_v3_destroy(textInput);
textInput = NULL;
}
if (result != NULL) {
free(result);
result = NULL;
}
if (wlInputMethodOwner != NULL) {
(*env)->DeleteGlobalRef(env, wlInputMethodOwner);
wlInputMethodOwner = NULL;
}
return NULL;
}
/// Destroys the context previously created by IMContext_Create
static void IMContext_Destroy(JNIEnv * const env, struct IMContext * const imContext) {
assert(env != NULL);
assert(imContext != NULL);
if (imContext->textInput != NULL) {
zwp_text_input_v3_destroy(imContext->textInput);
imContext->textInput = NULL;
}
if (imContext->wlInputMethodOwner != NULL) {
(*env)->DeleteGlobalRef(env, imContext->wlInputMethodOwner);
imContext->wlInputMethodOwner = NULL;
}
free(imContext);
}
// The IMContext_zwp_text_input_v3_on* callbacks are supposed to be as a thin bridge to
// `WLInputMethodZwpTextInputV3`'s JNI upcalls as possible in terms of contained logic.
// Generally they should only invoke the corresponding upcalls with the received parameters.
//
// Exceptions after making JNI upcalls to WLInputMethodZwpTextInputV3 are checked (via JNU_CHECK_EXCEPTION),
// but not suppressed on a purpose: all the corresponding Java methods already handles any java.lang.Exception.
// So if an exception leaves any of those methods, it's something really strange and it's better to let WLToolkit
// get to know about it rather than log and try to continue the normal path.
// The checks are just made to suppress -Xcheck:jni warnings.
static void IMContext_zwp_text_input_v3_onEnter(
void * const ctx,
struct zwp_text_input_v3 * const textInput,
struct wl_surface * const surface
) {
const struct IMContext * const imContext = ctx;
JNIEnv *env = NULL;
if (imContext == NULL) {
return;
}
env = getEnv();
if (env == NULL) {
return;
}
(*env)->CallVoidMethod(env, imContext->wlInputMethodOwner, jniIDs.mID_tiOnEnter, ptr_to_jlong(surface));
JNU_CHECK_EXCEPTION(env);
}
static void IMContext_zwp_text_input_v3_onLeave(
void * const ctx,
struct zwp_text_input_v3 * const textInput,
struct wl_surface * const surface
) {
const struct IMContext * const imContext = ctx;
JNIEnv *env = NULL;
if (imContext == NULL) {
return;
}
env = getEnv();
if (env == NULL) {
return;
}
(*env)->CallVoidMethod(env, imContext->wlInputMethodOwner, jniIDs.mID_tiOnLeave, ptr_to_jlong(surface));
JNU_CHECK_EXCEPTION(env);
}
/**
* Converts a UTF-8 string to a Java byte array, throwing an OutOfMemoryError if the allocation fails. Another kind
* of exceptions may also appear, according to the implementation of 'NewByteArray' and 'SetByteArrayRegion'.
* Regardless of the returned value, always check 'env->ExceptionCheck()' after each call of this function.
*
* @param utf8Str UTF-8 string to convert.
* @param utf8StrSizeInBytes size of the UTF-8 string in bytes, or a negative value to ask the function to
* calculate it manually.
* @param env JNI environment. Mustn't be NULL.
* @param oomMessage message to use in the OutOfMemoryError exception if it appears. Mustn't be NULL.
*
* @return a local JNI reference to a Java byte array or NULL if 'utf8Str' is NULL,
* an exception occurred,
* or 'NewByteArray' returned NULL for some other reason.
*/
static jbyteArray utf8StrToJByteArrayOrThrowOOM(
const char * const utf8Str,
ssize_t utf8StrSizeInBytes,
JNIEnv * const env,
const char * const oomMessage
) {
jbyteArray result = NULL;
assert(env != NULL);
assert(oomMessage != NULL);
if (utf8Str == NULL) {
return NULL;
}
if (utf8StrSizeInBytes < 0) {
// Let's believe there's a trailing NUL codepoint (otherwise we can't calculate the string's size), and
// there are no NUL codepoints in the middle, though it's possible in general for UTF-8.
utf8StrSizeInBytes = (ssize_t)(strlen(utf8Str) + 1);
}
result = (*env)->NewByteArray(env, (jsize)utf8StrSizeInBytes);
if (result == NULL) {
if ((*env)->ExceptionCheck(env) == JNI_FALSE) {
JNU_ThrowOutOfMemoryError(env, oomMessage);
}
} else {
(*env)->SetByteArrayRegion(
env,
result,
0,
(jsize)utf8StrSizeInBytes,
(const jbyte*)utf8Str
);
}
return result;
}
static void IMContext_zwp_text_input_v3_onPreeditString(
void * const ctx,
struct zwp_text_input_v3 * const textInput,
const char * const preeditStringUtf8,
const int32_t cursorBeginUtf8Byte,
const int32_t cursorEndUtf8Byte
) {
const struct IMContext * const imContext = ctx;
JNIEnv *env = NULL;
jbyteArray preeditStringUtf8Bytes = NULL;
if (imContext == NULL) {
return;
}
env = getEnv();
if (env == NULL) {
return;
}
// may still be NULL
preeditStringUtf8Bytes = utf8StrToJByteArrayOrThrowOOM(
preeditStringUtf8,
// the zwp_text_input_v3::preedit_string event doesn't provide the length or size of the string separately,
// asking the function to manually calculate it.
-1,
env,
"IMContext_zwp_text_input_v3_onPreeditString: failed to allocate a new Java byte array"
);
if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
return;
}
(*env)->CallVoidMethod(
env,
imContext->wlInputMethodOwner,
jniIDs.mID_tiOnPreeditString,
preeditStringUtf8Bytes,
(jint)cursorBeginUtf8Byte,
(jint)cursorEndUtf8Byte
);
JNU_CHECK_EXCEPTION(env);
}
static void IMContext_zwp_text_input_v3_onCommitString(
void * const ctx,
struct zwp_text_input_v3 * const textInput,
const char * const commitStringUtf8
) {
const struct IMContext * const imContext = ctx;
JNIEnv *env = NULL;
jbyteArray commitStringUtf8Bytes = NULL;
if (imContext == NULL) {
return;
}
env = getEnv();
if (env == NULL) {
return;
}
// may still be NULL
commitStringUtf8Bytes = utf8StrToJByteArrayOrThrowOOM(
commitStringUtf8,
// the zwp_text_input_v3::commit_string event doesn't provide the length or size of the string separately,
// asking the function to manually calculate it.
-1,
env,
"IMContext_zwp_text_input_v3_onCommitString: failed to allocate a new Java byte array"
);
if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
return;
}
(*env)->CallVoidMethod(env, imContext->wlInputMethodOwner, jniIDs.mID_tiOnCommitString, commitStringUtf8Bytes);
JNU_CHECK_EXCEPTION(env);
}
static void IMContext_zwp_text_input_v3_onDeleteSurroundingText(
void * const ctx,
struct zwp_text_input_v3 * const textInput,
const uint32_t numberOfUtf8BytesBeforeToDelete,
const uint32_t numberOfUtf8BytesAfterToDelete
) {
const struct IMContext * const imContext = ctx;
JNIEnv *env = NULL;
if (imContext == NULL) {
return;
}
env = getEnv();
if (env == NULL) {
return;
}
if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
return;
}
(*env)->CallVoidMethod(
env,
imContext->wlInputMethodOwner,
jniIDs.mID_tiOnDeleteSurroundingText,
(jlong)numberOfUtf8BytesBeforeToDelete,
(jlong)numberOfUtf8BytesAfterToDelete
);
JNU_CHECK_EXCEPTION(env);
}
static void IMContext_zwp_text_input_v3_onDone(
void *const ctx,
struct zwp_text_input_v3 * const textInput,
const uint32_t serial
) {
const struct IMContext * const imContext = ctx;
JNIEnv *env = NULL;
if (imContext == NULL) {
return;
}
env = getEnv();
if (env == NULL) {
return;
}
(*env)->CallVoidMethod(env, imContext->wlInputMethodOwner, jniIDs.mID_tiOnDone, (jlong)serial);
JNU_CHECK_EXCEPTION(env);
}
// ============================================= END of IMContext section =============================================
// =============================================== JNI downcalls section ==============================================
JNIEXPORT jboolean JNICALL
Java_sun_awt_wl_im_text_1input_1unstable_1v3_WLInputMethodDescriptorZwpTextInputV3_checkIfAvailableOnPlatform(JNIEnv * const env, const jclass clazz) {
return checkIfTheImplementationIsAvailable() ? JNI_TRUE : JNI_FALSE;
}
JNIEXPORT void JNICALL
Java_sun_awt_wl_im_text_1input_1unstable_1v3_WLInputMethodZwpTextInputV3_initIDs(JNIEnv * const env, const jclass clazz) {
CHECK_NULL_THROW_OOME(
env,
jniIDs.wlInputMethodClass = (*env)->NewGlobalRef(env, clazz),
"Allocation of a global reference to sun.awt.wl.im.text_input_unstable_v3.WLInputMethodZwpTextInputV3 class failed"
);
jniIDs.mID_tiOnEnter =
(*env)->GetMethodID(env, jniIDs.wlInputMethodClass, "zwp_text_input_v3_onEnter", "(J)V");
if (jniIDs.mID_tiOnEnter == NULL) {
// DeleteGlobalRef is one of the few JNI functions that are safe to call while there's a pending exception
(*env)->DeleteGlobalRef(env, jniIDs.wlInputMethodClass);
(void)memset(&jniIDs, 0, sizeof(jniIDs));
return;
}
jniIDs.mID_tiOnLeave =
(*env)->GetMethodID(env, jniIDs.wlInputMethodClass, "zwp_text_input_v3_onLeave", "(J)V");
if (jniIDs.mID_tiOnLeave == NULL) {
(*env)->DeleteGlobalRef(env, jniIDs.wlInputMethodClass);
(void)memset(&jniIDs, 0, sizeof(jniIDs));
return;
}
jniIDs.mID_tiOnPreeditString =
(*env)->GetMethodID(env, jniIDs.wlInputMethodClass, "zwp_text_input_v3_onPreeditString", "([BII)V");
if (jniIDs.mID_tiOnPreeditString == NULL) {
(*env)->DeleteGlobalRef(env, jniIDs.wlInputMethodClass);
(void)memset(&jniIDs, 0, sizeof(jniIDs));
return;
}
jniIDs.mID_tiOnCommitString =
(*env)->GetMethodID(env, jniIDs.wlInputMethodClass, "zwp_text_input_v3_onCommitString", "([B)V");
if (jniIDs.mID_tiOnCommitString == NULL) {
(*env)->DeleteGlobalRef(env, jniIDs.wlInputMethodClass);
(void)memset(&jniIDs, 0, sizeof(jniIDs));
return;
}
jniIDs.mID_tiOnDeleteSurroundingText =
(*env)->GetMethodID(env, jniIDs.wlInputMethodClass, "zwp_text_input_v3_onDeleteSurroundingText", "(JJ)V");
if (jniIDs.mID_tiOnDeleteSurroundingText == NULL) {
(*env)->DeleteGlobalRef(env, jniIDs.wlInputMethodClass);
(void)memset(&jniIDs, 0, sizeof(jniIDs));
return;
}
jniIDs.mID_tiOnDone =
(*env)->GetMethodID(env, jniIDs.wlInputMethodClass, "zwp_text_input_v3_onDone", "(J)V");
if (jniIDs.mID_tiOnDone == NULL) {
(*env)->DeleteGlobalRef(env, jniIDs.wlInputMethodClass);
(void)memset(&jniIDs, 0, sizeof(jniIDs));
return;
}
}
JNIEXPORT jlong JNICALL
Java_sun_awt_wl_im_text_1input_1unstable_1v3_WLInputMethodZwpTextInputV3_createNativeContext(JNIEnv * const env, const jobject self) {
struct IMContext *result = NULL;
if (!checkIfTheImplementationIsAvailable()) {
JNU_ThrowByName(env, "java/awt/AWTException", "sun.awt.wl.im.text_input_unstable_v3.WLInputMethodZwpTextInputV3 is not supported on this system");
return 0;
}
result = IMContext_Create(env, self);
return ptr_to_jlong(result);
}
JNIEXPORT void JNICALL
Java_sun_awt_wl_im_text_1input_1unstable_1v3_WLInputMethodZwpTextInputV3_disposeNativeContext(
JNIEnv * const env,
const jclass clazz,
const jlong contextPtr
) {
struct IMContext *imContext = jlong_to_ptr(contextPtr);
if (imContext == NULL) {
JNU_ThrowNullPointerException(env, "contextPtr");
return;
}
IMContext_Destroy(env, imContext);
imContext = NULL;
}
JNIEXPORT void JNICALL
Java_sun_awt_wl_im_text_1input_1unstable_1v3_WLInputMethodZwpTextInputV3_zwp_1text_1input_1v3_1enable(
JNIEnv * const env,
const jobject self,
const jlong contextPtr
) {
const struct IMContext * const imContext = jlong_to_ptr(contextPtr);
struct zwp_text_input_v3 *textInput = NULL;
if (imContext == NULL) {
JNU_ThrowNullPointerException(env, "contextPtr");
return;
}
textInput = imContext->textInput;
if (textInput == NULL) {
JNU_ThrowByName(env, "java/lang/IllegalStateException", "textInput == NULL");
return;
}
zwp_text_input_v3_enable(textInput);
}
JNIEXPORT void JNICALL
Java_sun_awt_wl_im_text_1input_1unstable_1v3_WLInputMethodZwpTextInputV3_zwp_1text_1input_1v3_1disable(
JNIEnv * const env,
const jobject self,
const jlong contextPtr
) {
const struct IMContext * const imContext = jlong_to_ptr(contextPtr);
struct zwp_text_input_v3 *textInput = NULL;
if (imContext == NULL) {
JNU_ThrowNullPointerException(env, "contextPtr");
return;
}
textInput = imContext->textInput;
if (textInput == NULL) {
JNU_ThrowByName(env, "java/lang/IllegalStateException", "textInput == NULL");
return;
}
zwp_text_input_v3_disable(textInput);
}
JNIEXPORT void JNICALL
Java_sun_awt_wl_im_text_1input_1unstable_1v3_WLInputMethodZwpTextInputV3_zwp_1text_1input_1v3_1set_1cursor_1rectangle(
JNIEnv * const env,
const jobject self,
const jlong contextPtr,
const jint surfaceLocalX,
const jint surfaceLocalY,
const jint width,
const jint height
) {
const struct IMContext * const imContext = jlong_to_ptr(contextPtr);
struct zwp_text_input_v3 *textInput = NULL;
if (imContext == NULL) {
JNU_ThrowNullPointerException(env, "contextPtr");
return;
}
textInput = imContext->textInput;
if (textInput == NULL) {
JNU_ThrowByName(env, "java/lang/IllegalStateException", "textInput == NULL");
return;
}
zwp_text_input_v3_set_cursor_rectangle(
textInput,
(int32_t)surfaceLocalX,
(int32_t)surfaceLocalY,
(int32_t)width,
(int32_t)height
);
}
JNIEXPORT void JNICALL
Java_sun_awt_wl_im_text_1input_1unstable_1v3_WLInputMethodZwpTextInputV3_zwp_1text_1input_1v3_1set_1content_1type(
JNIEnv * const env,
const jobject self,
const jlong contextPtr,
const jint hint,
const jint purpose
) {
const struct IMContext * const imContext = jlong_to_ptr(contextPtr);
struct zwp_text_input_v3 *textInput = NULL;
if (imContext == NULL) {
JNU_ThrowNullPointerException(env, "contextPtr");
return;
}
textInput = imContext->textInput;
if (textInput == NULL) {
JNU_ThrowByName(env, "java/lang/IllegalStateException", "textInput == NULL");
return;
}
zwp_text_input_v3_set_content_type(textInput, (uint32_t)hint, (uint32_t)purpose);
}
JNIEXPORT void JNICALL
Java_sun_awt_wl_im_text_1input_1unstable_1v3_WLInputMethodZwpTextInputV3_zwp_1text_1input_1v3_1set_1text_1change_1cause(
JNIEnv * const env,
const jobject self,
const jlong contextPtr,
const jint changeCause
) {
const struct IMContext * const imContext = jlong_to_ptr(contextPtr);
struct zwp_text_input_v3 *textInput = NULL;
if (imContext == NULL) {
JNU_ThrowNullPointerException(env, "contextPtr");
return;
}
textInput = imContext->textInput;
if (textInput == NULL) {
JNU_ThrowByName(env, "java/lang/IllegalStateException", "textInput == NULL");
return;
}
zwp_text_input_v3_set_text_change_cause(textInput, (uint32_t)changeCause);
}
JNIEXPORT void JNICALL
Java_sun_awt_wl_im_text_1input_1unstable_1v3_WLInputMethodZwpTextInputV3_zwp_1text_1input_1v3_1commit(
JNIEnv * const env,
const jobject self,
const jlong contextPtr
) {
const struct IMContext * const imContext = jlong_to_ptr(contextPtr);
struct zwp_text_input_v3 *textInput = NULL;
if (imContext == NULL) {
JNU_ThrowNullPointerException(env, "contextPtr");
return;
}
textInput = imContext->textInput;
if (textInput == NULL) {
JNU_ThrowByName(env, "java/lang/IllegalStateException", "textInput == NULL");
return;
}
zwp_text_input_v3_commit(textInput);
}
// =========================================== END of JNI downcalls section ===========================================

View File

@@ -1,825 +0,0 @@
/* Generated by wayland-scanner 1.19.0 */
#ifndef TEXT_INPUT_UNSTABLE_V3_CLIENT_PROTOCOL_H
#define TEXT_INPUT_UNSTABLE_V3_CLIENT_PROTOCOL_H
#include <stdint.h>
#include <stddef.h>
#include "wayland-client.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @page page_text_input_unstable_v3 The text_input_unstable_v3 protocol
* Protocol for composing text
*
* @section page_desc_text_input_unstable_v3 Description
*
* This protocol allows compositors to act as input methods and to send text
* to applications. A text input object is used to manage state of what are
* typically text entry fields in the application.
*
* This document adheres to the RFC 2119 when using words like "must",
* "should", "may", etc.
*
* Warning! The protocol described in this file is experimental and
* backward incompatible changes may be made. Backward compatible changes
* may be added together with the corresponding interface version bump.
* Backward incompatible changes are done by bumping the version number in
* the protocol and interface names and resetting the interface version.
* Once the protocol is to be declared stable, the 'z' prefix and the
* version number in the protocol and interface names are removed and the
* interface version number is reset.
*
* @section page_ifaces_text_input_unstable_v3 Interfaces
* - @subpage page_iface_zwp_text_input_v3 - text input
* - @subpage page_iface_zwp_text_input_manager_v3 - text input manager
* @section page_copyright_text_input_unstable_v3 Copyright
* <pre>
*
* Copyright © 2012, 2013 Intel Corporation
* Copyright © 2015, 2016 Jan Arne Petersen
* Copyright © 2017, 2018 Red Hat, Inc.
* Copyright © 2018 Purism SPC
*
* Permission to use, copy, modify, distribute, and sell this
* software and its documentation for any purpose is hereby granted
* without fee, provided that the above copyright notice appear in
* all copies and that both that copyright notice and this permission
* notice appear in supporting documentation, and that the name of
* the copyright holders not be used in advertising or publicity
* pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied
* warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
* THIS SOFTWARE.
* </pre>
*/
struct wl_seat;
struct wl_surface;
struct zwp_text_input_manager_v3;
struct zwp_text_input_v3;
#ifndef ZWP_TEXT_INPUT_V3_INTERFACE
#define ZWP_TEXT_INPUT_V3_INTERFACE
/**
* @page page_iface_zwp_text_input_v3 zwp_text_input_v3
* @section page_iface_zwp_text_input_v3_desc Description
*
* The zwp_text_input_v3 interface represents text input and input methods
* associated with a seat. It provides enter/leave events to follow the
* text input focus for a seat.
*
* Requests are used to enable/disable the text-input object and set
* state information like surrounding and selected text or the content type.
* The information about the entered text is sent to the text-input object
* via the preedit_string and commit_string events.
*
* Text is valid UTF-8 encoded, indices and lengths are in bytes. Indices
* must not point to middle bytes inside a code point: they must either
* point to the first byte of a code point or to the end of the buffer.
* Lengths must be measured between two valid indices.
*
* Focus moving throughout surfaces will result in the emission of
* zwp_text_input_v3.enter and zwp_text_input_v3.leave events. The focused
* surface must commit zwp_text_input_v3.enable and
* zwp_text_input_v3.disable requests as the keyboard focus moves across
* editable and non-editable elements of the UI. Those two requests are not
* expected to be paired with each other, the compositor must be able to
* handle consecutive series of the same request.
*
* State is sent by the state requests (set_surrounding_text,
* set_content_type and set_cursor_rectangle) and a commit request. After an
* enter event or disable request all state information is invalidated and
* needs to be resent by the client.
* @section page_iface_zwp_text_input_v3_api API
* See @ref iface_zwp_text_input_v3.
*/
/**
* @defgroup iface_zwp_text_input_v3 The zwp_text_input_v3 interface
*
* The zwp_text_input_v3 interface represents text input and input methods
* associated with a seat. It provides enter/leave events to follow the
* text input focus for a seat.
*
* Requests are used to enable/disable the text-input object and set
* state information like surrounding and selected text or the content type.
* The information about the entered text is sent to the text-input object
* via the preedit_string and commit_string events.
*
* Text is valid UTF-8 encoded, indices and lengths are in bytes. Indices
* must not point to middle bytes inside a code point: they must either
* point to the first byte of a code point or to the end of the buffer.
* Lengths must be measured between two valid indices.
*
* Focus moving throughout surfaces will result in the emission of
* zwp_text_input_v3.enter and zwp_text_input_v3.leave events. The focused
* surface must commit zwp_text_input_v3.enable and
* zwp_text_input_v3.disable requests as the keyboard focus moves across
* editable and non-editable elements of the UI. Those two requests are not
* expected to be paired with each other, the compositor must be able to
* handle consecutive series of the same request.
*
* State is sent by the state requests (set_surrounding_text,
* set_content_type and set_cursor_rectangle) and a commit request. After an
* enter event or disable request all state information is invalidated and
* needs to be resent by the client.
*/
extern const struct wl_interface zwp_text_input_v3_interface;
#endif
#ifndef ZWP_TEXT_INPUT_MANAGER_V3_INTERFACE
#define ZWP_TEXT_INPUT_MANAGER_V3_INTERFACE
/**
* @page page_iface_zwp_text_input_manager_v3 zwp_text_input_manager_v3
* @section page_iface_zwp_text_input_manager_v3_desc Description
*
* A factory for text-input objects. This object is a global singleton.
* @section page_iface_zwp_text_input_manager_v3_api API
* See @ref iface_zwp_text_input_manager_v3.
*/
/**
* @defgroup iface_zwp_text_input_manager_v3 The zwp_text_input_manager_v3 interface
*
* A factory for text-input objects. This object is a global singleton.
*/
extern const struct wl_interface zwp_text_input_manager_v3_interface;
#endif
#ifndef ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_ENUM
#define ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_ENUM
/**
* @ingroup iface_zwp_text_input_v3
* text change reason
*
* Reason for the change of surrounding text or cursor posision.
*/
enum zwp_text_input_v3_change_cause {
/**
* input method caused the change
*/
ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_INPUT_METHOD = 0,
/**
* something else than the input method caused the change
*/
ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_OTHER = 1,
};
#endif /* ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_ENUM */
#ifndef ZWP_TEXT_INPUT_V3_CONTENT_HINT_ENUM
#define ZWP_TEXT_INPUT_V3_CONTENT_HINT_ENUM
/**
* @ingroup iface_zwp_text_input_v3
* content hint
*
* Content hint is a bitmask to allow to modify the behavior of the text
* input.
*/
enum zwp_text_input_v3_content_hint {
/**
* no special behavior
*/
ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE = 0x0,
/**
* suggest word completions
*/
ZWP_TEXT_INPUT_V3_CONTENT_HINT_COMPLETION = 0x1,
/**
* suggest word corrections
*/
ZWP_TEXT_INPUT_V3_CONTENT_HINT_SPELLCHECK = 0x2,
/**
* switch to uppercase letters at the start of a sentence
*/
ZWP_TEXT_INPUT_V3_CONTENT_HINT_AUTO_CAPITALIZATION = 0x4,
/**
* prefer lowercase letters
*/
ZWP_TEXT_INPUT_V3_CONTENT_HINT_LOWERCASE = 0x8,
/**
* prefer uppercase letters
*/
ZWP_TEXT_INPUT_V3_CONTENT_HINT_UPPERCASE = 0x10,
/**
* prefer casing for titles and headings (can be language dependent)
*/
ZWP_TEXT_INPUT_V3_CONTENT_HINT_TITLECASE = 0x20,
/**
* characters should be hidden
*/
ZWP_TEXT_INPUT_V3_CONTENT_HINT_HIDDEN_TEXT = 0x40,
/**
* typed text should not be stored
*/
ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA = 0x80,
/**
* just Latin characters should be entered
*/
ZWP_TEXT_INPUT_V3_CONTENT_HINT_LATIN = 0x100,
/**
* the text input is multiline
*/
ZWP_TEXT_INPUT_V3_CONTENT_HINT_MULTILINE = 0x200,
};
#endif /* ZWP_TEXT_INPUT_V3_CONTENT_HINT_ENUM */
#ifndef ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_ENUM
#define ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_ENUM
/**
* @ingroup iface_zwp_text_input_v3
* content purpose
*
* The content purpose allows to specify the primary purpose of a text
* input.
*
* This allows an input method to show special purpose input panels with
* extra characters or to disallow some characters.
*/
enum zwp_text_input_v3_content_purpose {
/**
* default input, allowing all characters
*/
ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL = 0,
/**
* allow only alphabetic characters
*/
ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_ALPHA = 1,
/**
* allow only digits
*/
ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_DIGITS = 2,
/**
* input a number (including decimal separator and sign)
*/
ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NUMBER = 3,
/**
* input a phone number
*/
ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PHONE = 4,
/**
* input an URL
*/
ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_URL = 5,
/**
* input an email address
*/
ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_EMAIL = 6,
/**
* input a name of a person
*/
ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NAME = 7,
/**
* input a password (combine with sensitive_data hint)
*/
ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PASSWORD = 8,
/**
* input is a numeric password (combine with sensitive_data hint)
*/
ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PIN = 9,
/**
* input a date
*/
ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_DATE = 10,
/**
* input a time
*/
ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TIME = 11,
/**
* input a date and time
*/
ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_DATETIME = 12,
/**
* input for a terminal
*/
ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL = 13,
};
#endif /* ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_ENUM */
/**
* @ingroup iface_zwp_text_input_v3
* @struct zwp_text_input_v3_listener
*/
struct zwp_text_input_v3_listener {
/**
* enter event
*
* Notification that this seat's text-input focus is on a certain
* surface.
*
* When the seat has the keyboard capability the text-input focus
* follows the keyboard focus. This event sets the current surface
* for the text-input object.
*/
void (*enter)(void *data,
struct zwp_text_input_v3 *zwp_text_input_v3,
struct wl_surface *surface);
/**
* leave event
*
* Notification that this seat's text-input focus is no longer on
* a certain surface. The client should reset any preedit string
* previously set.
*
* The leave notification clears the current surface. It is sent
* before the enter notification for the new focus.
*
* When the seat has the keyboard capability the text-input focus
* follows the keyboard focus.
*/
void (*leave)(void *data,
struct zwp_text_input_v3 *zwp_text_input_v3,
struct wl_surface *surface);
/**
* pre-edit
*
* Notify when a new composing text (pre-edit) should be set at
* the current cursor position. Any previously set composing text
* must be removed. Any previously existing selected text must be
* removed.
*
* The argument text contains the pre-edit string buffer.
*
* The parameters cursor_begin and cursor_end are counted in bytes
* relative to the beginning of the submitted text buffer. Cursor
* should be hidden when both are equal to -1.
*
* They could be represented by the client as a line if both values
* are the same, or as a text highlight otherwise.
*
* Values set with this event are double-buffered. They must be
* applied and reset to initial on the next zwp_text_input_v3.done
* event.
*
* The initial value of text is an empty string, and cursor_begin,
* cursor_end and cursor_hidden are all 0.
*/
void (*preedit_string)(void *data,
struct zwp_text_input_v3 *zwp_text_input_v3,
const char *text,
int32_t cursor_begin,
int32_t cursor_end);
/**
* text commit
*
* Notify when text should be inserted into the editor widget.
* The text to commit could be either just a single character after
* a key press or the result of some composing (pre-edit).
*
* Values set with this event are double-buffered. They must be
* applied and reset to initial on the next zwp_text_input_v3.done
* event.
*
* The initial value of text is an empty string.
*/
void (*commit_string)(void *data,
struct zwp_text_input_v3 *zwp_text_input_v3,
const char *text);
/**
* delete surrounding text
*
* Notify when the text around the current cursor position should
* be deleted.
*
* Before_length and after_length are the number of bytes before
* and after the current cursor index (excluding the selection) to
* delete.
*
* If a preedit text is present, in effect before_length is counted
* from the beginning of it, and after_length from its end (see
* done event sequence).
*
* Values set with this event are double-buffered. They must be
* applied and reset to initial on the next zwp_text_input_v3.done
* event.
*
* The initial values of both before_length and after_length are 0.
* @param before_length length of text before current cursor position
* @param after_length length of text after current cursor position
*/
void (*delete_surrounding_text)(void *data,
struct zwp_text_input_v3 *zwp_text_input_v3,
uint32_t before_length,
uint32_t after_length);
/**
* apply changes
*
* Instruct the application to apply changes to state requested
* by the preedit_string, commit_string and delete_surrounding_text
* events. The state relating to these events is double-buffered,
* and each one modifies the pending state. This event replaces the
* current state with the pending state.
*
* The application must proceed by evaluating the changes in the
* following order:
*
* 1. Replace existing preedit string with the cursor. 2. Delete
* requested surrounding text. 3. Insert commit string with the
* cursor at its end. 4. Calculate surrounding text to send. 5.
* Insert new preedit text in cursor position. 6. Place cursor
* inside preedit text.
*
* The serial number reflects the last state of the
* zwp_text_input_v3 object known to the compositor. The value of
* the serial argument must be equal to the number of commit
* requests already issued on that object. When the client receives
* a done event with a serial different than the number of past
* commit requests, it must proceed as normal, except it should not
* change the current state of the zwp_text_input_v3 object.
*/
void (*done)(void *data,
struct zwp_text_input_v3 *zwp_text_input_v3,
uint32_t serial);
};
/**
* @ingroup iface_zwp_text_input_v3
*/
static inline int
zwp_text_input_v3_add_listener(struct zwp_text_input_v3 *zwp_text_input_v3,
const struct zwp_text_input_v3_listener *listener, void *data)
{
return wl_proxy_add_listener((struct wl_proxy *) zwp_text_input_v3,
(void (**)(void)) listener, data);
}
#define ZWP_TEXT_INPUT_V3_DESTROY 0
#define ZWP_TEXT_INPUT_V3_ENABLE 1
#define ZWP_TEXT_INPUT_V3_DISABLE 2
#define ZWP_TEXT_INPUT_V3_SET_SURROUNDING_TEXT 3
#define ZWP_TEXT_INPUT_V3_SET_TEXT_CHANGE_CAUSE 4
#define ZWP_TEXT_INPUT_V3_SET_CONTENT_TYPE 5
#define ZWP_TEXT_INPUT_V3_SET_CURSOR_RECTANGLE 6
#define ZWP_TEXT_INPUT_V3_COMMIT 7
/**
* @ingroup iface_zwp_text_input_v3
*/
#define ZWP_TEXT_INPUT_V3_ENTER_SINCE_VERSION 1
/**
* @ingroup iface_zwp_text_input_v3
*/
#define ZWP_TEXT_INPUT_V3_LEAVE_SINCE_VERSION 1
/**
* @ingroup iface_zwp_text_input_v3
*/
#define ZWP_TEXT_INPUT_V3_PREEDIT_STRING_SINCE_VERSION 1
/**
* @ingroup iface_zwp_text_input_v3
*/
#define ZWP_TEXT_INPUT_V3_COMMIT_STRING_SINCE_VERSION 1
/**
* @ingroup iface_zwp_text_input_v3
*/
#define ZWP_TEXT_INPUT_V3_DELETE_SURROUNDING_TEXT_SINCE_VERSION 1
/**
* @ingroup iface_zwp_text_input_v3
*/
#define ZWP_TEXT_INPUT_V3_DONE_SINCE_VERSION 1
/**
* @ingroup iface_zwp_text_input_v3
*/
#define ZWP_TEXT_INPUT_V3_DESTROY_SINCE_VERSION 1
/**
* @ingroup iface_zwp_text_input_v3
*/
#define ZWP_TEXT_INPUT_V3_ENABLE_SINCE_VERSION 1
/**
* @ingroup iface_zwp_text_input_v3
*/
#define ZWP_TEXT_INPUT_V3_DISABLE_SINCE_VERSION 1
/**
* @ingroup iface_zwp_text_input_v3
*/
#define ZWP_TEXT_INPUT_V3_SET_SURROUNDING_TEXT_SINCE_VERSION 1
/**
* @ingroup iface_zwp_text_input_v3
*/
#define ZWP_TEXT_INPUT_V3_SET_TEXT_CHANGE_CAUSE_SINCE_VERSION 1
/**
* @ingroup iface_zwp_text_input_v3
*/
#define ZWP_TEXT_INPUT_V3_SET_CONTENT_TYPE_SINCE_VERSION 1
/**
* @ingroup iface_zwp_text_input_v3
*/
#define ZWP_TEXT_INPUT_V3_SET_CURSOR_RECTANGLE_SINCE_VERSION 1
/**
* @ingroup iface_zwp_text_input_v3
*/
#define ZWP_TEXT_INPUT_V3_COMMIT_SINCE_VERSION 1
/** @ingroup iface_zwp_text_input_v3 */
static inline void
zwp_text_input_v3_set_user_data(struct zwp_text_input_v3 *zwp_text_input_v3, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) zwp_text_input_v3, user_data);
}
/** @ingroup iface_zwp_text_input_v3 */
static inline void *
zwp_text_input_v3_get_user_data(struct zwp_text_input_v3 *zwp_text_input_v3)
{
return wl_proxy_get_user_data((struct wl_proxy *) zwp_text_input_v3);
}
static inline uint32_t
zwp_text_input_v3_get_version(struct zwp_text_input_v3 *zwp_text_input_v3)
{
return wl_proxy_get_version((struct wl_proxy *) zwp_text_input_v3);
}
/**
* @ingroup iface_zwp_text_input_v3
*
* Destroy the wp_text_input object. Also disables all surfaces enabled
* through this wp_text_input object.
*/
static inline void
zwp_text_input_v3_destroy(struct zwp_text_input_v3 *zwp_text_input_v3)
{
wl_proxy_marshal((struct wl_proxy *) zwp_text_input_v3,
ZWP_TEXT_INPUT_V3_DESTROY);
wl_proxy_destroy((struct wl_proxy *) zwp_text_input_v3);
}
/**
* @ingroup iface_zwp_text_input_v3
*
* Requests text input on the surface previously obtained from the enter
* event.
*
* This request must be issued every time the active text input changes
* to a new one, including within the current surface. Use
* zwp_text_input_v3.disable when there is no longer any input focus on
* the current surface.
*
* This request resets all state associated with previous enable, disable,
* set_surrounding_text, set_text_change_cause, set_content_type, and
* set_cursor_rectangle requests, as well as the state associated with
* preedit_string, commit_string, and delete_surrounding_text events.
*
* The set_surrounding_text, set_content_type and set_cursor_rectangle
* requests must follow if the text input supports the necessary
* functionality.
*
* State set with this request is double-buffered. It will get applied on
* the next zwp_text_input_v3.commit request, and stay valid until the
* next committed enable or disable request.
*
* The changes must be applied by the compositor after issuing a
* zwp_text_input_v3.commit request.
*/
static inline void
zwp_text_input_v3_enable(struct zwp_text_input_v3 *zwp_text_input_v3)
{
wl_proxy_marshal((struct wl_proxy *) zwp_text_input_v3,
ZWP_TEXT_INPUT_V3_ENABLE);
}
/**
* @ingroup iface_zwp_text_input_v3
*
* Explicitly disable text input on the current surface (typically when
* there is no focus on any text entry inside the surface).
*
* State set with this request is double-buffered. It will get applied on
* the next zwp_text_input_v3.commit request.
*/
static inline void
zwp_text_input_v3_disable(struct zwp_text_input_v3 *zwp_text_input_v3)
{
wl_proxy_marshal((struct wl_proxy *) zwp_text_input_v3,
ZWP_TEXT_INPUT_V3_DISABLE);
}
/**
* @ingroup iface_zwp_text_input_v3
*
* Sets the surrounding plain text around the input, excluding the preedit
* text.
*
* The client should notify the compositor of any changes in any of the
* values carried with this request, including changes caused by handling
* incoming text-input events as well as changes caused by other
* mechanisms like keyboard typing.
*
* If the client is unaware of the text around the cursor, it should not
* issue this request, to signify lack of support to the compositor.
*
* Text is UTF-8 encoded, and should include the cursor position, the
* complete selection and additional characters before and after them.
* There is a maximum length of wayland messages, so text can not be
* longer than 4000 bytes.
*
* Cursor is the byte offset of the cursor within text buffer.
*
* Anchor is the byte offset of the selection anchor within text buffer.
* If there is no selected text, anchor is the same as cursor.
*
* If any preedit text is present, it is replaced with a cursor for the
* purpose of this event.
*
* Values set with this request are double-buffered. They will get applied
* on the next zwp_text_input_v3.commit request, and stay valid until the
* next committed enable or disable request.
*
* The initial state for affected fields is empty, meaning that the text
* input does not support sending surrounding text. If the empty values
* get applied, subsequent attempts to change them may have no effect.
*/
static inline void
zwp_text_input_v3_set_surrounding_text(struct zwp_text_input_v3 *zwp_text_input_v3, const char *text, int32_t cursor, int32_t anchor)
{
wl_proxy_marshal((struct wl_proxy *) zwp_text_input_v3,
ZWP_TEXT_INPUT_V3_SET_SURROUNDING_TEXT, text, cursor, anchor);
}
/**
* @ingroup iface_zwp_text_input_v3
*
* Tells the compositor why the text surrounding the cursor changed.
*
* Whenever the client detects an external change in text, cursor, or
* anchor posision, it must issue this request to the compositor. This
* request is intended to give the input method a chance to update the
* preedit text in an appropriate way, e.g. by removing it when the user
* starts typing with a keyboard.
*
* cause describes the source of the change.
*
* The value set with this request is double-buffered. It must be applied
* and reset to initial at the next zwp_text_input_v3.commit request.
*
* The initial value of cause is input_method.
*/
static inline void
zwp_text_input_v3_set_text_change_cause(struct zwp_text_input_v3 *zwp_text_input_v3, uint32_t cause)
{
wl_proxy_marshal((struct wl_proxy *) zwp_text_input_v3,
ZWP_TEXT_INPUT_V3_SET_TEXT_CHANGE_CAUSE, cause);
}
/**
* @ingroup iface_zwp_text_input_v3
*
* Sets the content purpose and content hint. While the purpose is the
* basic purpose of an input field, the hint flags allow to modify some of
* the behavior.
*
* Values set with this request are double-buffered. They will get applied
* on the next zwp_text_input_v3.commit request.
* Subsequent attempts to update them may have no effect. The values
* remain valid until the next committed enable or disable request.
*
* The initial value for hint is none, and the initial value for purpose
* is normal.
*/
static inline void
zwp_text_input_v3_set_content_type(struct zwp_text_input_v3 *zwp_text_input_v3, uint32_t hint, uint32_t purpose)
{
wl_proxy_marshal((struct wl_proxy *) zwp_text_input_v3,
ZWP_TEXT_INPUT_V3_SET_CONTENT_TYPE, hint, purpose);
}
/**
* @ingroup iface_zwp_text_input_v3
*
* Marks an area around the cursor as a x, y, width, height rectangle in
* surface local coordinates.
*
* Allows the compositor to put a window with word suggestions near the
* cursor, without obstructing the text being input.
*
* If the client is unaware of the position of edited text, it should not
* issue this request, to signify lack of support to the compositor.
*
* Values set with this request are double-buffered. They will get applied
* on the next zwp_text_input_v3.commit request, and stay valid until the
* next committed enable or disable request.
*
* The initial values describing a cursor rectangle are empty. That means
* the text input does not support describing the cursor area. If the
* empty values get applied, subsequent attempts to change them may have
* no effect.
*/
static inline void
zwp_text_input_v3_set_cursor_rectangle(struct zwp_text_input_v3 *zwp_text_input_v3, int32_t x, int32_t y, int32_t width, int32_t height)
{
wl_proxy_marshal((struct wl_proxy *) zwp_text_input_v3,
ZWP_TEXT_INPUT_V3_SET_CURSOR_RECTANGLE, x, y, width, height);
}
/**
* @ingroup iface_zwp_text_input_v3
*
* Atomically applies state changes recently sent to the compositor.
*
* The commit request establishes and updates the state of the client, and
* must be issued after any changes to apply them.
*
* Text input state (enabled status, content purpose, content hint,
* surrounding text and change cause, cursor rectangle) is conceptually
* double-buffered within the context of a text input, i.e. between a
* committed enable request and the following committed enable or disable
* request.
*
* Protocol requests modify the pending state, as opposed to the current
* state in use by the input method. A commit request atomically applies
* all pending state, replacing the current state. After commit, the new
* pending state is as documented for each related request.
*
* Requests are applied in the order of arrival.
*
* Neither current nor pending state are modified unless noted otherwise.
*
* The compositor must count the number of commit requests coming from
* each zwp_text_input_v3 object and use the count as the serial in done
* events.
*/
static inline void
zwp_text_input_v3_commit(struct zwp_text_input_v3 *zwp_text_input_v3)
{
wl_proxy_marshal((struct wl_proxy *) zwp_text_input_v3,
ZWP_TEXT_INPUT_V3_COMMIT);
}
#define ZWP_TEXT_INPUT_MANAGER_V3_DESTROY 0
#define ZWP_TEXT_INPUT_MANAGER_V3_GET_TEXT_INPUT 1
/**
* @ingroup iface_zwp_text_input_manager_v3
*/
#define ZWP_TEXT_INPUT_MANAGER_V3_DESTROY_SINCE_VERSION 1
/**
* @ingroup iface_zwp_text_input_manager_v3
*/
#define ZWP_TEXT_INPUT_MANAGER_V3_GET_TEXT_INPUT_SINCE_VERSION 1
/** @ingroup iface_zwp_text_input_manager_v3 */
static inline void
zwp_text_input_manager_v3_set_user_data(struct zwp_text_input_manager_v3 *zwp_text_input_manager_v3, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) zwp_text_input_manager_v3, user_data);
}
/** @ingroup iface_zwp_text_input_manager_v3 */
static inline void *
zwp_text_input_manager_v3_get_user_data(struct zwp_text_input_manager_v3 *zwp_text_input_manager_v3)
{
return wl_proxy_get_user_data((struct wl_proxy *) zwp_text_input_manager_v3);
}
static inline uint32_t
zwp_text_input_manager_v3_get_version(struct zwp_text_input_manager_v3 *zwp_text_input_manager_v3)
{
return wl_proxy_get_version((struct wl_proxy *) zwp_text_input_manager_v3);
}
/**
* @ingroup iface_zwp_text_input_manager_v3
*
* Destroy the wp_text_input_manager object.
*/
static inline void
zwp_text_input_manager_v3_destroy(struct zwp_text_input_manager_v3 *zwp_text_input_manager_v3)
{
wl_proxy_marshal((struct wl_proxy *) zwp_text_input_manager_v3,
ZWP_TEXT_INPUT_MANAGER_V3_DESTROY);
wl_proxy_destroy((struct wl_proxy *) zwp_text_input_manager_v3);
}
/**
* @ingroup iface_zwp_text_input_manager_v3
*
* Creates a new text-input object for a given seat.
*/
static inline struct zwp_text_input_v3 *
zwp_text_input_manager_v3_get_text_input(struct zwp_text_input_manager_v3 *zwp_text_input_manager_v3, struct wl_seat *seat)
{
struct wl_proxy *id;
id = wl_proxy_marshal_constructor((struct wl_proxy *) zwp_text_input_manager_v3,
ZWP_TEXT_INPUT_MANAGER_V3_GET_TEXT_INPUT, &zwp_text_input_v3_interface, NULL, seat);
return (struct zwp_text_input_v3 *) id;
}
#ifdef __cplusplus
}
#endif
#endif

Some files were not shown because too many files have changed in this diff Show More