소스 검색

code improvements

Milan Jurkulak 2 년 전
부모
커밋
c9fdb0de63
23개의 변경된 파일과 247개의 추가작업 그리고 358개의 파일을 삭제
  1. 35 0
      README.md
  2. 13 18
      build.gradle.kts
  3. 9 31
      gradle/libs.versions.toml
  4. 0 1
      src/main/kotlin/eu/mjdev/desktop/Main.kt
  5. 6 2
      src/main/kotlin/eu/mjdev/desktop/components/appsmenu/AppsMenu.kt
  6. 2 2
      src/main/kotlin/eu/mjdev/desktop/components/controlcenter/ControlCenter.kt
  7. 9 25
      src/main/kotlin/eu/mjdev/desktop/components/controlcenter/pages/WifiSettingsPage.kt
  8. 1 1
      src/main/kotlin/eu/mjdev/desktop/components/custom/PowerBlock.kt
  9. 2 2
      src/main/kotlin/eu/mjdev/desktop/components/desktoppanel/DesktopPanel.kt
  10. 1 2
      src/main/kotlin/eu/mjdev/desktop/components/desktoppanel/DesktopPanelIcon.kt
  11. 9 3
      src/main/kotlin/eu/mjdev/desktop/components/desktoppanel/applets/DesktopPanelFavoriteApps.kt
  12. 2 3
      src/main/kotlin/eu/mjdev/desktop/components/image/ImageAny.kt
  13. 3 2
      src/main/kotlin/eu/mjdev/desktop/components/sliding/SlidingPanel.kt
  14. 0 1
      src/main/kotlin/eu/mjdev/desktop/components/web/ComposeWebView.kt
  15. 2 25
      src/main/kotlin/eu/mjdev/desktop/data/App.kt
  16. 19 16
      src/main/kotlin/eu/mjdev/desktop/data/WifiInfo.kt
  17. 62 11
      src/main/kotlin/eu/mjdev/desktop/extensions/Custom.kt
  18. 0 144
      src/main/kotlin/eu/mjdev/desktop/helpers/coil/PdfDecoder.kt
  19. 3 0
      src/main/kotlin/eu/mjdev/desktop/helpers/system/Shell.kt
  20. 4 26
      src/main/kotlin/eu/mjdev/desktop/managers/apps/AppsManager.kt
  21. 26 17
      src/main/kotlin/eu/mjdev/desktop/managers/connectivity/ConnectivityManager.kt
  22. 33 26
      src/main/kotlin/eu/mjdev/desktop/managers/processes/ProcessManager.kt
  23. 6 0
      src/main/kotlin/eu/mjdev/desktop/provider/DesktopScope.kt

+ 35 - 0
README.md

@@ -48,3 +48,38 @@ Build on compose kotlin and jvm.
 
 - import to intellij idea
 - click run or build (or select from run dialog what you want to do)
