Skip to content

Commit

Permalink
Refactor ShellLoader, Using Class, This allows to bring more clean co…
Browse files Browse the repository at this point in the history
…de, execute multiple processes on singles shell instead of creating one for one thing only, Improve CPU Viewer, Fix Behaviour of Exit Button
  • Loading branch information
KreitinnSoftware committed Sep 2, 2024
1 parent 70c266f commit bce01c2
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 115 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import com.micewine.emu.controller.ControllerUtils.checkControllerButtons
import com.micewine.emu.controller.ControllerUtils.controllerMouseEmulation
import com.micewine.emu.controller.ControllerUtils.prepareButtonsAxisValues
import com.micewine.emu.core.ShellExecutorCmd
import com.micewine.emu.core.WineWrapper
import com.micewine.emu.core.WineWrapper.wineShell
import com.micewine.emu.fragments.FloatingLogViewerFragment
import com.micewine.emu.input.InputEventSender
import com.micewine.emu.input.InputStub
Expand Down Expand Up @@ -153,7 +153,9 @@ class EmulationActivity : AppCompatActivity(), View.OnApplyWindowInsetsListener
R.id.exit -> {
drawerLayout?.closeDrawers()

WineWrapper.wineServer("--kill")
wineShell.runCommand("pkill -9 wineserver")
wineShell.runCommand("pkill -9 .exe")
wineShell.runCommand("pkill -9 pulseaudio")

val intent = Intent(this, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
Expand Down
35 changes: 15 additions & 20 deletions app/src/main/java/com/micewine/emu/activities/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,15 @@ import com.micewine.emu.activities.GeneralSettings.Companion.SELECTED_D3DX_RENDE
import com.micewine.emu.activities.GeneralSettings.Companion.SELECTED_DRIVER_KEY
import com.micewine.emu.activities.GeneralSettings.Companion.SELECTED_DXVK_HUD_PRESET_KEY
import com.micewine.emu.activities.GeneralSettings.Companion.SELECTED_DXVK_KEY
import com.micewine.emu.activities.GeneralSettings.Companion.SELECTED_GL_PROFILE_KEY
import com.micewine.emu.activities.GeneralSettings.Companion.SELECTED_MESA_VK_WSI_PRESENT_MODE_KEY
import com.micewine.emu.activities.GeneralSettings.Companion.SELECTED_TU_DEBUG_PRESET_KEY
import com.micewine.emu.activities.GeneralSettings.Companion.SELECTED_GL_PROFILE_KEY
import com.micewine.emu.activities.GeneralSettings.Companion.SELECTED_WINED3D_KEY
import com.micewine.emu.core.ObbExtractor.extractZip
import com.micewine.emu.core.ShellExecutorCmd.executeShell
import com.micewine.emu.core.ShellExecutorCmd.ShellLoader
import com.micewine.emu.core.ShellExecutorCmd.executeShellWithOutput
import com.micewine.emu.core.WineWrapper
import com.micewine.emu.core.WineWrapper.wineShell
import com.micewine.emu.databinding.ActivityMainBinding
import com.micewine.emu.fragments.DeleteGameItemFragment
import com.micewine.emu.fragments.FileManagerFragment
Expand All @@ -63,7 +64,6 @@ import com.micewine.emu.fragments.SetupFragment
import com.micewine.emu.fragments.SetupFragment.Companion.dialogTitleText
import com.micewine.emu.fragments.SetupFragment.Companion.progressBarIsIndeterminate
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
Expand Down Expand Up @@ -139,6 +139,7 @@ class MainActivity : AppCompatActivity() {

private var bottomNavigation: BottomNavigationView? = null
private var runningXServer = false
private val xServerShell = ShellLoader()
private val homeFragment: HomeFragment = HomeFragment()
private val settingsFragment: SettingsFragment = SettingsFragment()
private val fileManagerFragment: FileManagerFragment = FileManagerFragment()
Expand Down Expand Up @@ -388,9 +389,7 @@ class MainActivity : AppCompatActivity() {

private suspend fun runWine(exePath: String, winePrefix: File) {
withContext(Dispatchers.Default) {
lifecycleScope.launch {
WineWrapper.wineServerSuspend("--foreground --persistent")
}
WineWrapper.wineServer("--foreground --persistent")

installDXWrapper(winePrefix)

Expand All @@ -410,9 +409,8 @@ class MainActivity : AppCompatActivity() {

runningXServer = true

executeShell(
"export CLASSPATH=${getClassPath(this@MainActivity)};" +
"/system/bin/app_process / com.micewine.emu.CmdEntryPoint $display", "XServer"
xServerShell.runCommand(
"env CLASSPATH=${getClassPath(this@MainActivity)} /system/bin/app_process / com.micewine.emu.CmdEntryPoint $display"
)
}
}
Expand All @@ -437,8 +435,8 @@ class MainActivity : AppCompatActivity() {

File("$appRootDir/wine-utils/CoreFonts").copyRecursively(File("$appRootDir/wine/share/wine/fonts"), true)

executeShellWithOutput("chmod 700 -R $appRootDir")
executeShellWithOutput("$usrDir/generateSymlinks.sh")
wineShell.runCommand("chmod 700 -R $appRootDir")
wineShell.runCommand("$usrDir/generateSymlinks.sh")

File("$usrDir/icons").mkdirs()

Expand Down Expand Up @@ -526,7 +524,7 @@ class MainActivity : AppCompatActivity() {

File("$userSharedFolder/AppData").mkdirs()

executeShell("ln -sf $userSharedFolder/AppData $localAppData", "Symlink")
ShellLoader().runCommand("ln -sf $userSharedFolder/AppData $localAppData")

startMenu.deleteRecursively()

Expand Down Expand Up @@ -649,17 +647,14 @@ class MainActivity : AppCompatActivity() {

suspend fun getCpuInfo() {
withContext(Dispatchers.IO) {
var usageInfo: String
var usageProcessed: String
var usageInfo: Float

while (enableCpuCounter) {
usageInfo = executeShellWithOutput("top -n 1 | grep user | head -n 1 | cut -d \"%\" -f 2 | sed \"s/cpu//g\"")

usageProcessed = usageInfo.replace(" ", "").replace("\n", "")

totalCpuUsage = "${(usageProcessed.toInt() / Runtime.getRuntime().availableProcessors())}%"
usageInfo = executeShellWithOutput("top -bn 1 -u \$(whoami) -o %CPU -q | head -n 1").toFloat() / Runtime.getRuntime().availableProcessors()

delay(750)
totalCpuUsage = "$usageInfo%"

Thread.sleep(650)
}
}
}
Expand Down
118 changes: 43 additions & 75 deletions app/src/main/java/com/micewine/emu/core/ShellExecutorCmd.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,132 +11,100 @@ import java.io.IOException
import java.io.InputStreamReader

object ShellExecutorCmd {
fun executeShell(cmd: String, msg: String?) {
fun executeShellWithOutput(cmd: String): String {
try {
Log.e(msg, "Trying to exec: $cmd")
val shell = Runtime.getRuntime().exec("/system/bin/sh")
val os = DataOutputStream(shell.outputStream)

os.writeBytes("$cmd\nexit\n")
os.flush()

val stdout = BufferedReader(InputStreamReader(shell.inputStream))
val stderr = BufferedReader(InputStreamReader(shell.errorStream))
BufferedReader(InputStreamReader(shell.errorStream))

var output = ""

val stdoutThread = Thread {
try {
var stdOut: String?
while (stdout.readLine().also { stdOut = it } != null) {
sharedLogs.appendText("$stdOut")
Log.v(msg, "$stdOut")
output += stdOut + "\n"
}
} catch (e: IOException) {
Log.e(msg, "Error reading stdout", e)
} catch (_: IOException) {
} finally {
try {
stdout.close()
} catch (e: IOException) {
Log.e(msg, "Error closing stdout", e)
}
}
}

val stderrThread = Thread {
try {
var stdErr: String?
while (stderr.readLine().also { stdErr = it } != null) {
sharedLogs.appendText("$stdErr")
Log.v(msg, "$stdErr")
}
} catch (e: IOException) {
Log.e(msg, "Error reading stderr", e)
} finally {
try {
stderr.close()
} catch (e: IOException) {
Log.e(msg, "Error closing stderr", e)
} catch (_: IOException) {
}
}
}

stdoutThread.start()
stderrThread.start()

stdoutThread.join()
stderrThread.join()

os.close()

shell.waitFor()
shell.destroy()
} catch (e: IOException) {
Log.e(msg, "IOException occurred", e)
throw RuntimeException(e)
} catch (e: InterruptedException) {
Log.e(msg, "InterruptedException occurred", e)
Thread.currentThread().interrupt()
}
}

fun executeShellWithOutput(cmd: String): String {
try {
val shell = Runtime.getRuntime().exec("/system/bin/sh")
val os = DataOutputStream(shell.outputStream)

os.writeBytes("$cmd\nexit\n")
os.flush()
return output
} catch (_: IOException) {
}

val stdout = BufferedReader(InputStreamReader(shell.inputStream))
val stderr = BufferedReader(InputStreamReader(shell.errorStream))
return ""
}

var output = ""
class ShellLoader {
var shell: Process? = Runtime.getRuntime().exec("/system/bin/sh")
var os: DataOutputStream? = DataOutputStream(shell?.outputStream)
var stdout: BufferedReader? = BufferedReader(InputStreamReader(shell?.inputStream))
var stderr: BufferedReader? = BufferedReader(InputStreamReader(shell?.errorStream))

val stdoutThread = Thread {
init {
Thread {
try {
var stdOut: String?
while (stdout.readLine().also { stdOut = it } != null) {
output += stdOut + "\n"
while ( stdout?.readLine().also { stdOut = it } != null) {
sharedLogs.appendText("$stdOut")
Log.v("ShellLoader", "$stdOut")
}
} catch (_: IOException) {
} catch (e: IOException) {
Log.e("ShellLoader", "Error reading stdout", e)
} finally {
try {
stdout.close()
} catch (_: IOException) {
stdout?.close()
} catch (e: IOException) {
Log.e("ShellLoader", "Error closing stdout", e)
}
}
}
}.start()

val stderrThread = Thread {
Thread {
try {
var stdErr: String?
while (stderr.readLine().also { stdErr = it } != null) {
output += stdErr + "\n"
while (stderr?.readLine().also { stdErr = it } != null) {
sharedLogs.appendText("$stdErr")
Log.v("ShellLoader", "$stdErr")
}
} catch (_: IOException) {
} catch (e: IOException) {
Log.e("ShellLoader", "Error reading stderr", e)
} finally {
try {
stderr.close()
} catch (_: IOException) {
stderr?.close()
} catch (e: IOException) {
Log.e("ShellLoader", "Error closing stderr", e)
}
}
}

stdoutThread.start()
stderrThread.start()

stdoutThread.join()
stderrThread.join()

os.close()
}.start()
}

shell.waitFor()
shell.destroy()
fun runCommand(cmd: String): Int {
os?.writeBytes("$cmd &\n")
os?.flush()

return output
} catch (_: IOException) {
return -1
}

return "0"
}

class ViewModelAppLogs : ViewModel() {
Expand Down
26 changes: 8 additions & 18 deletions app/src/main/java/com/micewine/emu/core/WineWrapper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,35 @@ import android.os.Build
import com.micewine.emu.activities.MainActivity.Companion.appRootDir
import com.micewine.emu.activities.MainActivity.Companion.usrDir
import com.micewine.emu.core.EnvVars.getEnv
import com.micewine.emu.core.ShellExecutorCmd.executeShell
import com.micewine.emu.core.ShellExecutorCmd.executeShellWithOutput
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import com.micewine.emu.core.ShellExecutorCmd.ShellLoader
import java.io.File

object WineWrapper {
private var IS_BOX64 = if (Build.SUPPORTED_ABIS[0] == "x86_64") "" else "$usrDir/bin/box64"
var wineShell = ShellLoader()

fun wineServer(args: String) {
executeShellWithOutput(
wineShell.runCommand(
getEnv() + "$IS_BOX64 $appRootDir/wine/bin/wineserver $args"
)
}

suspend fun wineServerSuspend(args: String) {
withContext(Dispatchers.Default) {
executeShell(
getEnv() + "$IS_BOX64 $appRootDir/wine/bin/wineserver $args", "WineServer"
)
}
}

fun wine(args: String, winePrefix: File) {
executeShell(
getEnv() + "WINEPREFIX=$winePrefix $IS_BOX64 $appRootDir/wine/bin/wine $args", "WineProcess"
wineShell.runCommand(
getEnv() + "WINEPREFIX=$winePrefix $IS_BOX64 $appRootDir/wine/bin/wine $args"
)
}

fun wine(args: String, winePrefix: File, cwd: String) {
executeShell(
wineShell.runCommand(
"cd $cwd;" +
getEnv() + "WINEPREFIX=$winePrefix $IS_BOX64 $appRootDir/wine/bin/wine $args", "WineProcess"
getEnv() + "WINEPREFIX=$winePrefix $IS_BOX64 $appRootDir/wine/bin/wine $args"
)
}

fun extractIcon(exeFile: File, output: String) {
if (exeFile.name.endsWith(".exe")) {
executeShellWithOutput(
wineShell.runCommand(
getEnv() + "$usrDir/bin/wrestool -x -t 14 '${exeFile.path}' > '$output'"
)
}
Expand Down

2 comments on commit bce01c2

@twaik
Copy link

@twaik twaik commented on bce01c2 Sep 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shell loader was needed to find apk using PackageManager API, make sure it is signed with termux-x11's key and pass control to CmdEntryPoint in conditions of different android application sandboxes. You can start CmdEntryPoint directly in background service, and it will work better. Also this way you can remove hacky code which instantiates Context and replace it with Context you have in Service.

@KreitinnSoftware
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shell loader was needed to find apk using PackageManager API, make sure it is signed with termux-x11's key and pass control to CmdEntryPoint in conditions of different android application sandboxes. You can start CmdEntryPoint directly in background service, and it will work better. Also this way you can remove hacky code which instantiates Context and replace it with Context you have in Service.

I will try do this later

Please sign in to comment.