Skip to content

Commit

Permalink
8280982: [Wayland] [XWayland] java.awt.Robot taking screenshots
Browse files Browse the repository at this point in the history
Reviewed-by: prr, kizune, psadhukhan
  • Loading branch information
Alexander Zvegintsev committed Jun 7, 2023
1 parent a1ab377 commit 9d7bf53
Show file tree
Hide file tree
Showing 106 changed files with 15,062 additions and 40 deletions.
1 change: 1 addition & 0 deletions make/modules/java.desktop/Java.gmk
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ EXCLUDE_FILES += \
ifeq ($(call isTargetOs, macosx), true)
# exclude all X11 on Mac.
EXCLUDES += \
sun/awt/screencast \
sun/awt/X11 \
sun/java2d/x11 \
sun/java2d/jules \
Expand Down
5 changes: 4 additions & 1 deletion make/modules/java.desktop/lib/Awt2dLibraries.gmk
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ ifeq ($(call isTargetOs, windows macosx), false)

LIBAWT_XAWT_EXCLUDES := medialib debug

LIBPIPEWIRE_HEADER_DIRS := \
$(TOPDIR)/src/$(MODULE)/unix/native/libpipewire/include

LIBAWT_XAWT_EXTRA_HEADER_DIRS := \
$(LIBAWT_DEFAULT_HEADER_DIRS) \
libawt_xawt/awt \
Expand All @@ -200,7 +203,7 @@ ifeq ($(call isTargetOs, windows macosx), false)
common/font \
common/java2d/opengl \
common/java2d/x11 \
#
$(LIBPIPEWIRE_HEADER_DIRS)

LIBAWT_XAWT_CFLAGS += -DXAWT -DXAWT_HACK \
$(FONTCONFIG_CFLAGS) \
Expand Down
56 changes: 47 additions & 9 deletions src/java.desktop/unix/classes/sun/awt/X11/XRobotPeer.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,43 @@
import sun.awt.UNIXToolkit;
import sun.awt.X11GraphicsConfig;
import sun.awt.X11GraphicsDevice;
import sun.awt.screencast.ScreencastHelper;
import sun.security.action.GetPropertyAction;

@SuppressWarnings("removal")
final class XRobotPeer implements RobotPeer {

private static final boolean tryGtk;
private static final String screenshotMethod;
private static final String METHOD_X11 = "x11";
private static final String METHOD_SCREENCAST = "dbusScreencast";

static {
loadNativeLibraries();

tryGtk = Boolean.parseBoolean(
AccessController.doPrivileged(
new GetPropertyAction("awt.robot.gtk", "true")
));
AccessController.doPrivileged(
new GetPropertyAction("awt.robot.gtk",
"true")
));

boolean isOnWayland = false;

if (Toolkit.getDefaultToolkit() instanceof SunToolkit sunToolkit) {
isOnWayland = sunToolkit.isRunningOnWayland();
}

screenshotMethod = AccessController.doPrivileged(
new GetPropertyAction(
"awt.robot.screenshotMethod",
isOnWayland
? METHOD_SCREENCAST
: METHOD_X11
));
}

private static volatile boolean useGtk;
private final X11GraphicsConfig xgc;
private final X11GraphicsConfig xgc;

