前一段在 Fedora 43 上尝试运行 HMCL 时遇到了一个问题。即明明已经安装了 OpenJDK,却提示缺少 JavaFX 组件。经过一番折腾,终于找到了解决方案,在此记录分享


问题现象

直接 java -jar HMCL-3.7.5.jar 出现如下错误

dev@192:~/.local/opt/hmcl$ java -jar HMCL-3.7.5.jar 
[09:47:11] [@.util.SelfDependencyPatcher.patch/INFO] Missing JavaFX dependencies, attempting to patch in missing classes
Exception in thread "main" java.awt.HeadlessException: 
No X11 DISPLAY variable was set,
or no headful library support was found,
but this program performed an operation which requires it.
	at java.desktop/java.awt.GraphicsEnvironment.checkHeadless(GraphicsEnvironment.java:158)
	at java.desktop/java.awt.Window.<init>(Window.java:518)
	at java.desktop/java.awt.Frame.<init>(Frame.java:428)
	at java.desktop/java.awt.Frame.<init>(Frame.java:393)
	at java.desktop/javax.swing.SwingUtilities$SharedOwnerFrame.<init>(SwingUtilities.java:1919)
	at java.desktop/javax.swing.SwingUtilities.getSharedOwnerFrame(SwingUtilities.java:1987)
	at java.desktop/javax.swing.JDialog.<init>(JDialog.java:276)
	at java.desktop/javax.swing.JDialog.<init>(JDialog.java:210)
	at java.desktop/javax.swing.JDialog.<init>(JDialog.java:158)
	at org.jackhuang.hmcl.util.SelfDependencyPatcher$ProgressFrame.<init>(SelfDependencyPatcher.java:396)
	at org.jackhuang.hmcl.util.SelfDependencyPatcher.fetchDependencies(SelfDependencyPatcher.java:277)
	at org.jackhuang.hmcl.util.SelfDependencyPatcher.patch(SelfDependencyPatcher.java:185)
	at org.jackhuang.hmcl.EntryPoint.checkJavaFX(EntryPoint.java:185)
	at org.jackhuang.hmcl.EntryPoint.main(EntryPoint.java:55)
	at org.jackhuang.hmcl.Main.main(Main.java:78)
^Cdev@192:~/.local/opt/hmcl$ 

这个 java.awt.HeadlessException 错误表明 Java 无法找到图形显示环境

