diff --git a/build.gradle.kts b/build.gradle.kts index aafafb0..65618e5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -36,7 +36,7 @@ dependencies { modImplementation("net.fabricmc.fabric-api:fabric-api:${deps.fabricApi}") - implementation ("ca.weblite:java-objc-bridge:1.0.0") + implementation("ca.weblite:java-objc-bridge:1.0.0") } tasks.processResources { diff --git a/src/client/java/dev/spiritstudios/snapper/Snapper.java b/src/client/java/dev/spiritstudios/snapper/Snapper.java index 15126dd..6f1e3a3 100644 --- a/src/client/java/dev/spiritstudios/snapper/Snapper.java +++ b/src/client/java/dev/spiritstudios/snapper/Snapper.java @@ -2,7 +2,7 @@ import dev.spiritstudios.snapper.gui.ScreenshotScreen; import dev.spiritstudios.snapper.gui.ScreenshotViewerScreen; -import dev.spiritstudios.snapper.util.ScreenshotImage; +import dev.spiritstudios.snapper.util.*; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; @@ -10,6 +10,7 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.client.option.KeyBinding; import net.minecraft.text.Text; +import net.minecraft.util.Util; import org.lwjgl.glfw.GLFW; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,7 +21,6 @@ import java.util.*; public class Snapper implements ClientModInitializer { - private final MinecraftClient client = MinecraftClient.getInstance(); public static final String MODID = "snapper"; public static final Logger LOGGER = LoggerFactory.getLogger(MODID); public static final boolean IS_IRIS_INSTALLED = FabricLoader.getInstance().isModLoaded("iris"); @@ -61,37 +61,22 @@ public void onInitializeClient() { client.player.sendMessage(Text.translatable("text.snapper.panorama_success", SCREENSHOT_MENU_KEY.getBoundKeyLocalizedText()), true); } while (RECENT_SCREENSHOT_KEY.wasPressed()) { + File latestScreenshot = ScreenshotActions.getScreenshots(client).getFirst(); + client.setScreen(new ScreenshotViewerScreen( - ScreenshotImage.of(getLatestScreenshot()), - getLatestScreenshot(), + ScreenshotImage.of(latestScreenshot, client.getTextureManager()), + latestScreenshot, null )); } }); } - private File getLatestScreenshot() { - File screenshotDir = new File(client.runDirectory, "screenshots"); - - File[] files = screenshotDir.listFiles(); - List screenshots = new ArrayList<>(List.of(files == null ? new File[0] : files)); - - screenshots.removeIf(file -> { - if (Files.isDirectory(file.toPath())) return true; - String fileType; - - try { - fileType = Files.probeContentType(file.toPath()); - } catch (IOException e) { - Snapper.LOGGER.error("Couldn't load screenshot list", e); - return true; - } - - return !Objects.equals(fileType, "image/png"); - }); - - screenshots.sort(Comparator.comparingLong(File::lastModified).reversed()); - - return screenshots.getFirst(); + public static PlatformHelper getPlatformHelper() { + return switch (Util.getOperatingSystem()) { + case WINDOWS -> new WindowsActions(); + case OSX -> new MacActions(); + default -> new WindowsActions(); + }; } } \ No newline at end of file diff --git a/src/client/java/dev/spiritstudios/snapper/gui/PanoramaViewerScreen.java b/src/client/java/dev/spiritstudios/snapper/gui/PanoramaViewerScreen.java index 50c07b6..48276f0 100644 --- a/src/client/java/dev/spiritstudios/snapper/gui/PanoramaViewerScreen.java +++ b/src/client/java/dev/spiritstudios/snapper/gui/PanoramaViewerScreen.java @@ -2,7 +2,6 @@ import dev.spiritstudios.snapper.Snapper; import dev.spiritstudios.snapper.util.ScreenshotImage; -import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.CubeMapRenderer; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.Element; @@ -32,14 +31,13 @@ public class PanoramaViewerScreen extends Screen { protected static final CubeMapRenderer FALLBACK_PANORAMA_RENDERER = new CubeMapRenderer(Identifier.ofVanilla("textures/gui/title/background/panorama")); protected static final RotatingCubeMapRenderer PANORAMA_RENDERER_CUBE = new RotatingCubeMapRenderer(PANORAMA_RENDERER); protected static final RotatingCubeMapRenderer FALLBACK_PANORAMA_RENDERER_CUBE = new RotatingCubeMapRenderer(FALLBACK_PANORAMA_RENDERER); - private static final MinecraftClient client = MinecraftClient.getInstance(); private final String title; + private final Screen parent; private boolean doBackgroundFade = true; private long backgroundFadeStart; private boolean loaded; private float backgroundAlpha; - private final Screen parent; protected PanoramaViewerScreen(String title, Screen parent) { super(Text.translatable("menu.snapper.viewermenu")); @@ -50,13 +48,13 @@ protected PanoramaViewerScreen(String title, Screen parent) { private void load() { List panorama = this.loadPanorama(); - if (panorama == null) return; + if (panorama == null || client == null) return; panorama.parallelStream().map(face -> { - ScreenshotImage icon = ScreenshotImage.forPanoramaFace(client.getTextureManager(), face.getName()); + ScreenshotImage icon = ScreenshotImage.forPanoramaFace(this.client.getTextureManager(), face.getName()); this.loadIcon(icon, face.getName(), Path.of(face.getPath())); return icon; - }).toList().forEach(ScreenshotImage::joinLoad); + }).forEach(ScreenshotImage::joinLoad); this.loaded = true; } @@ -76,12 +74,16 @@ private void loadIcon(ScreenshotImage icon, String fileName, Path filePath) { @Nullable private List loadPanorama() { - File panoramaDir = new File(client.runDirectory, "screenshots/panorama"); + if (client == null) return null; + + File panoramaDir = new File(this.client.runDirectory, "screenshots/panorama"); List panoramaFaces; if (!Files.exists(panoramaDir.toPath())) return null; File[] faceFiles = panoramaDir.listFiles(); - panoramaFaces = new ArrayList<>(List.of(faceFiles == null ? new File[0] : faceFiles)); + if (faceFiles == null) return new ArrayList<>(); + panoramaFaces = new ArrayList<>(List.of(faceFiles)); + panoramaFaces.removeIf(file -> { if (Files.isDirectory(file.toPath())) return true; String fileType; @@ -101,12 +103,15 @@ private List loadPanorama() { @Override public void close() { + if (client == null) return; client.setScreen(this.parent); } @SuppressWarnings("ResultOfMethodCallIgnored") @Override protected void init() { + if (client == null) return; + File panoramaDirectory = new File(client.runDirectory, "screenshots/panorama"); addDrawableChild(ButtonWidget.builder(Text.translatable("button.snapper.folder"), button -> { if (!panoramaDirectory.exists()) new File(String.valueOf(panoramaDirectory)).mkdirs(); diff --git a/src/client/java/dev/spiritstudios/snapper/gui/ScreenshotScreen.java b/src/client/java/dev/spiritstudios/snapper/gui/ScreenshotScreen.java index 629fa15..b854914 100644 --- a/src/client/java/dev/spiritstudios/snapper/gui/ScreenshotScreen.java +++ b/src/client/java/dev/spiritstudios/snapper/gui/ScreenshotScreen.java @@ -17,7 +17,6 @@ import org.lwjgl.glfw.GLFW; import java.io.File; -import java.io.IOException; import static dev.spiritstudios.snapper.Snapper.MODID; @@ -59,12 +58,14 @@ protected void init() { .build() ); - ButtonWidget doneButton = addDrawableChild(ButtonWidget.builder(ScreenTexts.DONE, button -> this.close()) - .width(100) - .build() + ButtonWidget doneButton = addDrawableChild( + ButtonWidget.builder(ScreenTexts.DONE, button -> this.close()) + .width(100) + .build() ); - this.deleteButton = addDrawableChild(ButtonWidget.builder(Text.translatable("button.snapper.delete"), button -> { + this.deleteButton = addDrawableChild( + ButtonWidget.builder(Text.translatable("button.snapper.delete"), button -> { if (selectedScreenshot != null) ScreenshotActions.deleteScreenshot(selectedScreenshot.screenshot, this); }) @@ -72,7 +73,8 @@ protected void init() { .build() ); - this.openButton = addDrawableChild(ButtonWidget.builder(Text.translatable("button.snapper.open"), button -> { + this.openButton = addDrawableChild( + ButtonWidget.builder(Text.translatable("button.snapper.open"), button -> { if (selectedScreenshot != null) Util.getOperatingSystem().open(selectedScreenshot.screenshot); }) @@ -80,7 +82,8 @@ protected void init() { .build() ); - this.renameButton = addDrawableChild(ButtonWidget.builder(Text.translatable("button.snapper.rename"), button -> { + this.renameButton = addDrawableChild( + ButtonWidget.builder(Text.translatable("button.snapper.rename"), button -> { if (this.selectedScreenshot != null) client.setScreen(new RenameScreenshotScreen(this.selectedScreenshot.screenshot, this)); }) @@ -88,9 +91,10 @@ protected void init() { .build() ); - this.copyButton = addDrawableChild(ButtonWidget.builder(Text.translatable("button.snapper.copy"), button -> { + this.copyButton = addDrawableChild( + ButtonWidget.builder(Text.translatable("button.snapper.copy"), button -> { if (selectedScreenshot != null) - ScreenshotActions.copyScreenshot(selectedScreenshot.screenshot); + Snapper.getPlatformHelper().copyScreenshot(selectedScreenshot.screenshot); }) .width(74) .build() @@ -126,28 +130,20 @@ protected void init() { } public void imageSelected(@Nullable ScreenshotListWidget.ScreenshotEntry screenshot) { - if (screenshot == null) { - this.copyButton.active = false; - this.deleteButton.active = false; - this.openButton.active = false; - this.renameButton.active = false; - this.viewButton.active = false; - this.selectedScreenshot = null; - } else { - this.copyButton.active = true; - this.deleteButton.active = true; - this.openButton.active = true; - this.renameButton.active = true; - this.viewButton.active = true; - this.selectedScreenshot = screenshot; - } + boolean hasScreenshot = screenshot != null; + this.copyButton.active = hasScreenshot; + this.deleteButton.active = hasScreenshot; + this.openButton.active = hasScreenshot; + this.renameButton.active = hasScreenshot; + this.viewButton.active = hasScreenshot; + this.selectedScreenshot = screenshot; } @Override public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - long handle = MinecraftClient.getInstance().getWindow().getHandle(); if (super.keyPressed(keyCode, scanCode, modifiers)) return true; + long handle = MinecraftClient.getInstance().getWindow().getHandle(); if (keyCode == GLFW.GLFW_KEY_F5) { if (client == null) return false; @@ -157,7 +153,7 @@ public boolean keyPressed(int keyCode, int scanCode, int modifiers) { if ((InputUtil.isKeyPressed(handle, GLFW.GLFW_KEY_LEFT_CONTROL) || InputUtil.isKeyPressed(handle, GLFW.GLFW_KEY_RIGHT_CONTROL)) && InputUtil.isKeyPressed(handle, InputUtil.GLFW_KEY_C)) { if (selectedScreenshot != null) { - ScreenshotActions.copyScreenshot(selectedScreenshot.screenshot); + Snapper.getPlatformHelper().copyScreenshot(selectedScreenshot.screenshot); return true; } } diff --git a/src/client/java/dev/spiritstudios/snapper/gui/ScreenshotViewerScreen.java b/src/client/java/dev/spiritstudios/snapper/gui/ScreenshotViewerScreen.java index 1cffc6e..f2e9a2f 100644 --- a/src/client/java/dev/spiritstudios/snapper/gui/ScreenshotViewerScreen.java +++ b/src/client/java/dev/spiritstudios/snapper/gui/ScreenshotViewerScreen.java @@ -115,7 +115,7 @@ protected void init() { // COPY SCREENSHOT - ButtonWidget copyButton = addDrawableChild(ButtonWidget.builder(Text.translatable("button.snapper.copy"), button -> ScreenshotActions.copyScreenshot(this.screenshot)) + ButtonWidget copyButton = addDrawableChild(ButtonWidget.builder(Text.translatable("button.snapper.copy"), button -> Snapper.getPlatformHelper().copyScreenshot(this.screenshot)) .width(100) .build() ); diff --git a/src/client/java/dev/spiritstudios/snapper/gui/widget/ScreenshotListWidget.java b/src/client/java/dev/spiritstudios/snapper/gui/widget/ScreenshotListWidget.java index 0ced122..809dfd0 100644 --- a/src/client/java/dev/spiritstudios/snapper/gui/widget/ScreenshotListWidget.java +++ b/src/client/java/dev/spiritstudios/snapper/gui/widget/ScreenshotListWidget.java @@ -4,6 +4,7 @@ import dev.spiritstudios.snapper.Snapper; import dev.spiritstudios.snapper.gui.ScreenshotScreen; import dev.spiritstudios.snapper.gui.ScreenshotViewerScreen; +import dev.spiritstudios.snapper.util.ScreenshotActions; import dev.spiritstudios.snapper.util.ScreenshotImage; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawContext; @@ -69,37 +70,12 @@ protected void clearEntries() { public CompletableFuture> load(MinecraftClient client) { return CompletableFuture.supplyAsync(() -> { - List screenshots = this.loadScreenshots(); + List screenshots = ScreenshotActions.getScreenshots(client); List entries = new ArrayList<>(); screenshots.parallelStream().forEach(file -> entries.add(new ScreenshotEntry(file, client, parent))); return entries; }); } - - private List loadScreenshots() { - File screenshotDir = new File(client.runDirectory, "screenshots"); - - File[] files = screenshotDir.listFiles(); - List screenshots = new ArrayList<>(List.of(files == null ? new File[0] : files)); - - screenshots.removeIf(file -> { - if (Files.isDirectory(file.toPath())) return true; - String fileType; - - try { - fileType = Files.probeContentType(file.toPath()); - } catch (IOException e) { - Snapper.LOGGER.error("Couldn't load screenshot list", e); - return true; - } - - return !Objects.equals(fileType, "image/png"); - }); - - screenshots.sort(Comparator.comparingLong(File::lastModified).reversed()); - return screenshots; - } - private void setEntrySelected(@Nullable ScreenshotEntry entry) { super.setSelected(entry); ScreenshotScreen parentScreen = (ScreenshotScreen) this.parent; diff --git a/src/client/java/dev/spiritstudios/snapper/util/ScreenshotActionsMac.java b/src/client/java/dev/spiritstudios/snapper/util/MacActions.java similarity index 94% rename from src/client/java/dev/spiritstudios/snapper/util/ScreenshotActionsMac.java rename to src/client/java/dev/spiritstudios/snapper/util/MacActions.java index 5216816..8f2a52d 100644 --- a/src/client/java/dev/spiritstudios/snapper/util/ScreenshotActionsMac.java +++ b/src/client/java/dev/spiritstudios/snapper/util/MacActions.java @@ -3,9 +3,10 @@ import ca.weblite.objc.Client; import ca.weblite.objc.Proxy; import dev.spiritstudios.snapper.Snapper; -import net.minecraft.client.MinecraftClient; -public class ScreenshotActionsMac { +import java.io.File; + +public class MacActions implements PlatformHelper { /* Screenshot copy logic (ScreenshotActions, ScreenshotActionsMac) heavily inspired by ScreenshotViewer by LGatodu47. (https://github.com/LGatodu47/ScreenshotViewer). @@ -61,13 +62,10 @@ of this software and associated documentation files (the "Software"), to deal SOFTWARE. */ - public static void copyScreenshotMac(String path) { - if (!MinecraftClient.IS_SYSTEM_MAC) { - return; - } - + @Override + public void copyScreenshot(File screenshot) { Client client = Client.getInstance(); - Proxy url = client.sendProxy("NSURL", "fileURLWithPath:", path); + Proxy url = client.sendProxy("NSURL", "fileURLWithPath:", screenshot.getAbsoluteFile()); Proxy image = client.sendProxy("NSImage", "alloc"); image.send("initWithContentsOfURL:", url); diff --git a/src/client/java/dev/spiritstudios/snapper/util/PlatformHelper.java b/src/client/java/dev/spiritstudios/snapper/util/PlatformHelper.java new file mode 100644 index 0000000..79bc0fb --- /dev/null +++ b/src/client/java/dev/spiritstudios/snapper/util/PlatformHelper.java @@ -0,0 +1,7 @@ +package dev.spiritstudios.snapper.util; + +import java.io.File; + +public interface PlatformHelper { + void copyScreenshot(File screenshot); +} diff --git a/src/client/java/dev/spiritstudios/snapper/util/ScreenshotActions.java b/src/client/java/dev/spiritstudios/snapper/util/ScreenshotActions.java index 263196c..41615ab 100644 --- a/src/client/java/dev/spiritstudios/snapper/util/ScreenshotActions.java +++ b/src/client/java/dev/spiritstudios/snapper/util/ScreenshotActions.java @@ -8,21 +8,20 @@ import net.minecraft.screen.ScreenTexts; import net.minecraft.text.Text; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import javax.imageio.ImageIO; import java.awt.*; -import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; -import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; public class ScreenshotActions { - private static final Clipboard clipboard = getClipboard(); - @SuppressWarnings("ResultOfMethodCallIgnored") public static void deleteScreenshot(File screenshot, Screen screen) { if (!screenshot.exists()) return; @@ -52,41 +51,34 @@ public static void renameScreenshot(File screenshot, String newName) { } } - @Nullable - private static Clipboard getClipboard() { - if (MinecraftClient.IS_SYSTEM_MAC) return null; + public static java.util.List getScreenshots(MinecraftClient client) { + File screenshotDir = new File(client.runDirectory, "screenshots"); - try { - return Toolkit.getDefaultToolkit().getSystemClipboard(); - } catch (HeadlessException e) { - Snapper.LOGGER.error("Failed to get clipboard", e); - } + File[] files = screenshotDir.listFiles(); + java.util.List screenshots = new ArrayList<>(List.of(files == null ? new File[0] : files)); - return null; - } - - public static void copyScreenshot(File screenshot) { - if (MinecraftClient.IS_SYSTEM_MAC) { - ScreenshotActionsMac.copyScreenshotMac(screenshot.getAbsolutePath()); - return; - } + screenshots.removeIf(file -> { + if (Files.isDirectory(file.toPath())) return true; + String fileType; - if (clipboard != null && screenshot.exists()) { try { - BufferedImage imageBuffer = ImageIO.read(screenshot); - - Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); - clipboard.setContents(new TransferableImage(imageBuffer), null); + fileType = Files.probeContentType(file.toPath()); } catch (IOException e) { - Snapper.LOGGER.error(String.format("Copying of image at %s failed", screenshot.toPath())); + Snapper.LOGGER.error("Couldn't load screenshot list", e); + return true; } - } + + return !Objects.equals(fileType, "image/png"); + }); + + screenshots.sort(Comparator.comparingLong(File::lastModified).reversed()); + return screenshots; } record TransferableImage(Image image) implements Transferable { @Override public DataFlavor[] getTransferDataFlavors() { - return new DataFlavor[] { + return new DataFlavor[]{ DataFlavor.imageFlavor }; } diff --git a/src/client/java/dev/spiritstudios/snapper/util/ScreenshotImage.java b/src/client/java/dev/spiritstudios/snapper/util/ScreenshotImage.java index 9ba910d..75c1f05 100644 --- a/src/client/java/dev/spiritstudios/snapper/util/ScreenshotImage.java +++ b/src/client/java/dev/spiritstudios/snapper/util/ScreenshotImage.java @@ -1,7 +1,6 @@ package dev.spiritstudios.snapper.util; import dev.spiritstudios.snapper.Snapper; -import net.minecraft.client.MinecraftClient; import net.minecraft.client.texture.NativeImage; import net.minecraft.client.texture.NativeImageBackedTexture; import net.minecraft.client.texture.TextureManager; @@ -18,14 +17,11 @@ public class ScreenshotImage implements AutoCloseable { private static final Identifier UNKNOWN_SERVER_ID = Identifier.ofVanilla("textures/misc/unknown_server.png"); - private final TextureManager textureManager; private final Identifier id; - @Nullable private NativeImageBackedTexture texture; private boolean closed; - private static final MinecraftClient client = MinecraftClient.getInstance(); private ScreenshotImage(TextureManager textureManager, Identifier id) { this.textureManager = textureManager; @@ -38,9 +34,9 @@ private ScreenshotImage(TextureManager textureManager, Identifier id, File scree this.loadIcon(screenshot.toPath()); } - public static ScreenshotImage of(File screenshot) { + public static ScreenshotImage of(File screenshot, TextureManager textureManager) { return new ScreenshotImage( - client.getTextureManager(), + textureManager, Identifier.ofVanilla( "screenshots/" + Util.replaceInvalidChars(screenshot.getName(), Identifier::isPathCharacterValid) + "/icon" ), @@ -48,20 +44,6 @@ public static ScreenshotImage of(File screenshot) { ); } - private void loadIcon(Path path) { - CompletableFuture.runAsync(() -> { - if (path == null || !Files.isRegularFile(path)) { - return; - } - - try (InputStream inputStream = Files.newInputStream(path)) { - this.load(NativeImage.read(inputStream)); - } catch (IOException error) { - Snapper.LOGGER.error("Invalid icon for screenshot {}", new File(String.valueOf(path)).getName(), error); - } - }); - } - public static ScreenshotImage forScreenshot(TextureManager textureManager, String screenshotName) { return new ScreenshotImage( textureManager, @@ -80,6 +62,18 @@ public static ScreenshotImage forPanoramaFace(TextureManager textureManager, Str ); } + private void loadIcon(Path path) { + CompletableFuture.runAsync(() -> { + if (path == null || !Files.isRegularFile(path)) return; + + try (InputStream inputStream = Files.newInputStream(path)) { + this.load(NativeImage.read(inputStream)); + } catch (IOException error) { + Snapper.LOGGER.error("Invalid icon for screenshot {}", new File(String.valueOf(path)).getName(), error); + } + }); + } + public void load(NativeImage image) { this.assertOpen(); if (image != null) { @@ -92,19 +86,21 @@ public void load(NativeImage image) { } } + /* + * Must be called on render thread + */ public void joinLoad() { - // must be called on render thread if (this.texture == null) return; this.texture.setFilter(true, false); } public void destroy() { this.assertOpen(); - if (this.texture != null) { - this.textureManager.destroyTexture(this.id); - this.texture.close(); - this.texture = null; - } + if (this.texture == null) return; + + this.textureManager.destroyTexture(this.id); + this.texture.close(); + this.texture = null; } public int getWidth() { diff --git a/src/client/java/dev/spiritstudios/snapper/util/WindowsActions.java b/src/client/java/dev/spiritstudios/snapper/util/WindowsActions.java new file mode 100644 index 0000000..865a7a9 --- /dev/null +++ b/src/client/java/dev/spiritstudios/snapper/util/WindowsActions.java @@ -0,0 +1,37 @@ +package dev.spiritstudios.snapper.util; + +import dev.spiritstudios.snapper.Snapper; +import net.minecraft.client.MinecraftClient; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.datatransfer.Clipboard; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +public class WindowsActions implements PlatformHelper { + @Override + public void copyScreenshot(File screenshot) { + if (getClipboard() == null || !screenshot.exists()) return; + + try { + BufferedImage imageBuffer = ImageIO.read(screenshot); + + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + clipboard.setContents(new ScreenshotActions.TransferableImage(imageBuffer), null); + } catch (IOException e) { + Snapper.LOGGER.error("Copying of image at {} failed", screenshot.toPath()); + } + } + + private static Clipboard getClipboard() { + try { + return Toolkit.getDefaultToolkit().getSystemClipboard(); + } catch (HeadlessException e) { + Snapper.LOGGER.error("Failed to get clipboard", e); + } + + return null; + } +}