diff options
author | YuutaW <17158086+Trumeet@users.noreply.github.com> | 2019-06-28 19:17:07 -0700 |
---|---|---|
committer | Trumeet <17158086+Trumeet@users.noreply.github.com> | 2019-06-28 19:17:07 -0700 |
commit | ef6197a4efbcc77dad26956e1eb05b99d6e1bc3e (patch) | |
tree | 58b0a96ff3746c56802628b9d48a7ca48b1e3e99 | |
parent | 58380f5159a570c13992809cce05cc00f4e9dfbc (diff) | |
download | Desktop-ef6197a4efbcc77dad26956e1eb05b99d6e1bc3e.tar Desktop-ef6197a4efbcc77dad26956e1eb05b99d6e1bc3e.tar.gz Desktop-ef6197a4efbcc77dad26956e1eb05b99d6e1bc3e.tar.bz2 Desktop-ef6197a4efbcc77dad26956e1eb05b99d6e1bc3e.zip |
feat: add a UI
BREAKING CHANGE: running custom programs is removed
Signed-off-by: Trumeet <17158086+Trumeet@users.noreply.github.com>
-rw-r--r-- | README.md | 8 | ||||
-rw-r--r-- | build.gradle | 24 | ||||
-rw-r--r-- | build.gradle.kts | 40 | ||||
-rw-r--r-- | settings.gradle | 2 | ||||
-rw-r--r-- | src/desktopMain/kotlin/moe.yuuta.desktop/Desktop.kt | 93 | ||||
-rw-r--r-- | src/desktopMain/kotlin/moe.yuuta.desktop/WinApi.kt (renamed from src/mingwMain/kotlin/moe.yuuta.desktop/WinApi.kt) | 39 | ||||
-rw-r--r-- | src/desktopMain/kotlin/moe.yuuta.desktop/WinDesktop.kt (renamed from src/mingwMain/kotlin/moe.yuuta.desktop/WinDesktop.kt) | 0 | ||||
-rw-r--r-- | src/desktopMain/resources/desktop.manifest | 42 | ||||
-rw-r--r-- | src/desktopMain/resources/desktop.rc | 6 | ||||
-rw-r--r-- | src/mingwMain/kotlin/moe.yuuta.desktop/Desktop.kt | 103 |
10 files changed, 222 insertions, 135 deletions
@@ -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<Exec>(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<String>() + +@Suppress("unused") +fun main(args: Array<String>?) { + 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<CHARVar>(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/mingwMain/kotlin/moe.yuuta.desktop/WinApi.kt b/src/desktopMain/kotlin/moe.yuuta.desktop/WinApi.kt index 2855923..9e596f8 100644 --- a/src/mingwMain/kotlin/moe.yuuta.desktop/WinApi.kt +++ b/src/desktopMain/kotlin/moe.yuuta.desktop/WinApi.kt @@ -1,17 +1,34 @@ 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 list(lParam: Long, callback: (lpszDesktop: LPSTR?, - lParam: Long) -> Int): Boolean { - return EnumDesktopsA(GetProcessWindowStation(), - staticCFunction(callback), - lParam) == 1 + fun run(program: String, desktop: String) { + memScoped { + val startupInfo = cValue<STARTUPINFOA> { + lpDesktop = desktop.cstr.ptr + dwFlags = STARTF_RUNFULLSCREEN.convert() + } + val pi = cValue<PROCESS_INFORMATION> { + } + // 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 } @@ -43,4 +60,12 @@ object WinApi { 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/mingwMain/kotlin/moe.yuuta.desktop/WinDesktop.kt b/src/desktopMain/kotlin/moe.yuuta.desktop/WinDesktop.kt index 4928f9e..4928f9e 100644 --- a/src/mingwMain/kotlin/moe.yuuta.desktop/WinDesktop.kt +++ b/src/desktopMain/kotlin/moe.yuuta.desktop/WinDesktop.kt 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 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> + <assemblyIdentity + version="1.0.0.0" + name="YuutaW.Lab.Desktop" + type="win32" + /> + <description>An experimental project to let you switch freely between desktops.</description> + <dependency> + <dependentAssembly> + <assemblyIdentity + type="win32" + name="Microsoft.Windows.Common-Controls" + version="6.0.0.0" + processorArchitecture="*" + publicKeyToken="6595b64144ccf1df" + language="*" + /> + </dependentAssembly> + </dependency> + <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> + <security> + <requestedPrivileges> + <requestedExecutionLevel level="asInvoker"/> + </requestedPrivileges> + </security> + </trustInfo> + <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> + <application> + <!--The ID below indicates application support for Windows Vista --> + <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> + <!--The ID below indicates application support for Windows 7 --> + <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> + <!--The ID below indicates application support for Windows 8 --> + <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> + <!--The ID below indicates application support for Windows 8.1 --> + <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> + <!--The ID below indicates application support for Windows 10 --> + <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> + </application> + </compatibility> +</assembly>
\ 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<String>?) { - 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<STARTUPINFOA> { - lpDesktop = arg.desktop.name.cstr.ptr - dwFlags = STARTF_RUNFULLSCREEN.convert() - } - val pi = cValue<PROCESS_INFORMATION> { - } - // 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 <path\\to\\your\\program\\.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 |