Compare commits

...

2 Commits

Author SHA1 Message Date
Kingkor Roy Tirtho
95e9d563ec fix: add mavenLocal() to plugin and dependency resolution management 2026-08-15 22:01:56 +06:00
Kingkor Roy Tirtho
e826052691 fix(webview): linux webview crashing the app 2026-08-15 21:59:15 +06:00
5 changed files with 115 additions and 75 deletions

View File

@ -19,6 +19,7 @@ package dev.krtirtho.spotube
import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionLayout
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
@ -41,6 +42,7 @@ import dev.krtirtho.spotube.core.ui.theming.SpotubeTheme
import dev.krtirtho.spotube.modules.settings.SettingsRepository
import dev.krtirtho.spotube.modules.settings.UserSettings
import dev.krtirtho.spotube.modules.shell.AppShell
import dev.krtirtho.spotube.modules.webview.WebViewScreen
import dev.krtirtho.spotube.resources.iconsax.Iconsax
import dev.krtirtho.spotube.resources.iconsax.IconsaxHome
import dev.krtirtho.spotube.resources.iconsax.IconsaxHomeBroken
@ -89,7 +91,11 @@ val tabs = listOf(
)
)
@OptIn(KoinExperimentalAPI::class, ExperimentalCoroutinesApi::class, ExperimentalSharedTransitionApi::class)
@OptIn(
KoinExperimentalAPI::class,
ExperimentalCoroutinesApi::class,
ExperimentalSharedTransitionApi::class
)
@Composable
fun App(
content: @Composable () -> Unit = {}
@ -108,6 +114,10 @@ fun App(
SpotubeTheme(settings = userSettings) {
val baseUITheme = rememberBaseUITheme()
CompositionLocalProvider(LocalBaseUITheme provides baseUITheme) {
Box(modifier = Modifier.fillMaxSize()) {
val currentRoute = navigationState.backStacks[navigationState.topLevelRoute]?.last()
Box(Modifier.fillMaxSize()) {
AppShell(navigator, navigationState) {
SharedTransitionLayout {
CompositionLocalProvider(
@ -123,6 +133,12 @@ fun App(
}
}
}
when (currentRoute) {
Routes.WebView -> WebViewScreen(koinInject())
else -> {}
}
}
}
content()
}
}

View File

@ -18,9 +18,8 @@
package dev.krtirtho.spotube.core.navigation
import androidx.navigation3.runtime.NavKey
import dev.krtirtho.spotube.modules.artist.ArtistScreen
import dev.krtirtho.spotube.modules.album.AlbumScreen
import dev.krtirtho.spotube.modules.artist.ArtistViewModel
import dev.krtirtho.spotube.modules.artist.ArtistScreen
import dev.krtirtho.spotube.modules.blacklist.BlacklistScreen
import dev.krtirtho.spotube.modules.home.HomeScreen
import dev.krtirtho.spotube.modules.library.LibraryScreen
@ -31,7 +30,6 @@ import dev.krtirtho.spotube.modules.saved_tracks.SAVED_TRACKS_COLLECTION_ID
import dev.krtirtho.spotube.modules.saved_tracks.SavedTracksScreen
import dev.krtirtho.spotube.modules.search.SearchScreen
import dev.krtirtho.spotube.modules.settings.SettingsScreen
import dev.krtirtho.spotube.modules.webview.WebViewScreen
import kotlinx.serialization.Serializable
import org.koin.compose.viewmodel.koinViewModel
import org.koin.core.annotation.KoinExperimentalAPI
@ -97,9 +95,7 @@ val navigationModule = module {
navigation<Routes.Plugins> {
PluginScreen(pluginManager = get())
}
navigation<Routes.WebView> {
WebViewScreen(get())
}
navigation<Routes.WebView> {}
navigation<Routes.Playlist> {
PlaylistScreen(
playlistId = it.playlistId,

View File

@ -19,6 +19,7 @@ package dev.krtirtho.spotube.core.webview
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
@ -51,6 +52,7 @@ import compose.icons.feathericons.ChevronLeft
import compose.icons.feathericons.ChevronRight
import compose.icons.feathericons.X
import dev.krtirtho.spotube.core.tools.user_agents.UserAgents
import dev.krtirtho.spotube.core.ui.component.ApplicationMainBar
import dev.nucleusframework.webview.jsbridge.IJsMessageHandler
import dev.nucleusframework.webview.jsbridge.JsMessage
import dev.nucleusframework.webview.jsbridge.rememberWebViewJsBridge
@ -149,6 +151,8 @@ fun PlatformWebViewScreen(webViewController: WebViewController) {
Scaffold(
contentWindowInsets = WindowInsets.statusBars,
topBar = {
ApplicationMainBar(
title = {
Row(
modifier = Modifier.fillMaxWidth().statusBarsPadding().height(56.dp),
horizontalArrangement = Arrangement.SpaceBetween,
@ -199,14 +203,11 @@ fun PlatformWebViewScreen(webViewController: WebViewController) {
.wrapContentHeight(Alignment.CenterVertically)
)
}
IconButton(
onClick = {
webViewController.closeWebview()
}) {
Icon(FeatherIcons.X, contentDescription = "Close WebView")
}
}
}) { innerPadding ->
)
})
{ innerPadding ->
WebView(
state = state,
modifier = Modifier.padding(innerPadding).fillMaxSize(),

View File

@ -26,6 +26,8 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.rememberWindowState
import com.sun.jna.Library
import com.sun.jna.Native
import dev.krtirtho.spotube.core.di.initKoin
import dev.krtirtho.spotube.core.newpipe.NewPipeDownloader
import dev.krtirtho.spotube.core.paths.Paths
@ -55,8 +57,33 @@ private object KoinServicesProvider : KoinComponent {
val settingsProvider: SettingsProvider get() = get()
}
private interface LibC : Library {
fun setenv(name: String, value: String, overwrite: Int): Int
companion object {
val INSTANCE: LibC = Native.load("c", LibC::class.java)
}
}
/**
* WebKitGTK is only used on Linux (Windows = WebView2, macOS = WKWebView).
* The webview is created *after* the window is already mapped and the Tao
* render/swap loop is running. WebKitGTK's accelerated-compositing path then
* initialises its own GL context in-process, racing Tao's swap thread on the
* same Mesa display, which deterministically segfaults `libgallium` on the
* next Compose flush. Disabling WebKit's hardware-accelerated compositing
* (and its DMABUF renderer) removes that GL context entirely login pages
* render fine in software. Must run before libwebkit2gtk is loaded.
*/
private fun disableWebKitGpuCompositing() {
if (!System.getProperty("os.name").lowercase().contains("linux")) return
LibC.INSTANCE.setenv("WEBKIT_DISABLE_COMPOSITING_MODE", "1", 1)
LibC.INSTANCE.setenv("WEBKIT_DISABLE_DMABUF_RENDERER", "1", 1)
}
@OptIn(ExperimentalComposeUiApi::class)
fun main() {
disableWebKitGpuCompositing()
FileKit.init(appId = "dev.krtirtho.spotube")
initKoin()
NewPipeDownloader.init(KoinPathsProvider.paths)

View File

@ -20,6 +20,7 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
pluginManagement {
repositories {
mavenLocal()
google {
mavenContent {
includeGroupAndSubgroups("androidx")
@ -29,13 +30,13 @@ pluginManagement {
}
mavenCentral()
gradlePluginPortal()
mavenLocal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenLocal()
google {
mavenContent {
includeGroupAndSubgroups("androidx")
@ -45,7 +46,6 @@ dependencyResolutionManagement {
}
mavenCentral()
maven("https://jitpack.io")
mavenLocal()
}
}