Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ dependencies {
compileOnly(libs.log4j.core)
runtimeOnly(libs.log4j.slf4j2.impl)

implementation(libs.jna)
implementation(libs.jna.platform)

implementation(libs.compose.runtime)
implementation(libs.compose.native.tray)

val onlyWindowsX64: Boolean by rootProject.extra
val onlyWindowsArm64: Boolean by rootProject.extra
Expand Down Expand Up @@ -181,6 +185,18 @@ val copyrightYears =
if (now > it) "$it–$now" else "$it"
}

val optimizedJvmArgs =
listOf(
"-Xms128m",
"-Xmx2g",
"-XX:+UseG1GC",
"-XX:MaxGCPauseMillis=50",
"-XX:InitiatingHeapOccupancyPercent=30",
"-XX:G1ReservePercent=15",
"-XX:+ParallelRefProcEnabled",
"-XX:+UseStringDeduplication",
)

compose.desktop.application {
mainClass = mainFunction
javaHome = sequenceOf(
Expand All @@ -207,6 +223,8 @@ compose.desktop.application {
""".trimIndent(),
)

jvmArgs += optimizedJvmArgs

nativeDistributions {
packageName = "ServerListExplorer"

Expand Down Expand Up @@ -244,6 +262,7 @@ compose.desktop.application {
obfuscate.set(false)
configurationFiles.from(
rootProject.file("proguard/base.pro"),
rootProject.file("proguard/ComposeNativeTray.pro"),
rootProject.file("proguard/compose.pro"),
rootProject.file("proguard/jna.pro"),
rootProject.file("proguard/ktor.pro"),
Expand Down
15 changes: 14 additions & 1 deletion app/src/main/kotlin/com/spoiligaming/explorer/ArgsParser.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* This file is part of Server List Explorer.
* Copyright (C) 2025 SpoilerRules
* Copyright (C) 2025-2026 SpoilerRules
*
* Server List Explorer is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand All @@ -23,7 +23,20 @@ import org.apache.logging.log4j.Level
import org.apache.logging.log4j.core.config.Configurator

internal object ArgsParser {
private const val STARTUP_SOURCE_ARG_PREFIX = "--startup-source="
private const val STARTUP_SOURCE_OS = "os"

var isAutoStartupLaunch = false
private set

fun parse(args: Array<String>) {
val startupSource =
args
.firstOrNull { it.startsWith(STARTUP_SOURCE_ARG_PREFIX) }
?.substringAfter(STARTUP_SOURCE_ARG_PREFIX)

isAutoStartupLaunch = startupSource == STARTUP_SOURCE_OS

if ("--verbose" in args) {
enableVerboseLogging()
}
Expand Down
35 changes: 32 additions & 3 deletions app/src/main/kotlin/com/spoiligaming/explorer/Main.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* This file is part of Server List Explorer.
* Copyright (C) 2025 SpoilerRules
* Copyright (C) 2025-2026 SpoilerRules
*
* Server List Explorer is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand All @@ -18,14 +18,43 @@

package com.spoiligaming.explorer

import com.kdroid.composetray.utils.SingleInstanceManager
import com.spoiligaming.explorer.settings.manager.startupSettingsManager
import com.spoiligaming.explorer.ui.launchInterface
import com.spoiligaming.explorer.util.AppActivationSignal
import com.spoiligaming.explorer.util.ComputerStartupRegistrationManager
import io.github.oshai.kotlinlogging.KotlinLogging

fun main(args: Array<String>) {
val env = if (System.getProperty("env") == "dev") "dev" else "prod"
val logsDir = LogStorage.logsDir
logsDir.mkdirs()
System.setProperty("log4j2.configurationFile", "log4j2-$env.xml")
System.setProperty("app.logs.dir", LogStorage.logsDir.absolutePath)
System.setProperty("app.logs.dir", logsDir.absolutePath)

ArgsParser.parse(args)
WindowsProcessPriority.applyAutoStartupPriority()

launchInterface()
val startupSettings = startupSettingsManager.getCachedSettings()
ComputerStartupRegistrationManager
.reconcile(startupSettings.computerStartupBehavior)
.onFailure { e ->
logger.error(e) {
"Failed to reconcile OS startup registration for behavior=${startupSettings.computerStartupBehavior}"
}
}

if (startupSettings.singleInstanceHandling) {
val isPrimary =
SingleInstanceManager.isSingleInstance(
onRestoreRequest = { AppActivationSignal.publish() },
)
if (!isPrimary) {
return
}
}

launchInterface(isAutoStartupLaunch = ArgsParser.isAutoStartupLaunch)
}

private val logger by lazy { KotlinLogging.logger {} }
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* This file is part of Server List Explorer.
* Copyright (C) 2026 SpoilerRules
*
* Server List Explorer is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Server List Explorer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Server List Explorer. If not, see <https://www.gnu.org/licenses/>.
*/

package com.spoiligaming.explorer

import com.spoiligaming.explorer.util.OSUtils
import com.sun.jna.platform.win32.Kernel32
import com.sun.jna.platform.win32.WinDef.DWORD
import io.github.oshai.kotlinlogging.KotlinLogging

internal object WindowsProcessPriority {
private const val WINDOWS_IDLE_PRIORITY_CLASS = 0x00000040

fun applyAutoStartupPriority() {
if (!OSUtils.isWindows || OSUtils.isRunningOnBareJvm) {
return
}

if (!ArgsParser.isAutoStartupLaunch) {
return
}

runCatching {
val currentProcess = Kernel32.INSTANCE.GetCurrentProcess()
val isApplied =
Kernel32.INSTANCE.SetPriorityClass(
currentProcess,
DWORD(WINDOWS_IDLE_PRIORITY_CLASS.toLong()),
)

if (!isApplied) {
val code = Kernel32.INSTANCE.GetLastError()
error("Failed to lower process priority for auto startup. Win32Error=$code")
}
}.onFailure { e ->
logger.error(e) { "Unable to apply low-priority mode for Windows auto startup." }
}
}
}

private val logger = KotlinLogging.logger {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* This file is part of Server List Explorer.
* Copyright (C) 2026 SpoilerRules
*
* Server List Explorer is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Server List Explorer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Server List Explorer. If not, see <https://www.gnu.org/licenses/>.
*/

package com.spoiligaming.explorer.util

import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow

object AppActivationSignal {
private val _events = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
val events = _events.asSharedFlow()

fun publish() {
_events.tryEmit(Unit)
}
}
Loading
Loading