diff --git a/build.gradle b/build.gradle index fa3d501f42..3ff3e386e5 100644 --- a/build.gradle +++ b/build.gradle @@ -50,7 +50,6 @@ sourceSets { launchWarn { java { srcDir "src/desktop/java" - include "**/LaunchWarn.java" } } } diff --git a/src/desktop/java/net/caffeinemc/mods/sodium/desktop/LaunchWarn.java b/src/desktop/java/net/caffeinemc/mods/sodium/desktop/LaunchWarn.java index 290fc496df..a1d55cb71f 100644 --- a/src/desktop/java/net/caffeinemc/mods/sodium/desktop/LaunchWarn.java +++ b/src/desktop/java/net/caffeinemc/mods/sodium/desktop/LaunchWarn.java @@ -1,48 +1,156 @@ package net.caffeinemc.mods.sodium.desktop; -import javax.swing.JOptionPane; -import javax.swing.UIManager; -import javax.swing.UnsupportedLookAndFeelException; -import java.awt.Desktop; -import java.awt.GraphicsEnvironment; +import net.caffeinemc.mods.sodium.desktop.utils.browse.BrowseUrlHandler; + +import javax.swing.*; +import java.awt.*; import java.io.IOException; -import java.net.URI; -/** - * Taken from - * https://github.com/IrisShaders/Iris/blob/6c880cd377d97ffd5de648ba4dfac7ea88897b4f/src/main/java/net/coderbot/iris/LaunchWarn.java - * and modified to fit Sodium. See Iris' license for more information. - */ public class LaunchWarn { + private static final String HELP_URL = "https://github.com/CaffeineMC/sodium-fabric/wiki/Installation"; + + private static final String RICH_MESSAGE = + "" + + "
" + + "" + + "You have tried to launch Sodium (a Minecraft mod) directly, but it is not an executable program or mod installer. Instead, " + + "you must install Fabric Loader for Minecraft, and then place this file in your mods directory." + + "
" + + "" + + "If this is your first time installing mods with Fabric Loader, then click the \"Help\" button for an installation guide." + + "
" + + "" + + ""; + + private static final String FALLBACK_MESSAGE = + "" + + "" + + "" + + "You have tried to launch Sodium (a Minecraft mod) directly, but it is not an executable program or mod installer. Instead, " + + "you must install Fabric Loader for Minecraft, and then place this file in your mods directory." + + "
" + + "" + + "If this is your first time installing mods with Fabric Loader, then visit " + HELP_URL + " for an installation guide." + + "
" + + "" + + ""; + + private static final String FAILED_TO_BROWSE_MESSAGE = + "" + + "" + + "" + + "Failed to open the default browser! Your system may be misconfigured. Please open the URL " + HELP_URL + " manually." + + "
" + + "" + + ""; + public static final String WINDOW_TITLE = "Sodium"; + public static void main(String[] args) { - String message = "You have tried to launch Sodium (a Minecraft mod) directly, but it is not an executable program or mod installer. You must install Fabric Loader for Minecraft, and place this file in your mods directory instead.\nIf this is your first time installing mods for Fabric Loader, click \"Help\" for a guide on how to do this."; - String fallback = "You have tried to launch Sodium (a Minecraft mod) directly, but it is not an executable program or mod installer. You must install Fabric Loader for Minecraft, and place this file in your mods directory instead.\nIf this is your first time installing mods for Fabric Loader, open \"https://github.com/CaffeineMC/sodium-fabric/wiki/Installation\" for a guide on how to do this."; if (GraphicsEnvironment.isHeadless()) { - System.err.println(fallback); + showHeadlessError(); } else { + showGraphicalError(); + } + } + + private static void showHeadlessError() { + System.err.println(FALLBACK_MESSAGE); + } + + private static void showGraphicalError() { + trySetSystemLookAndFeel(); + trySetSystemFontPreferences(); + + BrowseUrlHandler browseUrlHandler = BrowseUrlHandler.createImplementation(); + + if (browseUrlHandler != null) { + showRichGraphicalDialog(browseUrlHandler); + } else { + showFallbackGraphicalDialog(); + } + + System.exit(0); + } + + private static void showRichGraphicalDialog(BrowseUrlHandler browseUrlHandler) { + int selectedOption = showDialogBox(RICH_MESSAGE, WINDOW_TITLE, JOptionPane.YES_NO_OPTION, + JOptionPane.INFORMATION_MESSAGE, new String[] { "Help", "Close" }, JOptionPane.YES_OPTION); + + if (selectedOption == JOptionPane.YES_OPTION) { + log("Opening URL: " + HELP_URL); + try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } catch (ReflectiveOperationException | UnsupportedLookAndFeelException ignored) { - // Ignored + browseUrlHandler.browseTo(HELP_URL); + } catch (IOException e) { + log("Failed to open default web browser!", e); + + showDialogBox(FAILED_TO_BROWSE_MESSAGE, WINDOW_TITLE, JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, null, JOptionPane.DEFAULT_OPTION); } + } + } - if (Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { - int option = JOptionPane.showOptionDialog(null, message, "Sodium", JOptionPane.YES_NO_OPTION, - JOptionPane.INFORMATION_MESSAGE, null, new Object[] { "Help", "Cancel" }, JOptionPane.YES_OPTION); - - if (option == JOptionPane.YES_OPTION) { - try { - Desktop.getDesktop().browse(URI.create("https://github.com/CaffeineMC/sodium-fabric/wiki/Installation")); - } catch (IOException e) { - e.printStackTrace(); - } - } - } else { - // Fallback for Linux, etc users with no "default" browser - JOptionPane.showMessageDialog(null, fallback); + private static void showFallbackGraphicalDialog() { + // Fallback for Linux, etc users with no "default" browser + showDialogBox(FALLBACK_MESSAGE, WINDOW_TITLE, JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null, null); + } + + private static int showDialogBox(String message, + String title, + int optionType, + int messageType, + String[] options, + Object initialValue) { + JOptionPane pane = new JOptionPane(message, messageType, optionType, null, options, initialValue); + + JDialog dialog = pane.createDialog(title); + dialog.setVisible(true); + + Object selectedValue = pane.getValue(); + + if (selectedValue == null) { + return JOptionPane.CLOSED_OPTION; + } + + // If there is not an array of option buttons: + if (options == null) { + if (selectedValue instanceof Integer) { + return (Integer) selectedValue; } + + return JOptionPane.CLOSED_OPTION; } - System.exit(0); + // If there is an array of option buttons: + for (int counter = 0; counter < options.length; counter++) { + String option = options[counter]; + + if (option.equals(selectedValue)) { + return counter; + } + } + + return JOptionPane.CLOSED_OPTION; + } + + private static void trySetSystemLookAndFeel() { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (ReflectiveOperationException | UnsupportedLookAndFeelException ignored) { + // Ignored + } + } + + private static void trySetSystemFontPreferences() { + System.setProperty("awt.useSystemAAFontSettings", "on"); // Why is this not a default? + } + + private static void log(String message) { + System.err.println(message); + } + + private static void log(String message, Throwable exception) { + System.err.println(message); + exception.printStackTrace(System.err); } } diff --git a/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/BrowseUrlHandler.java b/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/BrowseUrlHandler.java new file mode 100644 index 0000000000..633021bf50 --- /dev/null +++ b/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/BrowseUrlHandler.java @@ -0,0 +1,27 @@ +package net.caffeinemc.mods.sodium.desktop.utils.browse; + +import java.io.IOException; + +public interface BrowseUrlHandler { + void browseTo(String url) throws IOException; + + static BrowseUrlHandler createImplementation() { + // OpenJDK doesn't use xdg-open and fails to provide an implementation on most systems. + if (XDGImpl.isSupported()) { + // Apparently xdg-open is just broken for some desktop environments, because *for some reason* + // setting a default browser is complicated. + if (KDEImpl.isSupported()) { + return new KDEImpl(); + } else if (GNOMEImpl.isSupported()) { + return new GNOMEImpl(); + } + + // If the user's desktop environment isn't KDE or GNOME, then we can only rely on xdg-open being present. + return new XDGImpl(); + } else if (CrossPlatformImpl.isSupported()) { + return new CrossPlatformImpl(); + } + + return null; + } +} diff --git a/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/CrossPlatformImpl.java b/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/CrossPlatformImpl.java new file mode 100644 index 0000000000..f9983d1cad --- /dev/null +++ b/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/CrossPlatformImpl.java @@ -0,0 +1,17 @@ +package net.caffeinemc.mods.sodium.desktop.utils.browse; + +import java.awt.*; +import java.io.IOException; +import java.net.URI; + +class CrossPlatformImpl implements BrowseUrlHandler { + public static boolean isSupported() { + return Desktop.getDesktop() + .isSupported(Desktop.Action.BROWSE); + } + + @Override + public void browseTo(String url) throws IOException { + Desktop.getDesktop().browse(URI.create(url)); + } +} diff --git a/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/GNOMEImpl.java b/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/GNOMEImpl.java new file mode 100644 index 0000000000..6bf377a7cf --- /dev/null +++ b/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/GNOMEImpl.java @@ -0,0 +1,15 @@ +package net.caffeinemc.mods.sodium.desktop.utils.browse; + +import java.io.IOException; + +class GNOMEImpl implements BrowseUrlHandler { + public static boolean isSupported() { + return XDGImpl.isSupported() && System.getenv("XDG_CURRENT_DESKTOP").equals("GNOME"); + } + + @Override + public void browseTo(String url) throws IOException { + Runtime.getRuntime() + .exec(new String[] { "gnome-open", url }); + } +} diff --git a/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/KDEImpl.java b/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/KDEImpl.java new file mode 100644 index 0000000000..d62412fdd1 --- /dev/null +++ b/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/KDEImpl.java @@ -0,0 +1,15 @@ +package net.caffeinemc.mods.sodium.desktop.utils.browse; + +import java.io.IOException; + +class KDEImpl implements BrowseUrlHandler { + public static boolean isSupported() { + return XDGImpl.isSupported() && System.getenv("XDG_CURRENT_DESKTOP").equals("KDE"); + } + + @Override + public void browseTo(String url) throws IOException { + Runtime.getRuntime() + .exec(new String[] { "kde-open", url }); + } +} diff --git a/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/XDGImpl.java b/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/XDGImpl.java new file mode 100644 index 0000000000..42adc65c1d --- /dev/null +++ b/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/XDGImpl.java @@ -0,0 +1,21 @@ +package net.caffeinemc.mods.sodium.desktop.utils.browse; + +import net.caffeinemc.mods.sodium.desktop.utils.browse.BrowseUrlHandler; + +import java.io.IOException; +import java.util.Locale; + +class XDGImpl implements BrowseUrlHandler { + public static boolean isSupported() { + String os = System.getProperty("os.name") + .toLowerCase(Locale.ROOT); + + return os.equals("linux"); + } + + @Override + public void browseTo(String url) throws IOException { + Runtime.getRuntime() + .exec(new String[] { "xdg-open", url }); + } +}