Skip to content

Commit ce95f34

Browse files
committed
feat: implement screen saver inhibition on Linux using D-Bus
1 parent afe0902 commit ce95f34

File tree

5 files changed

+97
-0
lines changed

5 files changed

+97
-0
lines changed

app/desktop/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ compose.desktop {
103103
"gluegen.rt",
104104
"jogl.all",
105105
"java.instrument", // ByteBuddy, for disabling PagingLogger
106+
"jdk.security.auth", // com.sun.security.auth.module.UnixSystem used by dbus-java SASL auth
106107
)
107108

108109
// ./gradlew suggestRuntimeModules

app/desktop/proguard-desktop.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
-keep class ** extends uk.co.caprica.vlcj.factory.discovery.provider.DiscoveryDirectoryProvider { *; }
7373
-keep class org.apache.logging.slf4j.SLF4JServiceProvider { *; }
7474
-keep class ** extends org.slf4j.spi.SLF4JServiceProvider { *; }
75+
-keep class org.freedesktop.dbus.** { *; } # dbus-java uses ServiceLoader and extensive reflection
7576

7677
# Ktor related
7778

app/shared/ui-foundation/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ kotlin {
6868
sourceSets.desktopMain.dependencies {
6969
implementation(libs.jna)
7070
implementation(libs.jna.platform)
71+
implementation(libs.dbus.java.core)
72+
implementation(libs.dbus.java.transport.native.unixsocket)
7173
api(libs.directories)
7274
}
7375
}

app/shared/ui-foundation/src/desktopMain/kotlin/platform/window/LinuxWindowUtils.kt

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,14 @@ package me.him188.ani.app.platform.window
1111

1212
import androidx.compose.ui.window.WindowPlacement
1313
import androidx.compose.ui.window.WindowState
14+
import kotlinx.atomicfu.locks.ReentrantLock
15+
import kotlinx.atomicfu.locks.withLock
1416
import me.him188.ani.app.platform.PlatformWindow
17+
import me.him188.ani.utils.logging.logger
18+
import org.freedesktop.dbus.connections.impl.DBusConnection
19+
import org.freedesktop.dbus.connections.impl.DBusConnectionBuilder
20+
import org.freedesktop.dbus.interfaces.DBusInterface
21+
import org.freedesktop.dbus.types.UInt32
1522

1623
class LinuxWindowUtils : AwtWindowUtils() {
1724
override suspend fun setUndecoratedFullscreen(
@@ -25,4 +32,87 @@ class LinuxWindowUtils : AwtWindowUtils() {
2532
windowState.apply { placement = WindowPlacement.Floating }
2633
}
2734
}
35+
36+
private val preventScreenSaverLock = ReentrantLock()
37+
38+
@Volatile
39+
private var inhibitCookie: UInt32? = null
40+
41+
@Volatile
42+
private var dbusConnection: DBusConnection? = null
43+
44+
override fun setPreventScreenSaver(prevent: Boolean) = preventScreenSaverLock.withLock {
45+
if (prevent) {
46+
if (inhibitCookie == null) {
47+
logger.info("Inhibiting screen saver via org.freedesktop.ScreenSaver D-Bus")
48+
try {
49+
val connection = withDbusClassLoader {
50+
DBusConnectionBuilder.forSessionBus().build()
51+
}
52+
val screenSaver = connection.getRemoteObject(
53+
"org.freedesktop.ScreenSaver",
54+
"/org/freedesktop/ScreenSaver",
55+
ScreenSaverInterface::class.java,
56+
)
57+
val cookie = screenSaver.Inhibit("Animeko", "Playing video")
58+
dbusConnection = connection
59+
inhibitCookie = cookie
60+
logger.info("Screen saver inhibited with cookie: $cookie")
61+
} catch (e: Exception) {
62+
logger.error("Failed to inhibit screen saver, see cause", e)
63+
}
64+
}
65+
} else {
66+
val cookie = inhibitCookie
67+
val connection = dbusConnection
68+
if (cookie != null && connection != null) {
69+
logger.info("Uninhibiting screen saver with cookie: $cookie")
70+
try {
71+
val screenSaver = connection.getRemoteObject(
72+
"org.freedesktop.ScreenSaver",
73+
"/org/freedesktop/ScreenSaver",
74+
ScreenSaverInterface::class.java,
75+
)
76+
screenSaver.UnInhibit(cookie)
77+
} catch (e: Exception) {
78+
logger.error("Failed to uninhibit screen saver, see cause", e)
79+
} finally {
80+
try {
81+
connection.close()
82+
} catch (_: Exception) {
83+
}
84+
dbusConnection = null
85+
inhibitCookie = null
86+
}
87+
}
88+
}
89+
}
90+
91+
@Suppress("FunctionName")
92+
@org.freedesktop.dbus.annotations.DBusInterfaceName("org.freedesktop.ScreenSaver")
93+
interface ScreenSaverInterface : DBusInterface {
94+
fun Inhibit(applicationName: String, reason: String): UInt32
95+
fun UnInhibit(cookie: UInt32)
96+
}
97+
98+
private companion object {
99+
private val logger = logger<LinuxWindowUtils>()
100+
101+
/**
102+
* ServiceLoader uses the thread context classloader to discover providers.
103+
* On the AWT EventDispatch thread, this may differ from the classloader that
104+
* loaded dbus-java, causing transport provider discovery to fail.
105+
* Temporarily switch to the classloader that loaded [DBusConnectionBuilder].
106+
*/
107+
private inline fun <T> withDbusClassLoader(block: () -> T): T {
108+
val currentThread = Thread.currentThread()
109+
val originalClassLoader = currentThread.contextClassLoader
110+
currentThread.contextClassLoader = DBusConnectionBuilder::class.java.classLoader
111+
try {
112+
return block()
113+
} finally {
114+
currentThread.contextClassLoader = originalClassLoader
115+
}
116+
}
117+
}
28118
}

gradle/libs.versions.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ androidx-compose-material3-adaptive = "1.2.0-alpha11"
5454

5555
bytebuddy = "1.17.4" # https://github.com/raphw/byte-buddy/releases
5656
jna = "5.13.0" # 不要轻易改这个版本, 它可能导致 VLC 兼容性问题
57+
dbus-java = "5.2.0"
5758
jsonpathkt-kotlinx = "3.0.2"
5859
ksoup = "0.2.2"
5960
filekit = "0.10.0-beta04" # https://github.com/vinceglb/FileKit/releases
@@ -296,6 +297,8 @@ ipaddress-parser = { module = "com.github.seancfoley:ipaddress", version.ref = "
296297
vlcj = { module = "uk.co.caprica:vlcj", version.ref = "vlcj" }
297298
jna = { module = "net.java.dev.jna:jna", version.ref = "jna" }
298299
jna-platform = { module = "net.java.dev.jna:jna-platform", version.ref = "jna" }
300+
dbus-java-core = { module = "com.github.hypfvieh:dbus-java-core", version.ref = "dbus-java" }
301+
dbus-java-transport-native-unixsocket = { module = "com.github.hypfvieh:dbus-java-transport-native-unixsocket", version.ref = "dbus-java" }
299302
jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
300303
ksoup = { module = "com.fleeksoft.ksoup:ksoup", version.ref = "ksoup" }
301304
ksoup-kotlinx = { module = "com.fleeksoft.ksoup:ksoup-kotlinx", version.ref = "ksoup" }

0 commit comments

Comments
 (0)