Fedora 43 (已全面转向 Wayland),这个问题尤其突出(不再默认支持 X11,因为:

  • Java AWT/Swing 在 Wayland 下支持不完善,需要强制使用 X11 后端
  • 自 Fedora **Fedora 38 起,官方仓库不再提供 **java-\*-openjdk-javafx 包(OpenJDK 已剥离 JavaFX),导致 HMCL 无法自动获取 JavaFX 依赖

正确解决方案:手动提供 JavaFX 并启用 X11 兼容

由于系统无法通过 dnf 安装 JavaFX,你需要 手动下载 JavaFX SDK 并运行 HMCL 时显式指定模块路径


解决方案

1. 下载 JavaFX for Linux (x64)

访问 GluonHQ 官网 (https://gluonhq.com/products/javafx/) 下载适合 Linux x64 的 JavaFX SDK。 或者使用命令行:

下载后解压,例如:

cd ~/Downloads
wget https://download2.gluonhq.com/openjfx/21.0.2/openjfx-21.0.2_linux-x64_bin-sdk.zip
unzip openjfx-21.0.2_linux-x64_bin-sdk.zip
mv javafx-sdk-21.0.2 ~/javafx-sdk

2. 在 Wayland 下启用 X11兼容

虽然 GNOME 默认用 Wayland,但 Java AWT/SwingWayland 下支持不完整,建议强制使用 X11 后端:

export GDK_BACKEND=x11
export _JAVA_AWT_WM_NONREPARENTING=1  # 避免窗口管理器问题(可选)

3. 使用 JavaFX 模块运行 HMCL

cd ~/xxx # 进入 HMCL-3.7.5.jar 所在的目录
java \
  --module-path ~/javafx-sdk/lib \
  --add-modules javafx.controls,javafx.fxml,javafx.swing,javafx.media \
  -jar HMCL-3.7.5.jar

4. 结束了?

dev@192:~/.local/opt/hmcl$ java --module-path ~/javafx-sdk/lib --add-modules javafx.controls,javafx.fxml,javafx.swing,javafx.media -jar HMCL-3.7.5.jar 
[10:03:10] [@.Launcher.main/INFO] *** HMCL 3.7.5 ***
[10:03:10] [@.Launcher.main/INFO] Operating System: Fedora Linux 43 (Workstation Edition) (Linux 6.17.8-300.fc43.x86_64)
[10:03:10] [@.Launcher.main/INFO] System Architecture: x86-64
[10:03:10] [@.Launcher.main/INFO] Native Encoding: UTF-8
[10:03:10] [@.Launcher.main/INFO] JNU Encoding: UTF-8
[10:03:10] [@.Launcher.main/INFO] Java Architecture: x86-64
[10:03:10] [@.Launcher.main/INFO] Java Version: 25.0.1, Red Hat, Inc.
[10:03:10] [@.Launcher.main/INFO] Java VM Version: OpenJDK 64-Bit Server VM (mixed mode, sharing), Red Hat, Inc.
[10:03:10] [@.Launcher.main/INFO] Java Home: /usr/lib/jvm/java-25-openjdk
[10:03:10] [@.Launcher.main/INFO] Current Directory: /home/dev/.local/opt/hmcl
[10:03:10] [@.Launcher.main/INFO] HMCL Global Directory: /home/dev/.local/share/hmcl
[10:03:10] [@.Launcher.main/INFO] HMCL Current Directory: /home/dev/.local/opt/hmcl/.hmcl
[10:03:10] [@.Launcher.main/INFO] HMCL Jar Path: /home/dev/.local/opt/hmcl/HMCL-3.7.5.jar
[10:03:10] [@.Launcher.main/INFO] HMCL Log File: /home/dev/.local/opt/hmcl/.hmcl/logs/2025-11-22T10-03-10.log
[10:03:10] [@.Launcher.main/INFO] JVM Max Memory: 4480 MiB
[10:03:10] [@.Launcher.main/INFO] Metaspace: 3.89 MiB
[10:03:10] [@.Launcher.main/INFO] Native Backend: None
[10:03:10] [@.Launcher.main/INFO] XDG Session Type: wayland
[10:03:10] [@.Launcher.main/INFO] XDG Current Desktop: GNOME
[10:03:10] [@.util.platform.SystemInfo.initialize/INFO] System Info:

 - CPU: AMD Ryzen 5 3400G (4 Cores / 8 Threads)
 - GPU: AMD Radeon Vega 11 Graphics [Integrated]
 - Memory: 3.46 GiB / 17.5 GiB (19%)
   [10:03:11] [@.Launcher.start/INFO] JavaFX Version: 21.0.2+5
   [10:03:11] [@.Launcher.start/INFO] Prism Pipeline: com.sun.prism.es2.ES2Pipeline
   [10:03:11] [@.Launcher.start/INFO] Dark Mode: false
   [10:03:11] [@.Launcher.start/INFO] Reduced Motion: false
   [10:03:11] [@.setting.ConfigHolder.init/INFO] Config location: /home/dev/.local/opt/hmcl/.hmcl/hmcl.json
   [10:03:11] [@.setting.ConfigHolder.loadGlobalConfig/INFO] Creating an empty global config
   [10:03:11] [@.setting.ConfigHolder.init/INFO] Creating config file /home/dev/.local/opt/hmcl/.hmcl/hmcl.json
   [10:03:11] [@.task.AsyncTaskExecutor.lambda$executeNormalTask$19/TRACE] Executing task: org.jackhuang.hmcl.game.GameRepository.refreshVersionsAsync(GameRepository.java:91)
   [10:03:11] [@.task.AsyncTaskExecutor.lambda$executeNormalTask$19/TRACE] Executing task: org.jackhuang.hmcl.game.GameRepository.refreshVersionsAsync(GameRepository.java:91)
   [10:03:11] [@.event.EventBus.fireEvent/INFO] RefreshingVersionsEvent [source=HMCLGameRepository [versions=null, baseDirectory=.minecraft]] gets fired
   [10:03:11] [@.event.EventBus.fireEvent/INFO] RefreshingVersionsEvent [source=HMCLGameRepository [versions=null, baseDirectory=.minecraft]] gets fired
   [10:03:11] [@.event.EventBus.fireEvent/INFO] RefreshedVersionsEvent [source=HMCLGameRepository [versions=[], baseDirectory=.minecraft]] gets fired
   [10:03:11] [@.event.EventBus.fireEvent/INFO] RefreshedVersionsEvent [source=HMCLGameRepository [versions=[], baseDirectory=.minecraft]] gets fired
   [10:03:11] [@.task.AsyncTaskExecutor.lambda$executeNormalTask$29/TRACE] Task finished: org.jackhuang.hmcl.game.GameRepository.refreshVersionsAsync(GameRepository.java:91)
   [10:03:11] [@.task.AsyncTaskExecutor.lambda$executeNormalTask$29/TRACE] Task finished: org.jackhuang.hmcl.game.GameRepository.refreshVersionsAsync(GameRepository.java:91)
   [10:03:11] [@.ui.Controllers.initialize/INFO] Start initializing application
   [10:03:11] [@.util.FileSaver.saveSync/INFO] Saving file /home/dev/.local/opt/hmcl/.hmcl/hmcl.json
   [10:03:11] [@.java.JavaManager.searchPotentialJavaExecutables/TRACE] Finished Java lookup, found 1
 - JRE 25.0.1 (x86-64, Red Hat, Inc.): /usr/lib/jvm/java-25-openjdk/bin/java
   [10:03:11] [@.setting.FontManager.findByFcMatch/INFO] Load font file: /usr/share/fonts/google-noto-sans-cjk-vf-fonts/NotoSansCJK-VF.ttc
   [10:03:12] [@.setting.FontManager.<clinit>/INFO] Font: Noto Sans CJK SC
   [10:03:12] [@.auth.authlibinjector.AuthlibInjectorServer.refreshMetadata/INFO] authlib-injector server metadata refreshed: https://littleskin.cn/api/yggdrasil/
   [10:03:12] [@.ui.construct.JFXDialogPane.push/INFO] JFXDialogPane@7f64efdf [JFXDialogLayout@1db3cfeb[styleClass=jfx-dialog-layout]]
   [10:03:12] [@.util.FileSaver.saveSync/INFO] Saving file /home/dev/.local/opt/hmcl/.hmcl/hmcl.json
   [10:03:12] [@.upgrade.IntegrityChecker.isSelfVerified/INFO] Successfully verified current JAR
   [10:03:13] [@.upgrade.UpdateChecker.lambda$requestCheckUpdate$2/INFO] Latest version (STABLE, preview=false) is [3.7.5 from http://mirrors.cloud.tencent.com/nexus/repository/maven-public/org/glavo/hmcl/hmcl-stable/3.7.5/hmcl-stable-3.7.5.jar]
   [10:03:14] [@.util.FileSaver.saveSync/INFO] Saving file /home/dev/.local/share/hmcl/config.json
   [10:03:14] [@.ui.construct.JFXDialogPane.pop/INFO] JFXDialogPane@7f64efdf [], removed: true, object: JFXDialogLayout@1db3cfeb[styleClass=jfx-dialog-layout]
   [10:03:22] [@.Launcher.stopApplication/INFO] Stopping application.
   at java.base/java.lang.Thread.getStackTrace(Thread.java:2193)
   at org.jackhuang.hmcl.Launcher.stopApplication(Launcher.java:286)
   at org.jackhuang.hmcl.ui.decorator.DecoratorController.lambda$new$1(DecoratorController.java:108)
   at org.jackhuang.hmcl.ui.decorator.Decorator.close(Decorator.java:302)
   at org.jackhuang.hmcl.ui.decorator.DecoratorSkin.lambda$new$7(DecoratorSkin.java:248)
   at javafx.base@21.0.2/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
   at javafx.base@21.0.2/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:232)
   at javafx.base@21.0.2/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:189)
   at javafx.base@21.0.2/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
   at javafx.base@21.0.2/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
   at javafx.base@21.0.2/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
   at javafx.base@21.0.2/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
   at javafx.base@21.0.2/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
   at javafx.base@21.0.2/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
   at javafx.base@21.0.2/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
   at javafx.base@21.0.2/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
   at javafx.base@21.0.2/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
   at javafx.base@21.0.2/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
   at javafx.base@21.0.2/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
   at javafx.base@21.0.2/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
   at javafx.base@21.0.2/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
   at javafx.base@21.0.2/javafx.event.Event.fireEvent(Event.java:198)
   at javafx.graphics@21.0.2/javafx.scene.Node.fireEvent(Node.java:8875)
   at javafx.controls@21.0.2/javafx.scene.control.Button.fire(Button.java:203)
   at javafx.controls@21.0.2/com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:207)
   at javafx.controls@21.0.2/com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
   at javafx.base@21.0.2/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:247)
   at javafx.base@21.0.2/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
   at javafx.base@21.0.2/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:232)
   at javafx.base@21.0.2/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:189)
   at javafx.base@21.0.2/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
   at javafx.base@21.0.2/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
   at javafx.base@21.0.2/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
   at javafx.base@21.0.2/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
   at javafx.base@21.0.2/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
   at javafx.base@21.0.2/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
   at javafx.base@21.0.2/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
   at javafx.base@21.0.2/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
   at javafx.base@21.0.2/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
   at javafx.base@21.0.2/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
   at javafx.base@21.0.2/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
   at javafx.base@21.0.2/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
   at javafx.base@21.0.2/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
   at javafx.base@21.0.2/javafx.event.Event.fireEvent(Event.java:198)
   at javafx.graphics@21.0.2/javafx.scene.Scene$MouseHandler.process(Scene.java:3984)
   at javafx.graphics@21.0.2/javafx.scene.Scene.processMouseEvent(Scene.java:1890)
   at javafx.graphics@21.0.2/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2708)
   at javafx.graphics@21.0.2/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:411)
   at javafx.graphics@21.0.2/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:301)
   at java.base/java.security.AccessController.doPrivileged(AccessController.java:138)
   at javafx.graphics@21.0.2/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:450)
   at javafx.graphics@21.0.2/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:424)
   at javafx.graphics@21.0.2/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:449)
   at javafx.graphics@21.0.2/com.sun.glass.ui.View.handleMouseEvent(View.java:551)
   at javafx.graphics@21.0.2/com.sun.glass.ui.View.notifyMouse(View.java:937)
   at javafx.graphics@21.0.2/com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
   at javafx.graphics@21.0.2/com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$10(GtkApplication.java:263)
   at java.base/java.lang.Thread.run(Thread.java:1474)

