From ef6197a4efbcc77dad26956e1eb05b99d6e1bc3e Mon Sep 17 00:00:00 2001 From: YuutaW <17158086+Trumeet@users.noreply.github.com> Date: Fri, 28 Jun 2019 19:17:07 -0700 Subject: feat: add a UI BREAKING CHANGE: running custom programs is removed Signed-off-by: Trumeet <17158086+Trumeet@users.noreply.github.com> --- README.md | 8 ++ build.gradle | 24 ----- build.gradle.kts | 40 ++++++++ settings.gradle | 2 +- .../kotlin/moe.yuuta.desktop/Desktop.kt | 93 +++++++++++++++++++ src/desktopMain/kotlin/moe.yuuta.desktop/WinApi.kt | 71 ++++++++++++++ .../kotlin/moe.yuuta.desktop/WinDesktop.kt | 8 ++ src/desktopMain/resources/desktop.manifest | 42 +++++++++ src/desktopMain/resources/desktop.rc | 6 ++ src/mingwMain/kotlin/moe.yuuta.desktop/Desktop.kt | 103 --------------------- src/mingwMain/kotlin/moe.yuuta.desktop/WinApi.kt | 46 --------- .../kotlin/moe.yuuta.desktop/WinDesktop.kt | 8 -- 12 files changed, 269 insertions(+), 182 deletions(-) delete mode 100644 build.gradle create mode 100644 build.gradle.kts create mode 100644 src/desktopMain/kotlin/moe.yuuta.desktop/Desktop.kt create mode 100644 src/desktopMain/kotlin/moe.yuuta.desktop/WinApi.kt create mode 100644 src/desktopMain/kotlin/moe.yuuta.desktop/WinDesktop.kt create mode 100644 src/desktopMain/resources/desktop.manifest create mode 100644 src/desktopMain/resources/desktop.rc delete mode 100644 src/mingwMain/kotlin/moe.yuuta.desktop/Desktop.kt delete mode 100644 src/mingwMain/kotlin/moe.yuuta.desktop/WinApi.kt delete mode 100644 src/mingwMain/kotlin/moe.yuuta.desktop/WinDesktop.kt diff --git a/README.md b/README.md index 236e0a2..135770a 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,14 @@ Feel free to ask me questions in Issues and I'm sure I cannot answer them becaus * [https://github.com/AlphaDelta/Secure-Desktop](https://github.com/AlphaDelta/Secure-Desktop) +* [msink/kotlin-ui](https://github.com/msink/kotlin-ui) + +* [msink/hello-libui](https://github.com/msink/hello-libui) + +* [msink/kotlin-libui](https://github.com/msink/kotlin-libui) + +* [libui](https://github.com/andlabs/libui) + * Others # Licenses diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 47dcc08..0000000 --- a/build.gradle +++ /dev/null @@ -1,24 +0,0 @@ -plugins { - id 'org.jetbrains.kotlin.multiplatform' version '1.3.40' -} -repositories { - jcenter() - mavenCentral() -} - -kotlin { - mingwX64() - sourceSets { - mingwX64Main { - mingwX64Main.kotlin.srcDirs += file("src/mingwMain/kotlin") - } - } - mingwX64 { - binaries { - executable { - entryPoint 'moe.yuuta.desktop.main' - linkerOpts "-Wl,--subsystem,windows" - } - } - } -} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..f5834c1 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,40 @@ +plugins { + kotlin("multiplatform") version "1.3.40" +} +repositories { + jcenter() +} + +kotlin { + mingwX86("desktop").binaries.executable { + entryPoint("moe.yuuta.desktop.main") + linkerOpts("-Wl,--subsystem,windows") + windowsResources("desktop.rc") + } + val desktopMain by sourceSets.getting { + dependencies { + implementation("com.github.msink:libui:0.1.4") + } + } +} + +fun org.jetbrains.kotlin.gradle.plugin.mpp.Executable.windowsResources(rcFileName: String) { + val taskName = linkTaskName.replaceFirst("link", "windres") + val inFile = compilation.defaultSourceSet.resources.sourceDirectories.singleFile.resolve(rcFileName) + val outFile = buildDir.resolve("processedResources/$taskName.res") + + val windresTask = tasks.create(taskName) { + val konanUserDir = System.getenv("KONAN_DATA_DIR") ?: "${System.getProperty("user.home")}/.konan" + val konanLlvmDir = "$konanUserDir/dependencies/msys2-mingw-w64-i686-gcc-7.4.0-clang-llvm-6.0.1/bin" + + inputs.file(inFile) + outputs.file(outFile) + commandLine("$konanLlvmDir/windres", inFile, "-D_${buildType.name}", "-O", "coff", "-o", outFile) + environment("PATH", "$konanLlvmDir;${System.getenv("PATH")}") + + dependsOn(compilation.compileKotlinTask) + } + + linkTask.dependsOn(windresTask) + linkerOpts(outFile.toString()) +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 35d5053..87a3ad7 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,2 @@ rootProject.name = 'Desktop' -// enableFeaturePreview('GRADLE_METADATA') \ No newline at end of file +enableFeaturePreview('GRADLE_METADATA') \ No newline at end of file diff --git a/src/desktopMain/kotlin/moe.yuuta.desktop/Desktop.kt b/src/desktopMain/kotlin/moe.yuuta.desktop/Desktop.kt new file mode 100644 index 0000000..b52680e --- /dev/null +++ b/src/desktopMain/kotlin/moe.yuuta.desktop/Desktop.kt @@ -0,0 +1,93 @@ +package moe.yuuta.desktop + +import kotlinx.cinterop.* +import libui.ktx.MsgBoxError +import libui.ktx.appWindow +import libui.ktx.hbox +import libui.ktx.tableview +import platform.windows.* + +const val FLAG = DESKTOP_READOBJECTS or + DESKTOP_CREATEWINDOW or + DESKTOP_CREATEMENU or + DESKTOP_HOOKCONTROL or + DESKTOP_JOURNALRECORD or + DESKTOP_JOURNALPLAYBACK or + DESKTOP_ENUMERATE or + DESKTOP_WRITEOBJECTS or + DESKTOP_SWITCHDESKTOP + +private val mDesktops = mutableListOf() + +@Suppress("unused") +fun main(args: Array?) { + try { + appMain() + } catch (e: Throwable) { + MessageBoxA(null, + "App crashed unexpectedly.\n" + + "Exception: $e\n" + + "System error code: ${GetLastError()}", + "Secure Desktop", + (MB_OK or MB_ICONERROR).convert()) + } +} + +fun appMain() = appWindow( + title = "Desktops", + width = 320, + height = 100 +) { + refresh() + if (!mDesktops.contains("CustomDesktop")) { + WinApi.create("CustomDesktop", FLAG) + mDesktops.add("CustomDesktop") + } + hbox { + tableview(mDesktops) { + column("Name") { + label { + data[it] + } + } + column("Action") { + button("Switch to") { + val desk = WinApi.open(FLAG, data[it]) + if (desk == null) { + MsgBoxError("Cannot open the desktop", + "The system returns an empty desktop.\n" + + "Error: ${GetLastError()}") + return@button + } + // TODO: Run an instance in a desktop only + val pathBuffer = nativeHeap.allocArray(MAX_PATH) + GetModuleFileNameA(null, pathBuffer, MAX_PATH) + val path = pathBuffer.toKString() + nativeHeap.free(pathBuffer) + WinApi.run(path, data[it]) + if (!WinApi.switch(desk)) { + MsgBoxError("Cannot switch to the desktop", + "The system reports this operation fails.\n" + + "Error: ${GetLastError()}") + return@button + } + } + } + } + // TODO: Support creating new desktops + } +} + +private fun refresh() { + mDesktops.clear() + if (EnumDesktopsA(GetProcessWindowStation(), + staticCFunction { lpszDesktop, _ -> + if (lpszDesktop == null) return@staticCFunction 1 + mDesktops.add(lpszDesktop.toKString()) + return@staticCFunction 1 + }, + 0) == 0) { + MsgBoxError("Cannot obtain the desktop list", + "Error code: ${GetLastError()}") + } +} \ No newline at end of file diff --git a/src/desktopMain/kotlin/moe.yuuta.desktop/WinApi.kt b/src/desktopMain/kotlin/moe.yuuta.desktop/WinApi.kt new file mode 100644 index 0000000..9e596f8 --- /dev/null +++ b/src/desktopMain/kotlin/moe.yuuta.desktop/WinApi.kt @@ -0,0 +1,71 @@ +package moe.yuuta.desktop + +import kotlinx.cinterop.cValue +import kotlinx.cinterop.convert +import kotlinx.cinterop.cstr +import kotlinx.cinterop.memScoped +import platform.windows.* + +object WinApi { + fun run(program: String, desktop: String) { + memScoped { + val startupInfo = cValue { + lpDesktop = desktop.cstr.ptr + dwFlags = STARTF_RUNFULLSCREEN.convert() + } + val pi = cValue { + } + // TODO: Close these objects? + return@memScoped CreateProcessA(program, + null, + null, + null, + 0, + 0.convert(), + null, + null, + startupInfo.ptr, + pi.ptr) + } + } + + fun close(desktop: WinDesktop): Boolean { + return CloseDesktop(desktop.desktop) == 1 + } + + fun getThreadDesktop(thread: Int): WinDesktop? { + val desk = GetThreadDesktop(thread.convert()) ?: return null + return WinDesktop(desk, null) + } + + fun getThreadDesktop(): WinDesktop? = getThreadDesktop(getCurrentThreadId()) + + fun getCurrentThreadId(): Int = GetCurrentThreadId().convert() + + fun create(lpszDesktop: String, + dwDesiredAccess: Int): WinDesktop? { + val desk = CreateDesktopA(lpszDesktop, + null, + null, + 0.convert(), + dwDesiredAccess.convert(), + null) ?: return null + return WinDesktop(desk, lpszDesktop) + } + + fun switch(desktop: WinDesktop): Boolean { + return SwitchDesktop(desktop.desktop) == 1 + } + + fun setThreadDesktop(desktop: WinDesktop): Boolean { + return SetThreadDesktop(desktop.desktop) == 1 + } + + fun open(dwDesiredAccess: Int, desktop: String): WinDesktop? { + val desk = OpenDesktopA(desktop, + 0.convert(), + 0.convert(), + dwDesiredAccess.convert()) ?: return null + return WinDesktop(desk, desktop) + } +} \ No newline at end of file diff --git a/src/desktopMain/kotlin/moe.yuuta.desktop/WinDesktop.kt b/src/desktopMain/kotlin/moe.yuuta.desktop/WinDesktop.kt new file mode 100644 index 0000000..4928f9e --- /dev/null +++ b/src/desktopMain/kotlin/moe.yuuta.desktop/WinDesktop.kt @@ -0,0 +1,8 @@ +package moe.yuuta.desktop + +import platform.windows.HDESK + +data class WinDesktop( + val desktop: HDESK, + val name: String? +) \ No newline at end of file diff --git a/src/desktopMain/resources/desktop.manifest b/src/desktopMain/resources/desktop.manifest new file mode 100644 index 0000000..1dffcde --- /dev/null +++ b/src/desktopMain/resources/desktop.manifest @@ -0,0 +1,42 @@ + + + + An experimental project to let you switch freely between desktops. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/desktopMain/resources/desktop.rc b/src/desktopMain/resources/desktop.rc new file mode 100644 index 0000000..cc31055 --- /dev/null +++ b/src/desktopMain/resources/desktop.rc @@ -0,0 +1,6 @@ +// this is a UTF-8 file +#pragma code_page(65001) + +// this is the Common Controls 6 manifest +// 1 is the value of CREATEPROCESS_MANIFEST_RESOURCE_ID and 24 is the value of RT_MANIFEST +1 24 "desktop.manifest" \ No newline at end of file diff --git a/src/mingwMain/kotlin/moe.yuuta.desktop/Desktop.kt b/src/mingwMain/kotlin/moe.yuuta.desktop/Desktop.kt deleted file mode 100644 index 1d64fb8..0000000 --- a/src/mingwMain/kotlin/moe.yuuta.desktop/Desktop.kt +++ /dev/null @@ -1,103 +0,0 @@ -package moe.yuuta.desktop - -import kotlinx.cinterop.cValue -import kotlinx.cinterop.convert -import kotlinx.cinterop.cstr -import kotlinx.cinterop.memScoped -import platform.posix.printf -import platform.windows.* -import kotlin.native.concurrent.TransferMode -import kotlin.native.concurrent.Worker - -private data class WorkerArg(val desktop: InternalDesktop, - val program: String?) - -/** - * Worker does not allow transferring external references. - */ -private data class InternalDesktop( - val desktop: HDESK, - val name: String? -) - -@Suppress("unused") -fun main(args: Array?) { - printf("Secure Desktop demo by i@yuuta.moe") - val oldDesktop = WinApi.getThreadDesktop() - val desktopName = "desktop_dialog" - val newDesktop = WinApi.create(desktopName, - DESKTOP_READOBJECTS or - DESKTOP_CREATEWINDOW or - DESKTOP_CREATEMENU or - DESKTOP_HOOKCONTROL or - DESKTOP_JOURNALRECORD or - DESKTOP_JOURNALPLAYBACK or - DESKTOP_ENUMERATE or - DESKTOP_WRITEOBJECTS or - DESKTOP_SWITCHDESKTOP) - if (newDesktop == null) { - MessageBoxA(null, - "Cannot create the desktop. API returned null.", - "Secure Desktop", - (MB_OK or MB_ICONERROR).toUInt()) - return - } - if (oldDesktop == null) { - MessageBoxA(null, - "Cannot obtain the current desktop. API returned null.", - "Secure Desktop", - (MB_OK or MB_ICONERROR).toUInt()) - return - } - WinApi.switch(newDesktop) - Worker.start().execute(TransferMode.SAFE, - { WorkerArg( - InternalDesktop(newDesktop.desktop, - newDesktop.name), - if (args != null && args.isNotEmpty()) args[0] else null - ) }) { - exec(it) - }.consume { - WinApi.switch(oldDesktop) - WinApi.close(newDesktop) - } -} - -private fun exec(arg: WorkerArg): Int { - WinApi.setThreadDesktop(WinDesktop(arg.desktop.desktop, - arg.desktop.name)) - if (arg.program != null) { - if (arg.desktop.name != null) { - memScoped { - val startupInfo = cValue { - lpDesktop = arg.desktop.name.cstr.ptr - dwFlags = STARTF_RUNFULLSCREEN.convert() - } - val pi = cValue { - } - // TODO: Close these objects? - return@memScoped CreateProcessA(arg.program, - null, - null, - null, - 0, - 0.convert(), - null, - null, - startupInfo.ptr, - pi.ptr) - } - } else { - MessageBoxA(null, - "Cannot run the program. Cannot obtain the created desktop's name.", - "Secure Desktop", - (MB_OK or MB_ICONERROR).convert()) - } - } - return MessageBoxA(null, - "Hi! This is message from thread ${GetCurrentThreadId()} in the secure desktop." + - "\nTo load a custom program, run Desktop.exe ." + - "\nYou can exit the desktop when you close the dialog or press OK. Programs in the desktop won't be terminated when you exit.", - "Secure Desktop", - (MB_OK or MB_ICONINFORMATION).toUInt()) -} \ No newline at end of file diff --git a/src/mingwMain/kotlin/moe.yuuta.desktop/WinApi.kt b/src/mingwMain/kotlin/moe.yuuta.desktop/WinApi.kt deleted file mode 100644 index 2855923..0000000 --- a/src/mingwMain/kotlin/moe.yuuta.desktop/WinApi.kt +++ /dev/null @@ -1,46 +0,0 @@ -package moe.yuuta.desktop - -import kotlinx.cinterop.convert -import platform.windows.* - -object WinApi { - /* - fun list(lParam: Long, callback: (lpszDesktop: LPSTR?, - lParam: Long) -> Int): Boolean { - return EnumDesktopsA(GetProcessWindowStation(), - staticCFunction(callback), - lParam) == 1 - } - */ - fun close(desktop: WinDesktop): Boolean { - return CloseDesktop(desktop.desktop) == 1 - } - - fun getThreadDesktop(thread: Int): WinDesktop? { - val desk = GetThreadDesktop(thread.convert()) ?: return null - return WinDesktop(desk, null) - } - - fun getThreadDesktop(): WinDesktop? = getThreadDesktop(getCurrentThreadId()) - - fun getCurrentThreadId(): Int = GetCurrentThreadId().convert() - - fun create(lpszDesktop: String, - dwDesiredAccess: Int): WinDesktop? { - val desk = CreateDesktopA(lpszDesktop, - null, - null, - 0.convert(), - dwDesiredAccess.convert(), - null) ?: return null - return WinDesktop(desk, lpszDesktop) - } - - fun switch(desktop: WinDesktop): Boolean { - return SwitchDesktop(desktop.desktop) == 1 - } - - fun setThreadDesktop(desktop: WinDesktop): Boolean { - return SetThreadDesktop(desktop.desktop) == 1 - } -} \ No newline at end of file diff --git a/src/mingwMain/kotlin/moe.yuuta.desktop/WinDesktop.kt b/src/mingwMain/kotlin/moe.yuuta.desktop/WinDesktop.kt deleted file mode 100644 index 4928f9e..0000000 --- a/src/mingwMain/kotlin/moe.yuuta.desktop/WinDesktop.kt +++ /dev/null @@ -1,8 +0,0 @@ -package moe.yuuta.desktop - -import platform.windows.HDESK - -data class WinDesktop( - val desktop: HDESK, - val name: String? -) \ No newline at end of file -- cgit v1.2.3