+
+# USed libraries
+
+- [Jetbrains Compose Desktop] (https://github.com/JetBrains/compose-multiplatform)
+  Main library for to made app
+- [Ini4J] (https://github.com/facebookarchive/ini4j)
+  Parse Desktop Files
+- [OkHttp] (https://github.com/square/okhttp)
+  For images & for future use (weather/ external apis)
+- [Coil] (https://github.com/coil-kt/coil)
+  Image loader
+- [Coroutines Swing] (https://github.com/Kotlin/kotlinx.coroutines)
+  Coroutines in JVM
+- [Google Gson] (https://github.com/google/gson)
+  JSON Parser
+- [FuzzyWuzzy] (https://github.com/xdrop/fuzzywuzzy)
+  Fuzzy search
+- [Thechance101 Chart] (https://github.com/TheChance101/AAY-chart)
+  Compose charts
+- [Shreyaspatil Generative AI] (https://github.com/PatilShreyas/generative-ai-kmp/)
+  AI support
+- [jna] (https://github.com/java-native-access/jna)
+  Load external libraries, for X11/Wayland
+- [Alexzhirkevich Grose] (https://github.com/alexzhirkevich/qrose)
+  QR code & Bar code (easy connect pc & mobile / Wifi share)
+- [Marc Apps TTS] (https://github.com/Marc-JB/TextToSpeechKt)
+  TTS support
+- [Hypfvieh DBus] (https://github.com/hypfvieh/dbus-java)
+  DBus support
+- [Kevinnzou Compose Webview] (https://github.com/KevinnZou/compose-webview-multiplatform)
+  Widgets with web view
+- [Mozilla Rhino] (https://github.com/mozilla/rhino)
+  Custom scripting & widgets code
+- [Bytedeco FFMpeg] (https://github.com/bytedeco/javacv)
+  Video wallpapers & widgets

+ 13 - 18
build.gradle.kts

@@ -110,37 +110,36 @@ allprojects {
         implementation(libs.qrose)
         // tts
         implementation(libs.tts)
+        // webview
+        implementation(libs.compose.webview)
+        // javascript
+        implementation(libs.mozilla.rhino)
         // dbus
-//        implementation(libs.dbus.java.core)
-//        implementation(libs.dbus.java.transport.native.unixsocket)
-        // wayland
-//        implementation("org.freedesktop:wayland-client:1.5.1")
+        implementation(libs.dbus.java.core)
+        implementation(libs.dbus.java.transport.native.unixsocket)
+ // testing yet :
         // gettext
         implementation("org.gnu.gettext:libintl:0.18.3")
+        // custom components
+        implementation("com.composables:core:1.12.0")
+        // events
+        implementation("org.rationalityfrontline:kevent:2.3.1")
+        // wayland
+//        implementation("org.freedesktop:wayland-client:1.5.1")
         // paths
 //        implementation("me.sujanpoudel.multiplatform.utils:multiplatform-paths:0.2.2")
         // files
 //        implementation("io.github.vinceglb:filekit-core:0.8.2")
         // files with composable utilities
 //        implementation("io.github.vinceglb:filekit-compose:0.8.2")
-        // testing yet
-        // anim
-//        implementation("org.jetbrains.compose.animation:animation:1.7.0-alpha03")
-//        implementation("org.jetbrains.compose.animation:animation-graphics:1.7.0-alpha03")
 //        // images metadata
 //        implementation("com.ashampoo:kim:0.18.4")
-        // connectivity
-        implementation("dev.tmapps:konnection:1.4.1")
-        // web
-        implementation("io.github.kevinnzou:compose-webview-multiplatform:1.9.40-alpha01")
         // bottom sheet flexible
 //        implementation("com.github.skydoves:flexible-bottomsheet-material3:0.1.5")
         // stt
 //        implementation("com.alphacephei:vosk:0.3.32+")
         // fs watcher
 //        implementation("io.github.irgaly.kfswatch:kfswatch:1.3.0")
-        // custom components
-        implementation("com.composables:core:1.12.0")
         // lib-app-indicator
 //        implementation("org.purejava:libappindicator-gtk3-java-full:1.4.1")
         // internationalization
@@ -176,12 +175,8 @@ allprojects {
 //        implementation("com.github.moaxcp.x11:x11-client:0.18.2")
         // jwm
 //        implementation("io.github.humbleui:jwm:0.4.8")
-
         // hid
 //        implementation('org.hid4java:hid4java')
-        // javascript
-        // implementation(libs.mozilla.rhino)
-//        implementation ("androidx.compose.ui:ui-text-google-fonts:1.6.8")
     }
 }
 

+ 9 - 31
gradle/libs.versions.toml

@@ -13,27 +13,24 @@ packageVersion = "1.0.2"
 gradle = "8.4.0"
 gradle-versions = "0.51.0"
 kotlin = "1.9.22"
-intellij = "1.17.4"
 compose = "1.7.0-alpha03"
-okhttp3 = "5.0.0-alpha.12"
-coil3 = "3.0.0-alpha06"
+okhttp3 = "5.0.0-alpha.14"
+coil3 = "3.0.0-rc01"
 ktxCoroutinesSwing = "1.8.0"
 slf4j-nop = "2.0.7"
 fuzzywuzzy = "1.4.0"
 ffmpeg-platform = "6.1.1-1.5.10"
-google-gson = "2.8.9"
-mozilla-rhino = "1.7.13"
+google-gson = "2.11.0"
+mozilla-rhino = "1.7.15"
 ini4j = "0.5.4"
 charts = "Beta-0.0.5"
 generativeai = "0.5.0-1.0.0-wasm"
 jna-platform = "5.6.0"
 qrose = "1.0.1"
-tts = "2.5.0"
+tts = "3.0.0-alpha.3"
+compose-webview = "1.9.40-alpha03"
 #compose-plugin = "1.8.0-dev1875"
-#korau = "4.0.10"
-#lanscapist = "2.3.8"
-#compose-webview = "1.9.20"
-#decompose = "3.0.0-alpha07"
+#intellij = "1.17.4"
 
 [libraries]
 gradle = { module = "com.android.tools.build:gradle", version.ref = "gradle" }
@@ -59,18 +56,9 @@ qrose = { module = "io.github.alexzhirkevich:qrose", version.ref = "qrose" }
 tts = { module = "nl.marc-apps:tts", version.ref = "tts" }
 dbus-java-core = { module = "com.github.hypfvieh:dbus-java-core", version = "5.0.0" }
 dbus-java-transport-native-unixsocket = { module = "com.github.hypfvieh:dbus-java-transport-native-unixsocket", version = "5.0.0" }
-
-#coil-gif = { module = "io.coil-kt.coil3:coil-gif", version.ref = "coil3" }
-#korau = { module = "com.soywiz.korlibs.korau:korau", version.ref = "korau" }
-#accompanist-drawablepainter = { module = "com.google.accompanist:accompanist-drawablepainter", version = "0.35.0-alpha" }
-#coil-gif = { module = "io.coil-kt.coil3:coil-gif", version = "2.2.7" }
-#landscapist-glide = { module = "com.github.skydoves:landscapist-glide", version.ref = "lanscapist" }
-#landscapist-palette = { module = "com.github.skydoves:landscapist-palette", version.ref = "lanscapist" }
-#font-icons-awesome = { module = "br.com.devsrsouza.compose.icons:font-awesome-desktop", version = "1.1.0" }
+compose-webview = { module = "io.github.kevinnzou:compose-webview-multiplatform", version.ref = "compose-webview" }
 #gradle-kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
-#compose-webview = { module = "io.github.kevinnzou:compose-webview-multiplatform", version.ref = "compose-webview" }
-#decompose = { module = "com.arkivanov.decompose:decompose", version.ref = "decompose" }
-#decompose-extensions-compose = { module = "com.arkivanov.decompose:extensions-compose", version.ref = "decompose" }
+
 #// open ai
 #//        implementation("com.aallam.openai:openai-client:3.8.2")
 #// byte buffer
@@ -95,16 +83,6 @@ dbus-java-transport-native-unixsocket = { module = "com.github.hypfvieh:dbus-jav
 #//        implementation("com.h2database:h2:1.4.192")
 #//        implementation("com.yuvimasory:orange-extensions:1.3.0")
 #//        implementation("org.projectlombok:lombok:1.18.4")
-#// decompose
-#//        implementation(libs.decompose)
-#//        implementation(libs.decompose.extensions.compose)
-#// ktor client
-#//        implementation("io.ktor:ktor-client-cio-jvm:2.3.2")
-#//        implementation("io.ktor:ktor-client-content-negotiation:2.3.2")
-#//        implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.2")
-#// gson
-#//        implementation("com.google.code.gson:gson:2.8.9")
-
 [bundles]
 
 [plugins]

+ 0 - 1
src/main/kotlin/eu/mjdev/desktop/Main.kt

@@ -12,7 +12,6 @@ fun main(
     exitProcessOnExit = true
 ) {
     // net devices  : ls /sys/class/net
-    // wave mon     : apt install wavemon
     // connect gui  : nmtui
     // eth settings : iwconfig
     MainWindow()

+ 6 - 2
src/main/kotlin/eu/mjdev/desktop/components/appsmenu/AppsMenu.kt

@@ -9,7 +9,10 @@ import androidx.compose.foundation.layout.*
 import androidx.compose.foundation.lazy.LazyListState
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.Divider
-import androidx.compose.runtime.*
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
@@ -32,6 +35,7 @@ import eu.mjdev.desktop.extensions.Compose.rememberCalculated
 import eu.mjdev.desktop.extensions.Compose.rememberComputed
 import eu.mjdev.desktop.extensions.Compose.rememberState
 import eu.mjdev.desktop.extensions.Compose.removeLast
+import eu.mjdev.desktop.extensions.Custom.flowBlock
 import eu.mjdev.desktop.extensions.Custom.trimIsNotEmpty
 import eu.mjdev.desktop.extensions.Modifier.dropShadow
 import eu.mjdev.desktop.helpers.animation.Animations.AppsMenuEnterAnimation
@@ -65,7 +69,7 @@ fun AppsMenu(
     onUserAvatarClick: () -> Unit = {}
 ) = withDesktopScope {
     var category by rememberState("")
-    val appCategories by appCategories.collectAsState(emptyList())
+    val appCategories by flowBlock(emptyList()) { appCategories }
     val items by rememberComputed(searchTextState.value, category) {
         when {
             // todo fuzzy sort

+ 2 - 2
src/main/kotlin/eu/mjdev/desktop/components/controlcenter/ControlCenter.kt

@@ -26,7 +26,7 @@ import androidx.compose.ui.unit.dp
 import androidx.compose.ui.window.WindowPosition
 import eu.mjdev.desktop.components.blur.BlurPanel
 import eu.mjdev.desktop.components.controlcenter.base.PageHeader
-import eu.mjdev.desktop.components.sliding.SlidingMenu
+import eu.mjdev.desktop.components.sliding.SlidingPanel
 import eu.mjdev.desktop.extensions.Compose.onLeftClick
 import eu.mjdev.desktop.extensions.Compose.onMousePress
 import eu.mjdev.desktop.extensions.Compose.onRightClick
@@ -86,7 +86,7 @@ fun ControlCenter(
         exitAnimation = exitAnimation,
         onFocusChange = onFocusChange
     ) {
-        SlidingMenu(
+        SlidingPanel(
             modifier = Modifier.fillMaxHeight(),
             orientation = Horizontal,
             state = controlCenterState,

+ 9 - 25
src/main/kotlin/eu/mjdev/desktop/components/controlcenter/pages/WifiSettingsPage.kt

@@ -5,13 +5,12 @@ import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.*
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.*
+import androidx.compose.material.RadioButton
+import androidx.compose.material.RadioButtonDefaults
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Wifi
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
@@ -19,19 +18,12 @@ import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
 import eu.mjdev.desktop.components.controlcenter.base.ControlCenterPage
+import eu.mjdev.desktop.components.icon.WifiLevelIcon
 import eu.mjdev.desktop.components.text.TextAny
 import eu.mjdev.desktop.data.WifiInfo
 import eu.mjdev.desktop.extensions.ColorUtils.alpha
-import eu.mjdev.desktop.extensions.Compose.launchedEffect
-import eu.mjdev.desktop.extensions.Compose.rememberState
 import eu.mjdev.desktop.extensions.Custom.flowBlock
-import eu.mjdev.desktop.helpers.system.Shell
-import eu.mjdev.desktop.helpers.system.Shell.Companion.CMD_NMCLI
 import eu.mjdev.desktop.provider.DesktopScope.Companion.withDesktopScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.flowOn
-import eu.mjdev.desktop.components.icon.WifiLevelIcon
 
 // nmcli -t -f ALL dev wifi
 // nmcli device show
@@ -41,29 +33,20 @@ fun WifiSettingsPage() = ControlCenterPage(
     name = "Wifi",
     condition = { connectionManager.isWifiAdapterAvailable }
 ) {
-    var lastSeen by rememberState(0L)
-    val list by flowBlock {
-        Shell.executeAndReadLines(CMD_NMCLI, "-t", "-f", "ALL", "dev", "wifi").map { ws ->
-            ws.split(":")
-        }
-    }.flowOn(Dispatchers.IO).collectAsState(emptyList())
+    val wifiList by flowBlock(emptyList(), 250L) { wifiConnections }
     Box(
         modifier = Modifier.fillMaxSize()
     ) {
         LazyColumn(
             modifier = Modifier.fillMaxWidth()
         ) {
-            list.forEach { wr ->
+            wifiList.forEach { wifiInfo ->
                 item {
-                    WifiRow(WifiInfo(wr))
+                    WifiRow(wifiInfo)
                 }
             }
         }
     }
-    launchedEffect(lastSeen) {
-        lastSeen = System.currentTimeMillis()
-        delay(500L)
-    }
 }
 
 @Composable
@@ -109,12 +92,13 @@ fun WifiRow(
             )
             RadioButton(
                 modifier = Modifier.padding(start = 8.dp).size(24.dp),
-                selected = true,
+                selected = info.isActive,
+                enabled = !info.isActive,
                 onClick = {},
                 colors = RadioButtonDefaults.colors(
                     selectedColor = textColor,
                     unselectedColor = textColor,
-                    disabledColor = textColor.alpha(0.5f)
+                    disabledColor = textColor
                 )
             )
         }

+ 1 - 1
src/main/kotlin/eu/mjdev/desktop/components/custom/PowerBlock.kt

@@ -62,7 +62,7 @@ fun PowerBlock(
                     .size(64.dp)
                     .clipCircle()
                     .circleBorder(2.dp, textColor.alpha(0.5f))
-                    .circleShadow(4.dp, shadowColor),
+                    .circleShadow(4.dp, Color.Black),
                 contentPadding = PaddingValues(1.dp),
                 onClick = onPowerButtonClick,
                 colors = ButtonDefaults.transparent(),

+ 2 - 2
src/main/kotlin/eu/mjdev/desktop/components/desktoppanel/DesktopPanel.kt

@@ -30,7 +30,7 @@ import eu.mjdev.desktop.components.desktoppanel.applets.DesktopMenuIcon
 import eu.mjdev.desktop.components.desktoppanel.applets.DesktopPanelDateTime
 import eu.mjdev.desktop.components.desktoppanel.applets.DesktopPanelFavoriteApps
 import eu.mjdev.desktop.components.desktoppanel.applets.DesktopPanelLanguage
-import eu.mjdev.desktop.components.sliding.SlidingMenu
+import eu.mjdev.desktop.components.sliding.SlidingPanel
 import eu.mjdev.desktop.components.tooltip.Tooltip
 import eu.mjdev.desktop.components.tooltip.TooltipState
 import eu.mjdev.desktop.components.tooltip.rememberTooltipState
@@ -103,7 +103,7 @@ fun DesktopPanel(
         exitAnimation = exitAnimation,
         onFocusChange = onFocusChange
     ) {
-        SlidingMenu(
+        SlidingPanel(
             modifier = Modifier.fillMaxWidth(),
             orientation = Vertical,
             state = panelState,

+ 1 - 2
src/main/kotlin/eu/mjdev/desktop/components/desktoppanel/DesktopPanelIcon.kt

@@ -15,7 +15,6 @@ import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
 import eu.mjdev.desktop.components.fonticon.FontIcon
 import eu.mjdev.desktop.data.App
-import eu.mjdev.desktop.data.App.Companion.rememberRunningIndicator
 import eu.mjdev.desktop.extensions.ColorUtils.alpha
 import eu.mjdev.desktop.extensions.Compose.color
 import eu.mjdev.desktop.extensions.Compose.noElevation
@@ -56,7 +55,7 @@ fun DesktopPanelIcon(
         else -> Color.Transparent
     }
     val isStarting by rememberCalculated(app) { app?.isStarting ?: false }
-    val isRunning by rememberRunningIndicator(app)
+    val isRunning by rememberCalculated(app) { app?.isRunning == true || processManager.hasAppProcess(app) }
     Box(
         modifier = Modifier
             .size(iconSize)

+ 9 - 3
src/main/kotlin/eu/mjdev/desktop/components/desktoppanel/applets/DesktopPanelFavoriteApps.kt

@@ -16,7 +16,8 @@ import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
 import eu.mjdev.desktop.components.desktoppanel.DesktopPanelIcon
 import eu.mjdev.desktop.data.App
-import eu.mjdev.desktop.managers.apps.AppsManager.Companion.rememberFavoriteApps
+import eu.mjdev.desktop.extensions.Custom.flowBlock
+import eu.mjdev.desktop.managers.processes.ProcessManager.Companion.rememberProcessManager
 import eu.mjdev.desktop.provider.DesktopScope.Companion.withDesktopScope
 
 @Suppress("FunctionName")
@@ -33,7 +34,12 @@ fun DesktopPanelFavoriteApps(
     onAppClick: (app: App) -> Unit = {},
     onContextMenuClick: (app: App) -> Unit = {}
 ) = withDesktopScope {
-    val apps by rememberFavoriteApps()
+    val apps by flowBlock(
+        emptyList(),
+        processManager.size
+    ) {
+        appsManager.favoriteApps
+    }
     Box(
         modifier = modifier
     ) {
@@ -63,4 +69,4 @@ fun DesktopPanelFavoriteApps(
 
 @Preview
 @Composable
-fun DesktopPanelFavoriteAppsPreview() = DesktopPanelFavoriteApps()
+fun DesktopPanelFavoriteAppsPreview() = DesktopPanelFavoriteApps()

+ 2 - 3
src/main/kotlin/eu/mjdev/desktop/components/image/ImageAny.kt

@@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.Box
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.SupervisedUserCircle
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.*
@@ -40,9 +39,9 @@ fun ImageAny(
     onLoading: () -> Unit = {},
     onFail: (error: Throwable) -> Unit = {}
 ) = withDesktopScope {
-    val image = flowBlock {
+    val image = flowBlock(null) {
         (src as? ImagesProvider).let { provider -> provider?.getOne() ?: src }
-    }.collectAsState(null)
+    }
     // todo from mime
     val isGif = image.value.toString().trim().endsWith(".gif")
     // todo from mime

+ 3 - 2
src/main/kotlin/eu/mjdev/desktop/components/sliding/SlidingMenu.kt → src/main/kotlin/eu/mjdev/desktop/components/sliding/SlidingPanel.kt

@@ -15,9 +15,10 @@ import eu.mjdev.desktop.components.sliding.base.VisibilityState.Companion.rememb
 import eu.mjdev.desktop.extensions.Compose.onMouseEnter
 import eu.mjdev.desktop.extensions.Compose.onMouseLeave
 
+// todo remove, move to chrome window
 @Suppress("FunctionName")
 @Composable
-fun SlidingMenu(
+fun SlidingPanel(
     modifier: Modifier = Modifier,
     state: VisibilityState = rememberVisibilityState(),
     orientation: Orientation = Horizontal,
@@ -52,4 +53,4 @@ fun SlidingMenu(
 
 @Preview
 @Composable
-fun SlidingMenuPreview() = SlidingMenu()
+fun SlidingMenuPreview() = SlidingPanel()

+ 0 - 1
src/main/kotlin/eu/mjdev/desktop/components/web/ComposeWebView.kt

@@ -89,7 +89,6 @@ fun ComposeWebView(
             webViewState.webSettings.apply {
                 isJavaScriptEnabled = true
                 with(desktopWebSettings) {
-//                offScreenRendering = true
                     transparent = true
                     this.disablePopupWindows = disablePopupWindows
                 }

+ 2 - 25
src/main/kotlin/eu/mjdev/desktop/data/App.kt

@@ -1,11 +1,10 @@
 package eu.mjdev.desktop.data
 
-import androidx.compose.runtime.*
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.ui.graphics.Color
 import eu.mjdev.desktop.helpers.exception.EmptyException.Companion.EmptyException
 import eu.mjdev.desktop.helpers.system.Shell
-import eu.mjdev.desktop.provider.DesktopProvider
-import eu.mjdev.desktop.provider.DesktopProvider.Companion.LocalDesktop
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
@@ -180,27 +179,5 @@ class App(
                 else -> list
             }
         }
-
-        @Composable
-        fun rememberRunningIndicator(
-            app: App?,
-            api: DesktopProvider = LocalDesktop.current
-        ) = remember(
-            app,
-            app?.isFavorite,
-            app?.isRunning,
-            app?.isStarting
-        ) {
-            RunningAppIndicator(api, app)
-        }
-
-        // todo
-        class RunningAppIndicator(
-            val api: DesktopProvider,
-            val app: App?
-        ) : State<Boolean> {
-            override val value: Boolean
-                get() = app?.isRunning == true || api.processManager.hasAppProcess(app)
-        }
     }
 }

+ 19 - 16
src/main/kotlin/eu/mjdev/desktop/data/WifiInfo.kt

@@ -8,29 +8,32 @@
 
 package eu.mjdev.desktop.data
 
+import eu.mjdev.desktop.extensions.Custom.ParsedBoolean
+
 data class WifiInfo(
     val data: List<String> = emptyList(),
-    val id: String = data.getOrNull(0).orEmpty(),
+    val name: String = data.getOrNull(0).orEmpty(),
     val ssid: String = data.getOrNull(1).orEmpty(),
-    val password: String = data.getOrNull(2).orEmpty(),
-    val a0: String = data.getOrNull(3).orEmpty(),
-    val a1: String = data.getOrNull(4).orEmpty(),
-    val a2: String = data.getOrNull(5).orEmpty(),
-    val a3: String = data.getOrNull(6).orEmpty(),
-    val a4: String = data.getOrNull(7).orEmpty(),
-    val a5: String = data.getOrNull(8).orEmpty(),
-    val infra: String = data.getOrNull(9).orEmpty(),
+    val ssidHex: String = data.getOrNull(2).orEmpty(),
+    private val a0: String = data.getOrNull(3).orEmpty(),
+    private val a1: String = data.getOrNull(4).orEmpty(),
+    private val a2: String = data.getOrNull(5).orEmpty(),
+    private val a3: String = data.getOrNull(6).orEmpty(),
+    private val a4: String = data.getOrNull(7).orEmpty(),
+    private val a5: String = data.getOrNull(8).orEmpty(),
+    val bssid: String = "$a0:$a1:$a2:$a3:$a4:$a5",
+    val mode: String = data.getOrNull(9).orEmpty(),
     val channel: String = data.getOrNull(10).orEmpty(),
     val frequency: String = data.getOrNull(11).orEmpty(),
-    val speed: String = data.getOrNull(12).orEmpty(),
+    val rate: String = data.getOrNull(12).orEmpty(),
     val bandwidth: String = data.getOrNull(13).orEmpty(),
     val signalLevel: String = data.getOrNull(14).orEmpty(),
     val signalGraph: String = data.getOrNull(15).orEmpty(),
     val encryption: String = data.getOrNull(16).orEmpty(),
-    val reserved1: String = data.getOrNull(17).orEmpty(),
-    val encryptionDetails: String = data.getOrNull(18).orEmpty(),
-    val adapter: String = data.getOrNull(19).orEmpty(),
-    val connected: String = data.getOrNull(20).orEmpty(),
-    val reserved2: String = data.getOrNull(21).orEmpty(),
-    val mntp: String = data.getOrNull(17).orEmpty(),
+    val wpaFlags: String = data.getOrNull(17).orEmpty(),
+    val rsnFlags: String = data.getOrNull(18).orEmpty(),
+    val device: String = data.getOrNull(19).orEmpty(),
+    val isActive: Boolean = ParsedBoolean(data.getOrNull(20).orEmpty()),
+    val isInUse: Boolean = ParsedBoolean(data.getOrNull(21).orEmpty()),
+    val dbusPath: String = data.getOrNull(17).orEmpty(),
 )

+ 62 - 11
src/main/kotlin/eu/mjdev/desktop/extensions/Custom.kt

@@ -1,27 +1,30 @@
 package eu.mjdev.desktop.extensions
 
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.MutableState
-import androidx.compose.runtime.State
-import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.*
 import androidx.compose.runtime.snapshots.SnapshotStateList
 import androidx.compose.ui.graphics.NativePaint
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.DpSize
 import com.google.gson.Gson
 import eu.mjdev.desktop.data.DesktopFile
+import eu.mjdev.desktop.extensions.Compose.rememberState
 import eu.mjdev.desktop.extensions.Locale.toLocale
 import eu.mjdev.desktop.helpers.streams.ResourceStream
+import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.catch
 import kotlinx.coroutines.flow.channelFlow
 import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.launch
+import org.jetbrains.skia.FilterBlurMode.NORMAL
+import org.jetbrains.skia.MaskFilter.Companion.makeBlur
 import java.io.BufferedReader
 import java.io.File
 import java.text.DateFormat
 import java.util.*
 import java.util.Locale
+import kotlin.coroutines.CoroutineContext
 import kotlin.jvm.optionals.getOrNull
 
 @Suppress("MemberVisibilityCanBePrivate", "FunctionName", "unused")
@@ -169,10 +172,14 @@ object Custom {
 
     fun ParsedBoolean(
         value: String?,
-        defaultValue: Boolean = false
+        defaultValue: Boolean = false,
     ) = value.orEmpty().trim().lowercase().let { b ->
         when (b) {
             "true" -> true
+            "ano" -> true
+            "yes" -> true
+            "enabled" -> true
+            "*" -> true
             "1" -> true
             else -> defaultValue
         }
@@ -183,17 +190,61 @@ object Custom {
         defaultValue: String = ""
     ) = value?.toString() ?: defaultValue
 
+    @Composable
     fun <T> flowBlock(
-        block: suspend () -> T
-    ): Flow<T> = flow {
-        emit(block())
+        initial: T,
+        delay: Long = 0L,
+        coroutineContext: CoroutineContext = Dispatchers.IO,
+        onError: (Throwable) -> Unit = { e -> e.printStackTrace() },
+        block: suspend () -> T,
+    ): MutableState<T> {
+        val result = remember { mutableStateOf(initial) }
+        var state by rememberState(0L)
+        LaunchedEffect(state) {
+            flow {
+                emit(block())
+            }.catch { t ->
+                onError(t)
+            }.flowOn(coroutineContext).collect { r ->
+                result.value = r
+            }
+            if (delay > 0L) {
+                delay(delay)
+                state = System.currentTimeMillis()
+            }
+        }
+        return result
+    }
+
+    // todo move to uiState
+    @Composable
+    fun <T> flowBlock(
+        initial: T,
+        key: Any,
+        coroutineContext: CoroutineContext = Dispatchers.IO,
+        onError: (Throwable) -> Unit = { e -> e.printStackTrace() },
+        block: suspend () -> T,
+    ): MutableState<T> {
+        println("flow block called")
+        val result = remember { mutableStateOf(initial) }
+        LaunchedEffect(key) {
+            println("launching for new key")
+            flow {
+                emit(block())
+            }.catch { t ->
+                onError(t)
+            }.flowOn(coroutineContext).collect { r ->
+                println("got new result")
+                result.value = r
+            }
+        }
+        return result
     }
 
     fun NativePaint.setMaskFilter(
         blurRadius: Float
     ) {
-        this.maskFilter =
-            org.jetbrains.skia.MaskFilter.makeBlur(org.jetbrains.skia.FilterBlurMode.NORMAL, blurRadius / 2, true)
+        maskFilter = makeBlur(NORMAL, blurRadius / 2, true)
     }
 
     val dateFlow

+ 0 - 144
src/main/kotlin/eu/mjdev/desktop/helpers/coil/PdfDecoder.kt

@@ -1,144 +0,0 @@
-@file:Suppress("unused")
-
-package eu.mjdev.desktop.helpers.coil
-
-import coil3.DrawableImage
-import coil3.Image
-import coil3.ImageLoader
-import coil3.annotation.ExperimentalCoilApi
-import coil3.decode.DecodeResult
-import coil3.decode.DecodeUtils
-import coil3.decode.Decoder
-import coil3.decode.ImageSource
-import coil3.fetch.SourceFetchResult
-import coil3.request.Options
-import coil3.size.Dimension
-import coil3.size.Scale
-import coil3.size.isOriginal
-import coil3.size.pxOrElse
-import org.jetbrains.skia.Canvas
-import org.jetbrains.skia.Data
-import org.jetbrains.skia.Rect
-import org.jetbrains.skia.svg.SVGDOM
-import org.jetbrains.skia.svg.SVGLength
-import org.jetbrains.skia.svg.SVGLengthUnit
-import kotlin.math.roundToInt
-
-// todo from svg
-class PdfDecoder(
-    private val source: ImageSource,
-    private val options: Options,
-    private val useViewBoundsAsIntrinsicSize: Boolean,
-) : Decoder {
-    @OptIn(ExperimentalCoilApi::class)
-    override suspend fun decode(): DecodeResult {
-//        val context = options.context
-        val bytes = source.source().readByteArray()
-        val pdf = SVGDOM(Data.makeFromBytes(bytes))
-        val pdfWidth: Float
-        val pdfHeight: Float
-        val viewBox: Rect? = pdf.root?.viewBox
-        if (useViewBoundsAsIntrinsicSize && viewBox != null) {
-            pdfWidth = viewBox.width
-            pdfHeight = viewBox.height
-        } else {
-            pdfWidth = pdf.root?.width?.value ?: 0f
-            pdfHeight = pdf.root?.height?.value ?: 0f
-        }
-        val bitmapWidth: Int
-        val bitmapHeight: Int
-        val (dstWidth, dstHeight) = getDstSize(pdfWidth, pdfHeight, options.scale)
-        if (pdfWidth > 0f && pdfHeight > 0f) {
-            val multiplier = DecodeUtils.computeSizeMultiplier(
-                srcWidth = pdfWidth,
-                srcHeight = pdfHeight,
-                dstWidth = dstWidth.toFloat(),
-                dstHeight = dstHeight.toFloat(),
-                scale = options.scale,
-            )
-            bitmapWidth = (multiplier * pdfWidth).toInt()
-            bitmapHeight = (multiplier * pdfHeight).toInt()
-        } else {
-            bitmapWidth = dstWidth
-            bitmapHeight = dstHeight
-        }
-        if (viewBox == null && pdfWidth > 0f && pdfHeight > 0f) {
-            pdf.root?.viewBox = Rect.makeWH(pdfWidth, pdfHeight)
-        }
-        pdf.root?.width = SVGLength(
-            value = 100f,
-            unit = SVGLengthUnit.PERCENTAGE,
-        )
-        pdf.root?.height = SVGLength(
-            value = 100f,
-            unit = SVGLengthUnit.PERCENTAGE,
-        )
-        pdf.setContainerSize(bitmapWidth.toFloat(), bitmapHeight.toFloat())
-        return DecodeResult(
-            image = pdf.asCoilImage(bitmapWidth, bitmapHeight),
-            isSampled = false,
-        )
-    }
-
-    private fun getDstSize(srcWidth: Float, srcHeight: Float, scale: Scale): Pair<Int, Int> {
-        if (options.size.isOriginal) {
-            val dstWidth = if (srcWidth > 0) srcWidth.roundToInt() else PDF_DEFAULT_SIZE
-            val dstHeight = if (srcHeight > 0) srcHeight.roundToInt() else PDF_DEFAULT_SIZE
-            return dstWidth to dstHeight
-        } else {
-            val (dstWidth, dstHeight) = options.size
-            return dstWidth.toPx(scale) to dstHeight.toPx(scale)
-        }
-    }
-
-    class Factory(
-        private val useViewBoundsAsIntrinsicSize: Boolean = true,
-    ) : Decoder.Factory {
-        override fun create(result: SourceFetchResult, options: Options, imageLoader: ImageLoader): Decoder? {
-            if (!isApplicable(result)) return null
-            return PdfDecoder(result.source, options, useViewBoundsAsIntrinsicSize)
-        }
-
-        private fun isApplicable(result: SourceFetchResult): Boolean =
-            result.mimeType == MIME_TYPE_PDF
-    }
-
-    @OptIn(ExperimentalCoilApi::class)
-    private class PdfImage(
-        val pdf: SVGDOM,
-        override val width: Int,
-        override val height: Int,
-    ) : DrawableImage() {
-        override val size: Long
-            get() = 4L * width * height
-
-        override val shareable: Boolean = true
-
-        @ExperimentalCoilApi
-        override fun Canvas.onDraw() {
-            pdf.render(this)
-        }
-    }
-
-    companion object {
-        private const val PDF_DEFAULT_SIZE = 512
-        private const val MIME_TYPE_PDF = "application/pdf"
-
-        private fun Dimension.toPx(scale: Scale): Int = pxOrElse {
-            when (scale) {
-                Scale.FILL -> Int.MIN_VALUE
-                Scale.FIT -> Int.MAX_VALUE
-            }
-        }
-
-        @OptIn(ExperimentalCoilApi::class)
-        internal fun SVGDOM.asCoilImage(
-            width: Int,
-            height: Int,
-        ): Image = PdfImage(
-            pdf = this,
-            width = width,
-            height = height,
-        )
-    }
-}

+ 3 - 0
src/main/kotlin/eu/mjdev/desktop/helpers/system/Shell.kt

@@ -91,6 +91,9 @@ class Shell(
     companion object {
 
         const val CMD_NMCLI = "nmcli"
+        val CMD_NMCLI_RESCAN = arrayOf("dev", "wifi", "rescan")
+        val CMD_NMCLI_GET_NETWORKS = arrayOf("-t", "-f", "ALL", "dev", "wifi")
+
         const val CMD_ENV = "env"
         const val CMD_DEX = "dex"
 

+ 4 - 26
src/main/kotlin/eu/mjdev/desktop/managers/apps/AppsManager.kt

@@ -8,14 +8,10 @@
 
 package eu.mjdev.desktop.managers.apps
 
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
-import androidx.compose.runtime.collectAsState
 import eu.mjdev.desktop.data.App
 import eu.mjdev.desktop.data.Category
 import eu.mjdev.desktop.extensions.Custom.desktopFiles
 import eu.mjdev.desktop.extensions.Custom.dirsOnly
-import eu.mjdev.desktop.extensions.Custom.flowBlock
 import eu.mjdev.desktop.extensions.Custom.get
 import eu.mjdev.desktop.extensions.Custom.jsonToList
 import eu.mjdev.desktop.extensions.Custom.lines
@@ -23,11 +19,7 @@ import eu.mjdev.desktop.extensions.Custom.text
 import eu.mjdev.desktop.extensions.Custom.textAsLocale
 import eu.mjdev.desktop.helpers.system.Shell
 import eu.mjdev.desktop.provider.DesktopProvider
-import eu.mjdev.desktop.provider.DesktopProvider.Companion.LocalDesktop
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flowOn
 import java.io.File
 import java.util.*
 
@@ -60,18 +52,6 @@ class AppsManager(
         const val FILE_NAME_MIME_APPS_LIST = "mimeapps.list"
         const val FILE_NAME_USER_DIRS = "user-dirs.dirs"
         const val FILE_NAME_USER_DIRS_LOCALE = "user-dirs.locale"
-
-        // todo
-        @Composable
-        fun rememberFavoriteApps(
-            api: DesktopProvider = LocalDesktop.current,
-            appsManager: AppsManager = api.appsManager,
-        ): State<List<App>> {
-//            var processes by mutableStateOf(0)
-//            processManagerListener { processes = size }
-            return appsManager.favoriteApps.collectAsState(emptyList())
-            // todo
-        }
     }
 
     private val rootDir = File(DIR_NAME_ROOT)
@@ -149,18 +129,17 @@ class AppsManager(
 //        backgroundFilesDir.filesOnly.sortedByName()
 //    }
 
-    val appCategories = flowBlock {
-        allApps.asSequence().flatMap { app ->
+    val appCategories
+        get() = allApps.asSequence().flatMap { app ->
             app.categories.map { c -> provideCategory(c) }
         }.distinct().sorted().map { c ->
             Category(c)
         }.sortedByDescending { c ->
             c.priority
         }.toList()
-    }.flowOn(Dispatchers.IO)
 
-    val favoriteApps: Flow<List<App>> = flowBlock {
-        Shell.executeAndRead(
+    val favoriteApps: List<App>
+        get() = Shell.executeAndRead(
             "gsettings",
             "get",
             "org.gnome.shell",
@@ -172,7 +151,6 @@ class AppsManager(
                 App(deskFile)
             }
         }.toList()
-    }.flowOn(Dispatchers.IO)
 
     // todo languages
     fun provideCategory(name: String): String {

+ 26 - 17
src/main/kotlin/eu/mjdev/desktop/managers/connectivity/ConnectivityManager.kt

@@ -1,12 +1,13 @@
 package eu.mjdev.desktop.managers.connectivity
 
-import dev.tmapps.konnection.Konnection
-import dev.tmapps.konnection.NetworkConnection
+import eu.mjdev.desktop.data.WifiInfo
+import eu.mjdev.desktop.helpers.system.Shell
+import eu.mjdev.desktop.helpers.system.Shell.Companion.CMD_NMCLI
+import eu.mjdev.desktop.helpers.system.Shell.Companion.CMD_NMCLI_GET_NETWORKS
+import eu.mjdev.desktop.helpers.system.Shell.Companion.CMD_NMCLI_RESCAN
 
 @Suppress("MemberVisibilityCanBePrivate", "unused")
 class ConnectivityManager {
-    val connection = Konnection.createInstance(enableDebugLog = false)
-
     // todo
     val isWifiAdapterAvailable
         get() = true
@@ -19,16 +20,24 @@ class ConnectivityManager {
     val isBthAdapterAvailable
         get() = true
 
-    val isConnected
-        get() = connection.isConnected()
-
-    val isWifiConnection
-        get() = isConnected && connection.getCurrentNetworkConnection() == NetworkConnection.WIFI
-
-    val isEthConnection
-        get() = isConnected && connection.getCurrentNetworkConnection() == NetworkConnection.ETHERNET
-
-    val isMobileConnection
-        get() = isConnected && connection.getCurrentNetworkConnection() == NetworkConnection.MOBILE
-
-}
+    // todo dbus
+    val wifiConnections
+        get() = try {
+            rescanWifi()
+            getWifiNetworks()
+        } catch (t: Throwable) {
+            t.printStackTrace()
+            emptyList()
+        }
+
+    fun rescanWifi() = Shell.executeAndReadLines(
+        CMD_NMCLI, *CMD_NMCLI_RESCAN
+    )
+
+    fun getWifiNetworks() = Shell.executeAndReadLines(
+        CMD_NMCLI, *CMD_NMCLI_GET_NETWORKS
+    ).map { ws ->
+        WifiInfo(ws.split(":"))
+    }.distinctBy { it.ssid }.sortedByDescending { it.isActive }
+
+}

+ 33 - 26
src/main/kotlin/eu/mjdev/desktop/managers/processes/ProcessManager.kt

@@ -10,6 +10,9 @@ package eu.mjdev.desktop.managers.processes
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.snapshots.SnapshotStateList
 import eu.mjdev.desktop.data.App
 import eu.mjdev.desktop.extensions.Custom.orElse
 import eu.mjdev.desktop.provider.DesktopProvider
@@ -17,22 +20,25 @@ import eu.mjdev.desktop.provider.DesktopProvider.Companion.LocalDesktop
 import kotlinx.coroutines.*
 import kotlin.jvm.optionals.getOrNull
 
+// todo dbus
 @Suppress("unused")
 class ProcessManager(
-    val delay: Long = 1000L
-) : ArrayList<ProcessManager.ProcessWrapper>(), AutoCloseable {
+    private val checkDelay: Long = 1000L
+) : AutoCloseable {
+    val size: Int get() = processes.size
     private val scope = CoroutineScope(Dispatchers.IO)
     private val listeners = mutableListOf<ProcessListener>()
     private var job: Job? = null
+    private val processes = mutableStateListOf<ProcessWrapper>()
 
     init {
         job = scope.launch(Dispatchers.IO) {
             while (isActive) {
                 ProcessHandle.allProcesses().toList().filterNotNull().let { phList ->
                     phList.forEach { ph ->
-                        if (!containsProcess(ph)) {
+                        if (!processes.containsProcess(ph)) {
                             ProcessWrapper(ph).also { pw ->
-                                add(pw)
+                                processes.add(pw)
                             }
                             listeners.filterIsInstance<ProcessAddListener>().forEach { l ->
                                 l.onProcessAdded(ph)
@@ -41,13 +47,13 @@ class ProcessManager(
                     }
                     cleanup()
                 }
-                delay(delay)
+                delay(checkDelay)
             }
         }
     }
 
     private fun cleanup() {
-        iterator().apply {
+        processes.iterator().apply {
             while (hasNext()) {
                 val pw = next()
                 if (pw.processHandle?.isAlive != true) {
@@ -58,22 +64,13 @@ class ProcessManager(
                 }
             }
         }
-
     }
 
-    fun addOnAddListener(listener: ProcessAddListener) {
+    fun addListener(listener: ProcessListener) {
         listeners.add(listener)
     }
 
-    fun addOnRemoveListener(listener: ProcessRemoveListener) {
-        listeners.add(listener)
-    }
-
-    fun removeOnAddListener(listener: ProcessAddListener) {
-        listeners.remove(listener)
-    }
-
-    fun removeOnRemoveListener(listener: ProcessRemoveListener) {
+    fun removeListener(listener: ProcessListener) {
         listeners.remove(listener)
     }
 
@@ -86,7 +83,7 @@ class ProcessManager(
         val appCmd = app?.cmd
         val appName = app?.name
         val appFullName = app?.fullAppName
-        any { pw ->
+        processes.any { pw ->
             (appName != null && pw.command.contains(appName)) ||
                     (appCmd != null && pw.command.contains(appCmd)) ||
                     (appFullName != null && pw.command.contains(appFullName)) ||
@@ -98,15 +95,24 @@ class ProcessManager(
     }.getOrNull() ?: false
 
     companion object {
-        fun ArrayList<ProcessWrapper>.containsProcess(ph: ProcessHandle) = ph.pid().let { pid ->
+        fun SnapshotStateList<ProcessWrapper>.containsProcess(
+            ph: ProcessHandle
+        ) = ph.pid().let { pid ->
             any { p -> p.pid == pid }
         }
 
-        fun ArrayList<ProcessWrapper>.containsProcess(ph: ProcessWrapper) = any { p -> p.pid == ph.pid }
+        fun SnapshotStateList<ProcessWrapper>.containsProcess(
+            ph: ProcessWrapper
+        ) = any { p -> p.pid == ph.pid }
+
+        @Composable
+        fun rememberProcessManager(
+            checkDelay: Long = 1000L
+        ) = remember { ProcessManager(checkDelay) }
 
         @Composable
         fun processManagerListener(
-            onChanged: ProcessManager.(processHandle: ProcessHandle?) -> Unit,
+            onChanged: ProcessManager.(processHandle: ProcessHandle?) -> Unit = {},
         ) = processManagerListener(onChanged, onChanged)
 
         @Composable
@@ -115,7 +121,7 @@ class ProcessManager(
             onRemove: ProcessManager.(processHandle: ProcessHandle?) -> Unit,
             api: DesktopProvider = LocalDesktop.current,
             processManger: ProcessManager = api.processManager
-        ) {
+        ): ProcessManager {
             val addListener = object : ProcessAddListener {
                 override fun onProcessAdded(processHandle: ProcessHandle?) {
                     onAdd.invoke(processManger, processHandle)
@@ -127,13 +133,14 @@ class ProcessManager(
                 }
             }
             DisposableEffect(Unit) {
-                processManger.addOnAddListener(addListener)
-                processManger.addOnRemoveListener(removeListener)
+                processManger.addListener(addListener)
+                processManger.addListener(removeListener)
                 onDispose {
-                    processManger.removeOnAddListener(addListener)
-                    processManger.removeOnRemoveListener(removeListener)
+                    processManger.removeListener(addListener)
+                    processManger.removeListener(removeListener)
                 }
             }
+            return processManger
         }
     }
 

+ 6 - 0
src/main/kotlin/eu/mjdev/desktop/provider/DesktopScope.kt

@@ -119,6 +119,12 @@ open class DesktopScope(
     val backgrounds
         get() = currentUser.backgrounds
 
+    val wifiConnections
+        get() = connectionManager.wifiConnections
+
+    val processManager
+        get() = api.processManager
+
     companion object {
         @Composable
         fun withDesktopScope(