[10:03:22] [@.task.Schedulers.shutdown/INFO] Shutting down executor services.
[10:03:22] [@.util.FileSaver.saveSync/INFO] Saving file /home/dev/.local/opt/hmcl/.hmcl/hmcl.json
dev@192:~/.local/opt/hmcl$ 

最后优化

每次输入长串命令很麻烦,可以写一个脚本

1. 将 HMCL.jar 放置于 /home/dev/.local/opt/hmcl/

mkdir /home/dev/.local/opt/hmcl/
cd /home/dev/.local/opt/hmcl/ # 这里 dev 字样换成你的用户名 

2. 编写启动脚本

vim run.sh

run.sh:

#!/bin/bash
cd "$(dirname "$0")"
export GDK_BACKEND=x11
java \
  --module-path "$HOME/javafx-sdk/lib" \
  --add-modules javafx.controls,javafx.fxml,javafx.swing,javafx.media \
  -jar HMCL-3.7.5.jar

赋予执行权限:

chmod +x run.sh

3. 创建桌面快捷方式

如果想从应用菜单启动,可以创建 .desktop 文件

图标可以直接下载官方仓库里的 https://github.com/HMCL-dev/HMCL/blob/main/HMCL/src/main/resources/assets/img/icon%408x.png

如果访问失败可以用我上传的蓝奏云 (图像来源即上述仓库) https://wwaoe.lanzoue.com/iTqn13ijfbre 密码:8smb

我们开始

cd ~.local/share/applications/
vim hmcl.desktop

hmcl.desktop:

填入以下内容(请将 Exec 和 Icon 路径替换为实际位置):

[Desktop Entry]
Version=1.0
Type=Application
Name=HMCL
Comment=HMCL
Exec=/home/dev/.local/opt/hmcl/run.sh
Icon=/home/dev/.local/opt/hmcl/hmcl.png
Terminal=false
StartupNotify=true
Categories=Games;Minecraft;

4. 更新桌面数据库

update-desktop-database ~/.local/share/applications

结束了,再一次