diff --git a/src/main/kotlin/app/termora/Application.kt b/src/main/kotlin/app/termora/Application.kt index df0bf68..1ca2785 100644 --- a/src/main/kotlin/app/termora/Application.kt +++ b/src/main/kotlin/app/termora/Application.kt @@ -3,7 +3,6 @@ package app.termora import com.formdev.flatlaf.util.SystemInfo import com.jthemedetecor.util.OsInfo import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.serialization.json.Json import okhttp3.OkHttpClient @@ -123,19 +122,18 @@ object Application { return "Termora" } - @Suppress("OPT_IN_USAGE") fun browse(uri: URI, async: Boolean = true) { // https://github.com/TermoraDev/termora/issues/178 if (SystemInfo.isWindows && uri.scheme == "file") { if (async) { - GlobalScope.launch(Dispatchers.IO) { tryBrowse(uri) } + swingCoroutineScope.launch(Dispatchers.IO) { tryBrowse(uri) } } else { tryBrowse(uri) } } else if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { Desktop.getDesktop().browse(uri) } else if (async) { - GlobalScope.launch(Dispatchers.IO) { tryBrowse(uri) } + swingCoroutineScope.launch(Dispatchers.IO) { tryBrowse(uri) } } else { tryBrowse(uri) } diff --git a/src/main/kotlin/app/termora/ApplicationRunner.kt b/src/main/kotlin/app/termora/ApplicationRunner.kt index 5757d38..916fde1 100644 --- a/src/main/kotlin/app/termora/ApplicationRunner.kt +++ b/src/main/kotlin/app/termora/ApplicationRunner.kt @@ -12,9 +12,7 @@ import com.jthemedetecor.OsThemeDetector import com.mixpanel.mixpanelapi.ClientDelivery import com.mixpanel.mixpanelapi.MessageBuilder import com.mixpanel.mixpanelapi.MixpanelAPI -import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import org.apache.commons.io.FileUtils import org.apache.commons.lang3.LocaleUtils @@ -51,8 +49,7 @@ class ApplicationRunner { val enableAnalytics = measureTimeMillis { enableAnalytics() } // init ActionManager、KeymapManager - @Suppress("OPT_IN_USAGE") - GlobalScope.launch(Dispatchers.IO) { + swingCoroutineScope.launch(Dispatchers.IO) { ActionManager.getInstance() KeymapManager.getInstance() } @@ -89,9 +86,8 @@ class ApplicationRunner { } } - @Suppress("OPT_IN_USAGE") private fun clearTemporary() { - GlobalScope.launch(Dispatchers.IO) { + swingCoroutineScope.launch(Dispatchers.IO) { // 启动时清除 FileUtils.cleanDirectory(Application.getTemporaryDir()) } @@ -273,13 +269,12 @@ class ApplicationRunner { /** * 统计 https://mixpanel.com */ - @OptIn(DelicateCoroutinesApi::class) private fun enableAnalytics() { if (Application.isUnknownVersion()) { return } - GlobalScope.launch(Dispatchers.IO) { + swingCoroutineScope.launch(Dispatchers.IO) { try { val properties = JSONObject() properties.put("os", SystemUtils.OS_NAME) diff --git a/src/main/kotlin/app/termora/Database.kt b/src/main/kotlin/app/termora/Database.kt index f4cde19..e50129c 100644 --- a/src/main/kotlin/app/termora/Database.kt +++ b/src/main/kotlin/app/termora/Database.kt @@ -11,7 +11,6 @@ import app.termora.terminal.CursorStyle import jetbrains.exodus.bindings.StringBinding import jetbrains.exodus.env.* import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import org.apache.commons.io.IOUtils import org.apache.commons.lang3.StringUtils @@ -348,8 +347,7 @@ class Database private constructor(private val env: Environment) : Disposable { private val properties = Collections.synchronizedMap(mutableMapOf()) init { - @Suppress("OPT_IN_USAGE") - GlobalScope.launch(Dispatchers.IO) { properties.putAll(getProperties()) } + swingCoroutineScope.launch(Dispatchers.IO) { properties.putAll(getProperties()) } } protected open fun getString(key: String): String? { diff --git a/src/main/kotlin/app/termora/DialogWrapper.kt b/src/main/kotlin/app/termora/DialogWrapper.kt index b54dffd..9d625b9 100644 --- a/src/main/kotlin/app/termora/DialogWrapper.kt +++ b/src/main/kotlin/app/termora/DialogWrapper.kt @@ -222,7 +222,7 @@ abstract class DialogWrapper(owner: Window?) : JDialog(owner) { return } - doCancelAction() + SwingUtilities.invokeLater { doCancelAction() } } }) diff --git a/src/main/kotlin/app/termora/HostDialog.kt b/src/main/kotlin/app/termora/HostDialog.kt index 4da42de..a05ad48 100644 --- a/src/main/kotlin/app/termora/HostDialog.kt +++ b/src/main/kotlin/app/termora/HostDialog.kt @@ -2,8 +2,10 @@ package app.termora import app.termora.actions.AnAction import app.termora.actions.AnActionEvent -import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import kotlinx.coroutines.swing.Swing +import kotlinx.coroutines.withContext import org.apache.commons.lang3.exception.ExceptionUtils import org.apache.sshd.client.SshClient import org.apache.sshd.client.session.ClientSession @@ -51,8 +53,7 @@ class HostDialog(owner: Window, host: Host? = null) : DialogWrapper(owner) { putValue(NAME, "${I18n.getString("termora.new-host.test-connection")}...") isEnabled = false - @OptIn(DelicateCoroutinesApi::class) - GlobalScope.launch(Dispatchers.IO) { + swingCoroutineScope.launch(Dispatchers.IO) { testConnection(pane.getHost()) withContext(Dispatchers.Swing) { putValue(NAME, I18n.getString("termora.new-host.test-connection")) diff --git a/src/main/kotlin/app/termora/HostOptionsPane.kt b/src/main/kotlin/app/termora/HostOptionsPane.kt index c727a3b..58ffc78 100644 --- a/src/main/kotlin/app/termora/HostOptionsPane.kt +++ b/src/main/kotlin/app/termora/HostOptionsPane.kt @@ -10,7 +10,6 @@ import com.formdev.flatlaf.util.SystemInfo import com.jgoodies.forms.builder.FormBuilder import com.jgoodies.forms.layout.FormLayout import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.swing.Swing import kotlinx.coroutines.withContext @@ -1123,8 +1122,7 @@ open class HostOptionsPane : OptionsPane() { addComponentListener(object : ComponentAdapter() { override fun componentShown(e: ComponentEvent) { removeComponentListener(this) - @Suppress("OPT_IN_USAGE") - GlobalScope.launch(Dispatchers.IO) { + swingCoroutineScope.launch(Dispatchers.IO) { for (commPort in SerialPort.getCommPorts()) { withContext(Dispatchers.Swing) { serialPortComboBox.addItem(commPort.systemPortName) diff --git a/src/main/kotlin/app/termora/OptionPane.kt b/src/main/kotlin/app/termora/OptionPane.kt index 6453e2e..74e2836 100644 --- a/src/main/kotlin/app/termora/OptionPane.kt +++ b/src/main/kotlin/app/termora/OptionPane.kt @@ -5,7 +5,9 @@ import com.formdev.flatlaf.FlatClientProperties import com.formdev.flatlaf.extras.components.FlatTextPane import com.formdev.flatlaf.util.SystemInfo import com.jetbrains.JBR -import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import kotlinx.coroutines.swing.Swing import org.apache.commons.lang3.StringUtils import java.awt.BorderLayout @@ -20,6 +22,8 @@ import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds object OptionPane { + private val coroutineScope = swingCoroutineScope + fun showConfirmDialog( parentComponent: Component?, message: Any, @@ -104,9 +108,8 @@ object OptionPane { val dialog = initDialog(pane.createDialog(parentComponent, title)) if (duration.inWholeMilliseconds > 0) { dialog.addWindowListener(object : WindowAdapter() { - @OptIn(DelicateCoroutinesApi::class) override fun windowOpened(e: WindowEvent) { - GlobalScope.launch(Dispatchers.Swing) { + coroutineScope.launch(Dispatchers.Swing) { delay(duration.inWholeMilliseconds) if (dialog.isVisible) { dialog.isVisible = false diff --git a/src/main/kotlin/app/termora/Scope.kt b/src/main/kotlin/app/termora/Scope.kt index c871165..75f4020 100644 --- a/src/main/kotlin/app/termora/Scope.kt +++ b/src/main/kotlin/app/termora/Scope.kt @@ -1,5 +1,10 @@ package app.termora +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import kotlinx.coroutines.swing.Swing import org.slf4j.LoggerFactory import java.awt.Component import java.awt.Window @@ -8,6 +13,8 @@ import javax.swing.JPopupMenu import javax.swing.SwingUtilities import kotlin.reflect.KClass +val swingCoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Swing) + @Suppress("UNCHECKED_CAST") open class Scope( private val beans: MutableMap, Any> = ConcurrentHashMap(), @@ -151,6 +158,7 @@ class ApplicationScope private constructor() : Scope() { if (log.isInfoEnabled) { log.info("ApplicationScope disposed") } + swingCoroutineScope.cancel() super.dispose() } diff --git a/src/main/kotlin/app/termora/SettingsOptionsPane.kt b/src/main/kotlin/app/termora/SettingsOptionsPane.kt index 934e58e..9f76988 100644 --- a/src/main/kotlin/app/termora/SettingsOptionsPane.kt +++ b/src/main/kotlin/app/termora/SettingsOptionsPane.kt @@ -36,8 +36,10 @@ import com.jgoodies.forms.builder.FormBuilder import com.jgoodies.forms.layout.FormLayout import com.jthemedetecor.OsThemeDetector import com.sun.jna.LastErrorException -import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import kotlinx.coroutines.swing.Swing +import kotlinx.coroutines.withContext import kotlinx.serialization.json.* import org.apache.commons.codec.binary.Base64 import org.apache.commons.io.IOUtils @@ -615,10 +617,9 @@ class SettingsOptionsPane : OptionsPane() { add(getCenterComponent(), BorderLayout.CENTER) } - @OptIn(DelicateCoroutinesApi::class) private fun initEvents() { syncConfigButton.addActionListener { - GlobalScope.launch(Dispatchers.IO) { sync() } + swingCoroutineScope.launch(Dispatchers.IO) { sync() } } typeComboBox.addItemListener { diff --git a/src/main/kotlin/app/termora/ThemeManager.kt b/src/main/kotlin/app/termora/ThemeManager.kt index 9e7def8..068263d 100644 --- a/src/main/kotlin/app/termora/ThemeManager.kt +++ b/src/main/kotlin/app/termora/ThemeManager.kt @@ -4,7 +4,6 @@ import com.formdev.flatlaf.FlatLaf import com.formdev.flatlaf.extras.FlatAnimatedLafChange import com.jthemedetecor.OsThemeDetector import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import org.slf4j.LoggerFactory import java.util.* @@ -76,8 +75,7 @@ class ThemeManager private constructor() { init { - @Suppress("OPT_IN_USAGE") - GlobalScope.launch(Dispatchers.IO) { + swingCoroutineScope.launch(Dispatchers.IO) { OsThemeDetector.getDetector().registerListener(object : Consumer { override fun accept(isDark: Boolean) { if (!appearance.followSystem) { diff --git a/src/main/kotlin/app/termora/actions/AppUpdateAction.kt b/src/main/kotlin/app/termora/actions/AppUpdateAction.kt index 86de39e..50c29de 100644 --- a/src/main/kotlin/app/termora/actions/AppUpdateAction.kt +++ b/src/main/kotlin/app/termora/actions/AppUpdateAction.kt @@ -36,6 +36,7 @@ class AppUpdateAction private constructor() : AnAction( StringUtils.EMPTY, Icons.ideUpdate ) { + private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Swing) companion object { private val log = LoggerFactory.getLogger(AppUpdateAction::class.java) @@ -59,7 +60,6 @@ class AppUpdateAction private constructor() : AnAction( } - @OptIn(DelicateCoroutinesApi::class) private fun scheduleUpdate() { fixedRateTimer( name = "check-update-timer", @@ -67,7 +67,7 @@ class AppUpdateAction private constructor() : AnAction( period = 5.hours.inWholeMilliseconds, daemon = true ) { if (!isRemindMeNextTime) { - GlobalScope.launch(Dispatchers.IO) { supervisorScope { launch { checkUpdate() } } } + coroutineScope.launch(Dispatchers.IO) { checkUpdate() } } } } diff --git a/src/main/kotlin/app/termora/terminal/panel/TerminalDisplay.kt b/src/main/kotlin/app/termora/terminal/panel/TerminalDisplay.kt index 33a315d..a3342b5 100644 --- a/src/main/kotlin/app/termora/terminal/panel/TerminalDisplay.kt +++ b/src/main/kotlin/app/termora/terminal/panel/TerminalDisplay.kt @@ -3,8 +3,11 @@ package app.termora.terminal.panel import app.termora.Database import app.termora.DynamicColor import app.termora.assertEventDispatchThread +import app.termora.swingCoroutineScope import app.termora.terminal.* -import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import kotlinx.coroutines.swing.Swing import java.awt.* import javax.swing.JComponent @@ -484,14 +487,13 @@ class TerminalDisplay( g.font = font } - @OptIn(DelicateCoroutinesApi::class) fun toast(text: String, duration: Duration) { if (!terminalPanel.showToast) { return } val toast = Toast(text) - GlobalScope.launch(Dispatchers.Swing) { + swingCoroutineScope.launch(Dispatchers.Swing) { delay(duration) toasts.remove(toast) terminalPanel.repaintImmediate()