aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuutaW <17158086+Trumeet@users.noreply.github.com>2019-06-28 19:17:07 -0700
committerTrumeet <17158086+Trumeet@users.noreply.github.com>2019-06-28 19:17:07 -0700
commitef6197a4efbcc77dad26956e1eb05b99d6e1bc3e (patch)
tree58b0a96ff3746c56802628b9d48a7ca48b1e3e99
parent58380f5159a570c13992809cce05cc00f4e9dfbc (diff)
downloadDesktop-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.md8
-rw-r--r--build.gradle24
-rw-r--r--build.gradle.kts40
-rw-r--r--settings.gradle2
-rw-r--r--src/desktopMain/kotlin/moe.yuuta.desktop/Desktop.kt93
-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.manifest42
-rw-r--r--src/desktopMain/resources/desktop.rc6
-rw-r--r--src/mingwMain/kotlin/moe.yuuta.desktop/Desktop.kt103
10 files changed, 222 insertions, 135 deletions
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<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