XRobotPeer(X11GraphicsDevice gd) {
xgc = (X11GraphicsConfig) gd.getDefaultConfiguration();
Expand Down Expand Up @@ -100,15 +122,31 @@ public void keyRelease(int keycode) {
@Override
public int getRGBPixel(int x, int y) {
int[] pixelArray = new int[1];
getRGBPixelsImpl(xgc, x, y, 1, 1, pixelArray, useGtk);
if (screenshotMethod.equals(METHOD_SCREENCAST)
&& ScreencastHelper.isAvailable()) {

ScreencastHelper.getRGBPixels(x, y, 1, 1, pixelArray);
} else {
getRGBPixelsImpl(xgc, x, y, 1, 1, pixelArray, useGtk);
}
return pixelArray[0];
}

@Override
public int [] getRGBPixels(Rectangle bounds) {
int[] pixelArray = new int[bounds.width*bounds.height];
getRGBPixelsImpl(xgc, bounds.x, bounds.y, bounds.width, bounds.height,
pixelArray, useGtk);
public int[] getRGBPixels(Rectangle bounds) {
int[] pixelArray = new int[bounds.width * bounds.height];
if (screenshotMethod.equals(METHOD_SCREENCAST)
&& ScreencastHelper.isAvailable()) {

ScreencastHelper.getRGBPixels(bounds.x, bounds.y,
bounds.width, bounds.height,
pixelArray);
} else {
getRGBPixelsImpl(xgc,
bounds.x, bounds.y,
bounds.width, bounds.height,
pixelArray, useGtk);
}
return pixelArray;
}

Expand Down
191 changes: 191 additions & 0 deletions src/java.desktop/unix/classes/sun/awt/screencast/ScreencastHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
/*
* Copyright (c) 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.awt.screencast;

import sun.awt.UNIXToolkit;
import sun.security.action.GetPropertyAction;

import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.security.AccessController;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.IntStream;

/**
* Helper class for grabbing pixels from the screen using the
* <a href="https://flatpak.github.io/xdg-desktop-portal/#gdbus-org.freedesktop.portal.ScreenCast">
* org.freedesktop.portal.ScreenCast API</a>
*/

@SuppressWarnings("removal")
public class ScreencastHelper {

static final boolean SCREENCAST_DEBUG;
private static final boolean IS_NATIVE_LOADED;


private static final int ERROR = -1;
private static final int DENIED = -11;
private static final int OUT_OF_BOUNDS = -12;

private ScreencastHelper() {
}

static {
SCREENCAST_DEBUG = Boolean.parseBoolean(
AccessController.doPrivileged(
new GetPropertyAction(
"awt.robot.screenshotDebug",
"false"
)
));

boolean loadFailed = false;

if (!(Toolkit.getDefaultToolkit() instanceof UNIXToolkit tk
&& tk.loadGTK())
|| !loadPipewire(SCREENCAST_DEBUG)) {

System.err.println(
"Could not load native libraries for ScreencastHelper"
);

loadFailed = true;
}

IS_NATIVE_LOADED = !loadFailed;
}

public static boolean isAvailable() {
return IS_NATIVE_LOADED;
}

private static native boolean loadPipewire(boolean screencastDebug);

private static native int getRGBPixelsImpl(
int x, int y, int width, int height,
int[] pixelArray,
int[] affectedScreensBoundsArray,
String token
);

private static List<Rectangle> getSystemScreensBounds() {
return Arrays
.stream(GraphicsEnvironment
.getLocalGraphicsEnvironment()
.getScreenDevices())
.map(graphicsDevice ->
graphicsDevice.getDefaultConfiguration().getBounds()
).toList();
}

public static synchronized void getRGBPixels(
int x, int y, int width, int height, int[] pixelArray
) {
if (!IS_NATIVE_LOADED) return;

Rectangle captureArea = new Rectangle(x, y, width, height);

List<Rectangle> affectedScreenBounds = getSystemScreensBounds()
.stream()
.filter(captureArea::intersects)
.toList();

if (SCREENCAST_DEBUG) {
System.out.printf("// getRGBPixels in %s, affectedScreenBounds %s\n",
captureArea, affectedScreenBounds);
}

if (affectedScreenBounds.isEmpty()) {
if (SCREENCAST_DEBUG) {
System.out.println("// getRGBPixels - requested area "
+ "outside of any screen");
}
return;
}

int retVal;
Set<TokenItem> tokensForRectangle =
TokenStorage.getTokens(affectedScreenBounds);

int[] affectedScreenBoundsArray = affectedScreenBounds
.stream()
.filter(captureArea::intersects)
.flatMapToInt(bounds -> IntStream.of(
bounds.x, bounds.y,
bounds.width, bounds.height
))
.toArray();

for (TokenItem tokenItem : tokensForRectangle) {
retVal = getRGBPixelsImpl(
x, y, width, height,
pixelArray,
affectedScreenBoundsArray,
tokenItem.token
);

if (retVal >= 0) { // we have received a screen data
return;
} else if (!checkReturnValue(retVal)) {
return;
} // else, try other tokens
}

// we do not have a saved token or it did not work,
// try without the token to show the system's permission request window
retVal = getRGBPixelsImpl(
x, y, width, height,
pixelArray,
affectedScreenBoundsArray,
null
);

checkReturnValue(retVal);
}

private static boolean checkReturnValue(int retVal) {
if (retVal == DENIED) {
// user explicitly denied the capture, no more tries.
throw new SecurityException(
"Screen Capture in the selected area was not allowed"
);
} else if (retVal == ERROR) {
if (SCREENCAST_DEBUG) {
System.err.println("Screen capture failed.");
}
} else if (retVal == OUT_OF_BOUNDS) {
if (SCREENCAST_DEBUG) {
System.err.println(
"Token does not provide access to requested area.");
}
}
return retVal != ERROR;
}
}
Loading

7 comments on commit 9d7bf53

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MBaesken
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/integrate jdk17u-dev

@openjdk
Copy link

@openjdk openjdk bot commented on 9d7bf53 Jul 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MBaesken The command integrate can only be used in pull requests.

@MBaesken
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/backport jdk17u-dev

@openjdk
Copy link

@openjdk openjdk bot commented on 9d7bf53 Jul 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MBaesken Could not automatically backport 9d7bf532 to openjdk/jdk17u-dev due to conflicts in the following files:

  • src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c

Please fetch the appropriate branch/commit and manually resolve these conflicts by using the following commands in your personal fork of openjdk/jdk17u-dev. Note: these commands are just some suggestions and you can use other equivalent commands you know.

# Fetch the up-to-date version of the target branch
$ git fetch --no-tags https://git.openjdk.org/jdk17u-dev.git master:master

# Check out the target branch and create your own branch to backport
$ git checkout master
$ git checkout -b backport-MBaesken-9d7bf532-master

# Fetch the commit you want to backport
$ git fetch --no-tags https://git.openjdk.org/jdk.git 9d7bf5329e5a0393553bca2e3a51ad1125b41b96

# Backport the commit
$ git cherry-pick --no-commit 9d7bf5329e5a0393553bca2e3a51ad1125b41b96
# Resolve conflicts now

# Commit the files you have modified
$ git add files/with/resolved/conflicts
$ git commit -m 'Backport 9d7bf5329e5a0393553bca2e3a51ad1125b41b96'

Once you have resolved the conflicts as explained above continue with creating a pull request towards the openjdk/jdk17u-dev with the title Backport 9d7bf5329e5a0393553bca2e3a51ad1125b41b96.

Below you can find a suggestion for the pull request body:

Hi all,

This pull request contains a backport of commit 9d7bf532 from the openjdk/jdk repository.

The commit being backported was authored by Alexander Zvegintsev on 7 Jun 2023 and was reviewed by Phil Race, Alexander Zuev and Prasanta Sadhukhan.

Thanks!

@MBaesken
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/backport jdk17u-dev

@openjdk
Copy link

@openjdk openjdk bot commented on 9d7bf53 Jul 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MBaesken Could not automatically backport 9d7bf532 to openjdk/jdk17u-dev due to conflicts in the following files:

  • src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c

Please fetch the appropriate branch/commit and manually resolve these conflicts by using the following commands in your personal fork of openjdk/jdk17u-dev. Note: these commands are just some suggestions and you can use other equivalent commands you know.

# Fetch the up-to-date version of the target branch
$ git fetch --no-tags https://git.openjdk.org/jdk17u-dev.git master:master

# Check out the target branch and create your own branch to backport
$ git checkout master
$ git checkout -b backport-MBaesken-9d7bf532-master

# Fetch the commit you want to backport
$ git fetch --no-tags https://git.openjdk.org/jdk.git 9d7bf5329e5a0393553bca2e3a51ad1125b41b96

# Backport the commit
$ git cherry-pick --no-commit 9d7bf5329e5a0393553bca2e3a51ad1125b41b96
# Resolve conflicts now

# Commit the files you have modified
$ git add files/with/resolved/conflicts
$ git commit -m 'Backport 9d7bf5329e5a0393553bca2e3a51ad1125b41b96'

Once you have resolved the conflicts as explained above continue with creating a pull request towards the openjdk/jdk17u-dev with the title Backport 9d7bf5329e5a0393553bca2e3a51ad1125b41b96.

Below you can find a suggestion for the pull request body:

Hi all,

This pull request contains a backport of commit 9d7bf532 from the openjdk/jdk repository.

The commit being backported was authored by Alexander Zvegintsev on 7 Jun 2023 and was reviewed by Phil Race, Alexander Zuev and Prasanta Sadhukhan.

Thanks!

Please sign in to comment.