Skip to content

Commit

Permalink
Over-engineer the launch warning beyond reason
Browse files Browse the repository at this point in the history
  • Loading branch information
jellysquid3 committed Jan 26, 2024
1 parent 7ad9414 commit 9c045da
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 33 deletions.
1 change: 0 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ sourceSets {
launchWarn {
java {
srcDir "src/desktop/java"
include "**/LaunchWarn.java"
}
}
}
Expand Down
172 changes: 140 additions & 32 deletions src/desktop/java/net/caffeinemc/mods/sodium/desktop/LaunchWarn.java
Original file line number Diff line number Diff line change
@@ -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 =
"<html>" +
"<body>" +
"<p style='width: 600px; padding: 0 0 8px 0;'>" +
"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." +
"</p>" +
"<p style='width: 600px; padding: 0 0 8px 0;'>" +
"If this is your first time installing mods with Fabric Loader, then click the \"Help\" button for an installation guide." +
"</p>" +
"</body>" +
"</html>";

private static final String FALLBACK_MESSAGE =
"<html>" +
"<body>" +
"<p style='width: 600px; padding: 0 0 8px 0;'>" +
"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." +
"</p>" +
"<p style='width: 600px; padding: 0 0 8px 0;'>" +
"If this is your first time installing mods with Fabric Loader, then visit <i>" + HELP_URL + "</i> for an installation guide." +
"</p>" +
"</body>" +
"</html>";

private static final String FAILED_TO_BROWSE_MESSAGE =
"<html>" +
"<body>" +
"<p style='width: 400px; padding: 0 0 8px 0;'>" +
"Failed to open the default browser! Your system may be misconfigured. Please open the URL <i>" + HELP_URL + "</i> manually." +
"</p>" +
"</body>" +
"</html>";
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);
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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));
}
}
Original file line number Diff line number Diff line change
@@ -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 });
}
}
Original file line number Diff line number Diff line change
@@ -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 });
}
}
Original file line number Diff line number Diff line change
@@ -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 });
}
}

0 comments on commit 9c045da

Please sign in to comment.