From ea39e874979bc2d706be53861a0d8a5980b37239 Mon Sep 17 00:00:00 2001 From: Jack Greiner Date: Tue, 10 Sep 2024 18:48:55 -0400 Subject: [PATCH 1/9] build.gradle: Add dbus-java library as a util dependency --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index ca0f514..41e4058 100644 --- a/build.gradle +++ b/build.gradle @@ -35,6 +35,7 @@ dependencies { implementation 'org.imgscalr:imgscalr-lib:4.2' implementation 'org.apache.commons:commons-math3:3.6.1' implementation 'com.opencsv:opencsv:5.7.1' + implementation 'com.github.hypfvieh:dbus-java:3.3.2' // Tests testImplementation 'org.springframework.boot:spring-boot-starter-test' From aefb6ea045e5778b88c93cd6459d9c8776fea65d Mon Sep 17 00:00:00 2001 From: Jack Greiner Date: Tue, 10 Sep 2024 18:50:12 -0400 Subject: [PATCH 2/9] ScreenshotUtil.java: Add a screenshot utility to take dbus screenshots on Wayland This will use a standard robot screenshot on other non-buggy platforms. --- .../companion/utils/ScreenshotInterface.java | 18 +++ .../companion/utils/ScreenshotUtil.java | 127 ++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 src/main/java/tools/sctrade/companion/utils/ScreenshotInterface.java create mode 100644 src/main/java/tools/sctrade/companion/utils/ScreenshotUtil.java diff --git a/src/main/java/tools/sctrade/companion/utils/ScreenshotInterface.java b/src/main/java/tools/sctrade/companion/utils/ScreenshotInterface.java new file mode 100644 index 0000000..eeba197 --- /dev/null +++ b/src/main/java/tools/sctrade/companion/utils/ScreenshotInterface.java @@ -0,0 +1,18 @@ +package tools.sctrade.companion.utils; + +import java.util.Map; + +import org.freedesktop.dbus.DBusPath; +import org.freedesktop.dbus.annotations.DBusInterfaceName; +import org.freedesktop.dbus.interfaces.DBusInterface; +import org.freedesktop.dbus.types.Variant; + +@DBusInterfaceName(value = "org.freedesktop.portal.Screenshot") +public interface ScreenshotInterface extends DBusInterface { + + // https://flatpak.github.io/xdg-desktop-portal/portal-docs.html#gdbus-org.freedesktop.portal.Screenshot + // Screenshot (IN s parent_window, + // IN a{sv} options, + // OUT o handle); + DBusPath Screenshot(String parentWindow, Map options); +} diff --git a/src/main/java/tools/sctrade/companion/utils/ScreenshotUtil.java b/src/main/java/tools/sctrade/companion/utils/ScreenshotUtil.java new file mode 100644 index 0000000..e86c3f2 --- /dev/null +++ b/src/main/java/tools/sctrade/companion/utils/ScreenshotUtil.java @@ -0,0 +1,127 @@ +package tools.sctrade.companion.utils; + +import java.awt.Dimension; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.image.BufferedImage; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.LinkedTransferQueue; +import java.util.concurrent.TransferQueue; + +import javax.imageio.ImageIO; + +import org.freedesktop.dbus.DBusMap; +import org.freedesktop.dbus.DBusMatchRule; +import org.freedesktop.dbus.DBusPath; +import org.freedesktop.dbus.connections.impl.DBusConnection; +import org.freedesktop.dbus.interfaces.DBusSigHandler; +import org.freedesktop.dbus.messages.DBusSignal; +import org.freedesktop.dbus.types.UInt32; +import org.freedesktop.dbus.types.Variant; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ScreenshotUtil { + + private static final Logger LOG = LoggerFactory.getLogger(ScreenshotUtil.class); + + public static BufferedImage createScreenshot() { + if (isWayland()) { + LOG.debug("Wayland detected"); + return createDbusScreenshot(); + } + return createRobotScreenshot(); + } + + private static boolean isWayland() { + return "wayland".equalsIgnoreCase(System.getenv("XDG_SESSION_TYPE")); + } + + private static BufferedImage createRobotScreenshot() { + try { + LOG.debug("Taking robot screenshot"); + Robot robot = new Robot(); + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + Rectangle screenRect = new Rectangle(screenSize); + BufferedImage shot = robot.createScreenCapture(screenRect); + return shot; + } catch (Exception ex) { + LOG.error("Error creating robot screenshot", ex); + return null; + } + } + + private static BufferedImage createDbusScreenshot() { + try { + LOG.debug("Taking DBus Screenshot..."); + DBusConnection bus = DBusConnection.getConnection(DBusConnection.DBusBusType.SESSION); + LOG.debug("Unique name: %s%n", bus.getUniqueName()); + + String token = UUID.randomUUID().toString().replaceAll("-", ""); + String sender = bus.getUniqueName().substring(1).replace('.', '_'); + String path = String.format("/org/freedesktop/portal/desktop/request/%s/%s", sender, token); + + TransferQueue> queue = new LinkedTransferQueue<>(); + + DBusMatchRule matchRule = new DBusMatchRule("signal", "org.freedesktop.portal.Request", "Response"); + bus.addGenericSigHandler(matchRule, new DBusSigHandler() { + @Override + public void handle(DBusSignal t) { + LOG.debug("DBUS signal received"); + if (path.equals(t.getPath())) { + try { + Object[] params = t.getParameters(); + LOG.debug("params: size=%d%n", params.length); + UInt32 response = (UInt32) params[0]; + DBusMap results = (DBusMap) params[1]; + + if (response.intValue() == 0) { + LOG.debug("Screenshot successful"); + Variant vuri = results.get("uri"); + String uri = (String) vuri.getValue(); + LOG.debug("uri: %s%n", uri); + + BufferedImage shot = ImageIO.read(URI.create(uri).toURL()); + queue.add(Optional.of(shot)); + + if (Files.deleteIfExists(Path.of(URI.create(uri)))) { + LOG.debug("deleted temporary file"); + } + } else { + LOG.error("Failed: response=%s%n", response); + queue.add(Optional.empty()); + } + + bus.removeGenericSigHandler(matchRule, this); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } + }); + + ScreenshotInterface iface = bus.getRemoteObject("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop", ScreenshotInterface.class); + Map options = new HashMap<>(); + options.put("interactive", new Variant(Boolean.FALSE)); + options.put("handle_token", new Variant(token)); + DBusPath result = iface.Screenshot("", options); + LOG.debug("result: %s%n", result); + LOG.debug("expected path: %s%n", path); + + Optional shotResult = queue.take(); + if (shotResult.isPresent()) { + return shotResult.get(); + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } +} From a074ac02b4a2681103fc7c951d4eba6c4f4d8efb Mon Sep 17 00:00:00 2001 From: Jack Greiner Date: Tue, 10 Sep 2024 18:52:45 -0400 Subject: [PATCH 3/9] ScreenPrinter.java: Adapt to use new screenshot utility method --- .../tools/sctrade/companion/input/ScreenPrinter.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/tools/sctrade/companion/input/ScreenPrinter.java b/src/main/java/tools/sctrade/companion/input/ScreenPrinter.java index ad3ce59..8064aa1 100644 --- a/src/main/java/tools/sctrade/companion/input/ScreenPrinter.java +++ b/src/main/java/tools/sctrade/companion/input/ScreenPrinter.java @@ -1,20 +1,20 @@ package tools.sctrade.companion.input; -import java.awt.Rectangle; -import java.awt.Robot; -import java.awt.Toolkit; import java.awt.image.BufferedImage; import java.util.Collection; import java.util.Collections; import java.util.List; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import tools.sctrade.companion.domain.image.ImageManipulation; import tools.sctrade.companion.domain.image.ImageType; import tools.sctrade.companion.domain.image.ImageWriter; import tools.sctrade.companion.domain.notification.NotificationService; import tools.sctrade.companion.utils.AsynchronousProcessor; import tools.sctrade.companion.utils.LocalizationUtil; +import tools.sctrade.companion.utils.ScreenshotUtil; import tools.sctrade.companion.utils.SoundUtil; public class ScreenPrinter implements Runnable { @@ -48,8 +48,7 @@ public void run() { try { logger.debug("Printing screen..."); soundPlayer.play(CAMERA_SHUTTER); - var screenRectangle = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()); - var screenCapture = postProcess(new Robot().createScreenCapture(screenRectangle)); + var screenCapture = postProcess(ScreenshotUtil.createScreenshot()); logger.debug("Printed screen"); logger.debug("Calling image processors..."); From f714efbf6838c5d75470611c25b4fef356c97469 Mon Sep 17 00:00:00 2001 From: Jack Greiner Date: Tue, 10 Sep 2024 19:47:18 -0400 Subject: [PATCH 4/9] ImageUtil.java: Ensure we remove the alpha channel when writing screenshots This is due to the output screenshots being in a JPG format, this small function ensures that the output BufferedImage doesn't include an alpha channel. --- .../sctrade/companion/utils/ImageUtil.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/java/tools/sctrade/companion/utils/ImageUtil.java b/src/main/java/tools/sctrade/companion/utils/ImageUtil.java index 3fae675..040eb13 100644 --- a/src/main/java/tools/sctrade/companion/utils/ImageUtil.java +++ b/src/main/java/tools/sctrade/companion/utils/ImageUtil.java @@ -239,7 +239,25 @@ public static void writeToDiskNoFail(BufferedImage image, Path path) { static void writeToDisk(BufferedImage image, Path path) throws IOException { Files.createDirectories(path.getParent()); File imageFile = new File(path.toString()); - String format = path.toString().substring(path.toString().lastIndexOf(".") + 1); + + // Get the format from the file extension + String format = path.toString().substring(path.toString().lastIndexOf(".") + 1).toLowerCase(); + + // If the format is JPG, ensure no alpha channel is present + if ("jpg".equals(format) || "jpeg".equals(format)) { + if (image.getTransparency() != BufferedImage.OPAQUE) { + image = convertToJpgCompatible(image); + } + } + + // Write the image to disk in the desired format ImageIO.write(image, format, imageFile); } + + // Helper method to convert an image to a JPG-compatible format (no alpha channel) + private static BufferedImage convertToJpgCompatible(BufferedImage image) { + BufferedImage newImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB); + newImage.createGraphics().drawImage(image, 0, 0, null); + return newImage; + } } From e6777b257f86fea94fdcc391eecc617fd8a096d8 Mon Sep 17 00:00:00 2001 From: Jack Greiner Date: Tue, 10 Sep 2024 21:26:56 -0400 Subject: [PATCH 5/9] build.gradle: Update dbus-java to latest available version --- build.gradle | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 41e4058..3126647 100644 --- a/build.gradle +++ b/build.gradle @@ -35,7 +35,10 @@ dependencies { implementation 'org.imgscalr:imgscalr-lib:4.2' implementation 'org.apache.commons:commons-math3:3.6.1' implementation 'com.opencsv:opencsv:5.7.1' - implementation 'com.github.hypfvieh:dbus-java:3.3.2' + + // DBUS interaction libraries + implementation 'com.github.hypfvieh:dbus-java-core:5.1.0' + implementation 'com.github.hypfvieh:dbus-java-transport-junixsocket:5.1.0' // Tests testImplementation 'org.springframework.boot:spring-boot-starter-test' From c8964050afc71d4657bb2d1564e72c65bf500ea2 Mon Sep 17 00:00:00 2001 From: Jack Greiner Date: Tue, 10 Sep 2024 21:27:38 -0400 Subject: [PATCH 6/9] ScreenshotUtil.java: Update createDbusScreenshot for new API --- .../companion/utils/ScreenshotUtil.java | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/src/main/java/tools/sctrade/companion/utils/ScreenshotUtil.java b/src/main/java/tools/sctrade/companion/utils/ScreenshotUtil.java index e86c3f2..caa74fc 100644 --- a/src/main/java/tools/sctrade/companion/utils/ScreenshotUtil.java +++ b/src/main/java/tools/sctrade/companion/utils/ScreenshotUtil.java @@ -9,6 +9,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Optional; import java.util.UUID; @@ -17,10 +18,10 @@ import javax.imageio.ImageIO; -import org.freedesktop.dbus.DBusMap; import org.freedesktop.dbus.DBusMatchRule; import org.freedesktop.dbus.DBusPath; import org.freedesktop.dbus.connections.impl.DBusConnection; +import org.freedesktop.dbus.connections.impl.DBusConnectionBuilder; import org.freedesktop.dbus.interfaces.DBusSigHandler; import org.freedesktop.dbus.messages.DBusSignal; import org.freedesktop.dbus.types.UInt32; @@ -58,11 +59,11 @@ private static BufferedImage createRobotScreenshot() { } } - private static BufferedImage createDbusScreenshot() { - try { - LOG.debug("Taking DBus Screenshot..."); - DBusConnection bus = DBusConnection.getConnection(DBusConnection.DBusBusType.SESSION); - LOG.debug("Unique name: %s%n", bus.getUniqueName()); + private static BufferedImage createDbusScreenshot() { + try { + LOG.debug("Taking DBus Screenshot..."); + DBusConnection bus = DBusConnectionBuilder.forSessionBus().build(); + LOG.debug("Unique name: %s%n", bus.getUniqueName()); String token = UUID.randomUUID().toString().replaceAll("-", ""); String sender = bus.getUniqueName().substring(1).replace('.', '_'); @@ -70,17 +71,17 @@ private static BufferedImage createDbusScreenshot() { TransferQueue> queue = new LinkedTransferQueue<>(); - DBusMatchRule matchRule = new DBusMatchRule("signal", "org.freedesktop.portal.Request", "Response"); - bus.addGenericSigHandler(matchRule, new DBusSigHandler() { - @Override - public void handle(DBusSignal t) { - LOG.debug("DBUS signal received"); - if (path.equals(t.getPath())) { - try { - Object[] params = t.getParameters(); - LOG.debug("params: size=%d%n", params.length); - UInt32 response = (UInt32) params[0]; - DBusMap results = (DBusMap) params[1]; + DBusMatchRule matchRule = new DBusMatchRule("signal", "org.freedesktop.portal.Request", "Response"); + bus.addGenericSigHandler(matchRule, new DBusSigHandler() { + @Override + public void handle(DBusSignal t) { + LOG.debug("DBUS signal received"); + if (path.equals(t.getPath())) { + try { + Object[] params = t.getParameters(); + LOG.debug("params: size=%d%n", params.length); + UInt32 response = (UInt32) params[0]; + LinkedHashMap results = (LinkedHashMap) params[1]; if (response.intValue() == 0) { LOG.debug("Screenshot successful"); @@ -99,13 +100,13 @@ public void handle(DBusSignal t) { queue.add(Optional.empty()); } - bus.removeGenericSigHandler(matchRule, this); - } catch (Exception ex) { - ex.printStackTrace(); - } - } - } - }); + bus.removeGenericSigHandler(matchRule, this); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + }); ScreenshotInterface iface = bus.getRemoteObject("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop", ScreenshotInterface.class); Map options = new HashMap<>(); From a28c887534135bc2f684502454b236cdc0aac293 Mon Sep 17 00:00:00 2001 From: Jack Greiner Date: Tue, 10 Sep 2024 21:45:55 -0400 Subject: [PATCH 7/9] ImageUtil.java: Fix warning --- src/main/java/tools/sctrade/companion/utils/ImageUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/tools/sctrade/companion/utils/ImageUtil.java b/src/main/java/tools/sctrade/companion/utils/ImageUtil.java index 040eb13..404e614 100644 --- a/src/main/java/tools/sctrade/companion/utils/ImageUtil.java +++ b/src/main/java/tools/sctrade/companion/utils/ImageUtil.java @@ -231,7 +231,7 @@ public static BufferedImage toBufferedImage(Mat mat) throws IOException { public static void writeToDiskNoFail(BufferedImage image, Path path) { try { writeToDisk(image, path.toAbsolutePath()); - } catch (Exception e) { + } catch (IOException e) { logger.error("There was an error writing to disk", e); } } From 4c29cb155ebc35a99768504472ff75a748bab683 Mon Sep 17 00:00:00 2001 From: Jack Greiner Date: Tue, 10 Sep 2024 21:46:20 -0400 Subject: [PATCH 8/9] ScreenshotInterface.java: Whitespace and fix warning --- .../sctrade/companion/utils/ScreenshotInterface.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/tools/sctrade/companion/utils/ScreenshotInterface.java b/src/main/java/tools/sctrade/companion/utils/ScreenshotInterface.java index eeba197..a690fda 100644 --- a/src/main/java/tools/sctrade/companion/utils/ScreenshotInterface.java +++ b/src/main/java/tools/sctrade/companion/utils/ScreenshotInterface.java @@ -10,9 +10,9 @@ @DBusInterfaceName(value = "org.freedesktop.portal.Screenshot") public interface ScreenshotInterface extends DBusInterface { - // https://flatpak.github.io/xdg-desktop-portal/portal-docs.html#gdbus-org.freedesktop.portal.Screenshot - // Screenshot (IN s parent_window, - // IN a{sv} options, - // OUT o handle); - DBusPath Screenshot(String parentWindow, Map options); + // https://flatpak.github.io/xdg-desktop-portal/portal-docs.html#gdbus-org.freedesktop.portal.Screenshot + // Screenshot (IN s parent_window, + // IN a{sv} options, + // OUT o handle); + DBusPath Screenshot(String parentWindow, Map> options); } From 3a71b7b05f1c1e51af75267052724b406f077d1e Mon Sep 17 00:00:00 2001 From: Jack Greiner Date: Tue, 10 Sep 2024 21:46:40 -0400 Subject: [PATCH 9/9] ScreenshotUtil.java: Fix whitespace and warnings --- .../companion/utils/ScreenshotUtil.java | 175 +++++++++--------- 1 file changed, 90 insertions(+), 85 deletions(-) diff --git a/src/main/java/tools/sctrade/companion/utils/ScreenshotUtil.java b/src/main/java/tools/sctrade/companion/utils/ScreenshotUtil.java index caa74fc..3fb58b4 100644 --- a/src/main/java/tools/sctrade/companion/utils/ScreenshotUtil.java +++ b/src/main/java/tools/sctrade/companion/utils/ScreenshotUtil.java @@ -1,10 +1,13 @@ package tools.sctrade.companion.utils; +import java.awt.AWTException; import java.awt.Dimension; +import java.awt.HeadlessException; import java.awt.Rectangle; import java.awt.Robot; import java.awt.Toolkit; import java.awt.image.BufferedImage; +import java.io.IOException; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; @@ -22,6 +25,7 @@ import org.freedesktop.dbus.DBusPath; import org.freedesktop.dbus.connections.impl.DBusConnection; import org.freedesktop.dbus.connections.impl.DBusConnectionBuilder; +import org.freedesktop.dbus.exceptions.DBusException; import org.freedesktop.dbus.interfaces.DBusSigHandler; import org.freedesktop.dbus.messages.DBusSignal; import org.freedesktop.dbus.types.UInt32; @@ -31,98 +35,99 @@ public class ScreenshotUtil { - private static final Logger LOG = LoggerFactory.getLogger(ScreenshotUtil.class); + private static final Logger LOG = LoggerFactory.getLogger(ScreenshotUtil.class); - public static BufferedImage createScreenshot() { - if (isWayland()) { - LOG.debug("Wayland detected"); - return createDbusScreenshot(); - } - return createRobotScreenshot(); - } - - private static boolean isWayland() { - return "wayland".equalsIgnoreCase(System.getenv("XDG_SESSION_TYPE")); + public static BufferedImage createScreenshot() { + if (isWayland()) { + LOG.debug("Wayland detected"); + return createDbusScreenshot(); } - - private static BufferedImage createRobotScreenshot() { - try { - LOG.debug("Taking robot screenshot"); - Robot robot = new Robot(); - Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); - Rectangle screenRect = new Rectangle(screenSize); - BufferedImage shot = robot.createScreenCapture(screenRect); - return shot; - } catch (Exception ex) { - LOG.error("Error creating robot screenshot", ex); - return null; - } + return createRobotScreenshot(); + } + + private static boolean isWayland() { + return "wayland".equalsIgnoreCase(System.getenv("XDG_SESSION_TYPE")); + } + + private static BufferedImage createRobotScreenshot() { + try { + LOG.debug("Taking robot screenshot"); + Robot robot = new Robot(); + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + Rectangle screenRect = new Rectangle(screenSize); + BufferedImage shot = robot.createScreenCapture(screenRect); + return shot; + } catch (AWTException | HeadlessException ex) { + LOG.error("Error creating robot screenshot", ex); + return null; } + } private static BufferedImage createDbusScreenshot() { - try { - LOG.debug("Taking DBus Screenshot..."); - DBusConnection bus = DBusConnectionBuilder.forSessionBus().build(); - LOG.debug("Unique name: %s%n", bus.getUniqueName()); - - String token = UUID.randomUUID().toString().replaceAll("-", ""); - String sender = bus.getUniqueName().substring(1).replace('.', '_'); - String path = String.format("/org/freedesktop/portal/desktop/request/%s/%s", sender, token); - - TransferQueue> queue = new LinkedTransferQueue<>(); - - DBusMatchRule matchRule = new DBusMatchRule("signal", "org.freedesktop.portal.Request", "Response"); - bus.addGenericSigHandler(matchRule, new DBusSigHandler() { - @Override - public void handle(DBusSignal t) { - LOG.debug("DBUS signal received"); - if (path.equals(t.getPath())) { - try { - Object[] params = t.getParameters(); - LOG.debug("params: size=%d%n", params.length); - UInt32 response = (UInt32) params[0]; - LinkedHashMap results = (LinkedHashMap) params[1]; - - if (response.intValue() == 0) { - LOG.debug("Screenshot successful"); - Variant vuri = results.get("uri"); - String uri = (String) vuri.getValue(); - LOG.debug("uri: %s%n", uri); - - BufferedImage shot = ImageIO.read(URI.create(uri).toURL()); - queue.add(Optional.of(shot)); - - if (Files.deleteIfExists(Path.of(URI.create(uri)))) { - LOG.debug("deleted temporary file"); - } - } else { - LOG.error("Failed: response=%s%n", response); - queue.add(Optional.empty()); - } - - bus.removeGenericSigHandler(matchRule, this); - } catch (Exception e) { - e.printStackTrace(); - } - } + try { + LOG.debug("Taking DBus Screenshot..."); + DBusConnection bus = DBusConnectionBuilder.forSessionBus().build(); + LOG.debug("Unique name: {}", bus.getUniqueName()); + + String token = UUID.randomUUID().toString().replaceAll("-", ""); + String sender = bus.getUniqueName().substring(1).replace('.', '_'); + String path = String.format("/org/freedesktop/portal/desktop/request/%s/%s", sender, token); + + TransferQueue> queue = new LinkedTransferQueue<>(); + + DBusMatchRule matchRule = new DBusMatchRule("signal", "org.freedesktop.portal.Request", "Response"); + bus.addGenericSigHandler(matchRule, new DBusSigHandler() { + @Override + public void handle(DBusSignal t) { + LOG.debug("DBUS signal received"); + if (path.equals(t.getPath())) { + try { + Object[] params = t.getParameters(); + LOG.debug("params: size={}", params.length); + UInt32 response = (UInt32) params[0]; + @SuppressWarnings("unchecked") + LinkedHashMap> results = (LinkedHashMap>) params[1]; + + if (response.intValue() == 0) { + LOG.debug("Screenshot successful"); + Variant vuri = results.get("uri"); + String uri = (String) vuri.getValue(); + LOG.debug("uri: {}", uri); + + BufferedImage shot = ImageIO.read(URI.create(uri).toURL()); + queue.add(Optional.of(shot)); + + if (Files.deleteIfExists(Path.of(URI.create(uri)))) { + LOG.debug("deleted temporary file"); + } + } else { + LOG.error("Failed: response={}", response); + queue.add(Optional.empty()); } - }); - - ScreenshotInterface iface = bus.getRemoteObject("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop", ScreenshotInterface.class); - Map options = new HashMap<>(); - options.put("interactive", new Variant(Boolean.FALSE)); - options.put("handle_token", new Variant(token)); - DBusPath result = iface.Screenshot("", options); - LOG.debug("result: %s%n", result); - LOG.debug("expected path: %s%n", path); - - Optional shotResult = queue.take(); - if (shotResult.isPresent()) { - return shotResult.get(); + + bus.removeGenericSigHandler(matchRule, this); + } catch (IOException | DBusException e) { + LOG.error("Error handling DBus signal", e); } - } catch (Exception e) { - e.printStackTrace(); + } } - return null; + }); + + ScreenshotInterface iface = bus.getRemoteObject("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop", ScreenshotInterface.class); + Map> options = new HashMap<>(); + options.put("interactive", new Variant<>(Boolean.FALSE)); + options.put("handle_token", new Variant<>(token)); + DBusPath result = iface.Screenshot("", options); + LOG.debug("result: {}", result); + LOG.debug("expected path: {}", path); + + Optional shotResult = queue.take(); + if (shotResult.isPresent()) { + return shotResult.get(); + } + } catch (IllegalArgumentException | InterruptedException | DBusException e) { + LOG.error("Error taking DBus screenshot", e); } + return null; + } }