Skip to content

Commit

Permalink
Add Windows Jump Lists support in AWT (#943)
Browse files Browse the repository at this point in the history
This PR adds Windows Jump Lists support to AWT and fixes compilation on
Visual Studio 2022.
  • Loading branch information
Alovchin91 authored Jul 10, 2024
1 parent 576deeb commit c4db278
Show file tree
Hide file tree
Showing 7 changed files with 719 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package org.jetbrains.skiko.sample

import org.jetbrains.skiko.*
import org.jetbrains.skiko.windows.*
import java.awt.Dimension
import javax.swing.*

fun main() {
fun main(args: Array<String>) {
val skiaLayer = SkiaLayer()
parseArgs(args)
val app = run {
//EmojiStory()
AwtClocks(skiaLayer)
Expand All @@ -20,5 +22,70 @@ fun main() {
skiaLayer.needRedraw()
window.pack()
window.isVisible = true
createWindowsJumpList()
}
}

/**
* The Jump List sample requires a separate executable for two reasons:
* 1. The java.exe process somehow blocks the Jump Lists so to enable them we would have to provide
* an AppUserModelID for the sample's window, and
* 2. In the current implementation, the Jump List items launch the calling process when clicked with
* command line arguments provided to the JumpListItem. Without a separate executable,
* java.exe will be launched.
*/
private fun createWindowsJumpList() {
// Check if Jump Lists are supported
if (JumpList.isSupported()) {
// Start a Jump List building transaction
JumpList.build {
// If the process has an explicit AppUserModelID, it should be set for the Jump List
// before calling [beginList]
// setAppID("org.jetbrains.skiko.SkiaMultiplatformSample.awt")

// Create a new Jump List
beginList()

// Collect elements to add to the Jump List, e.g. some recent documents
val recentItems = listOf(
JumpListItem("Recent item 1", "path\\to\\recent\\item\\1").apply {
attributes = JumpListItemAttributes().apply {
description = "The first recent item"
icon = JumpListItemIcon("explorer.exe", 0)
}
}
)

// TODO: Exclude [removedItems] from [recentItems]
val removedItems = getRemovedItems()

// Add recent items to the Jump List
addCategory("Recent items", recentItems)

// Add a user task to the Jump List
addUserTask(JumpListItem("User Task 1", "--user-task-1"))

// Commit the Jump List and finish the transaction
try {
commit().onFailure {
println("The Jump List has been committed but appending categories failed. Please make sure Jump Lists are enabled for the user.")
}
}
catch (e: JumpListException) {
println("Couldn't build the Jump List, most likely because the sample is run via java.exe. ${e.message}")
}
}
}
}

// When a Jump List item is clicked, the app is re-launched with an argument
// passed to the JumpListItem's constructor
private fun parseArgs(args: Array<String>) {
for(arg in args) {
when (arg) {
"--user-task-1" -> println("User Task 1 in the Jump List was clicked!")
"path\\to\\recent\\item\\1" -> println("Recent Item 1 in the Jump List was clicked!")
else -> println("Unknown argument: ($arg)")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.bundling.Jar
import org.gradle.api.tasks.testing.Test
import org.gradle.kotlin.dsl.withType
import org.gradle.util.internal.VersionNumber
import projectDirs
import registerOrGetSkiaDirProvider
import registerSkikoTask
Expand Down Expand Up @@ -295,13 +296,25 @@ fun SkikoProjectContext.createLinkJvmBindings(
"/ignore:4217"
)
)
// workaround for VS Build Tools 2022 (17.2+) change
// https://developercommunity.visualstudio.com/t/-imp-std-init-once-complete-unresolved-external-sy/1684365#T-N10041864
if (windowsSdkPaths.toolchainVersion >= VersionNumber.parse("14.32")) {
addAll(
arrayOf(
"/ALTERNATENAME:__imp___std_init_once_begin_initialize=__imp_InitOnceBeginInitialize",
"/ALTERNATENAME:__imp___std_init_once_complete=__imp_InitOnceComplete"
)
)
}
addAll(
arrayOf(
"/NOLOGO",
"/DLL",
"Advapi32.lib",
"gdi32.lib",
"Dwmapi.lib",
"ole32.lib",
"Propsys.lib",
"shcore.lib",
"user32.lib",
)
Expand Down
16 changes: 15 additions & 1 deletion skiko/buildSrc/src/main/kotlin/windowsSdkPaths.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import org.gradle.api.invocation.Gradle
import org.gradle.kotlin.dsl.support.serviceOf
import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.*
import org.gradle.nativeplatform.platform.internal.NativePlatformInternal
import org.gradle.nativeplatform.toolchain.internal.EmptySystemLibraries
import org.gradle.nativeplatform.toolchain.internal.SystemLibraries
import org.gradle.nativeplatform.toolchain.internal.msvcpp.*
import org.gradle.util.internal.VersionNumber
import java.io.File
import kotlin.math.abs

Expand All @@ -12,6 +14,7 @@ data class WindowsSdkPaths(
val linker: File,
val includeDirs: Collection<File>,
val libDirs: Collection<File>,
val toolchainVersion: VersionNumber,
)

private const val ENV_SKIKO_VSBT_PATH = "SKIKO_VSBT_PATH"
Expand All @@ -26,12 +29,14 @@ fun findWindowsSdkPaths(gradle: Gradle, arch: Arch): WindowsSdkPaths {
val visualCpp = finder.findVisualCpp()
val windowsSdk = finder.findWindowsSdk()
val ucrt = finder.findUcrt()
val systemLibraries = listOf(visualCpp, windowsSdk, ucrt)
val winrt = finder.findWinrt()
val systemLibraries = listOf(visualCpp, windowsSdk, ucrt, winrt)
return WindowsSdkPaths(
compiler = visualCpp.compilerExecutable.fixPathFor(arch),
linker = visualCpp.linkerExecutable.fixPathFor(arch),
includeDirs = systemLibraries.flatMap { it.includeDirs }.map { it.fixPathFor(arch) },
libDirs = systemLibraries.flatMap { it.libDirs }.map { it.fixPathFor(arch) },
toolchainVersion = visualCpp.implementationVersion,
)
}

Expand Down Expand Up @@ -92,6 +97,15 @@ private class GradleWindowsComponentFinderWrapper(
?: error("UCRT component for host platform '$hostPlatform' is null")
}

fun findWinrt(): SystemLibraries {
// Gradle doesn't have a Locator for WinRT, so we take the UCRT one and fix the path
return object : EmptySystemLibraries() {
override fun getIncludeDirs(): List<File> {
return findUcrt().includeDirs.map { File(it.path.replace("ucrt", "winrt")) }
}
}
}

private fun <T : Any> List<T>.chooseComponentByPreferredVersion(
componentType: String,
preferredVersionEnvVar: String,
Expand Down
8 changes: 8 additions & 0 deletions skiko/src/awtMain/cpp/include/jni_helpers.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
#pragma once

#include <cstdint>
#include <string>
#include <jni.h>

template<typename T>
T inline fromJavaPointer(jlong ptr) { return reinterpret_cast<T>(static_cast<uintptr_t>(ptr)); }

template<typename T>
jlong inline toJavaPointer(T ptr) { return static_cast<jlong>(reinterpret_cast<uintptr_t>(ptr)); }

std::wstring inline toStdString(JNIEnv *env, jstring jstr) {
const jchar* jchars = env->GetStringCritical(jstr, NULL);
std::wstring wstr = reinterpret_cast<const wchar_t*>(jchars);
env->ReleaseStringCritical(jstr, jchars);
return wstr;
}
Loading

0 comments on commit c4db278

Please sign in to comment.