feat: initial experimentation
This commit is contained in:
commit
9c7ca4aaa1
40
.gitignore
vendored
Normal file
40
.gitignore
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
.gradle
|
||||||
|
build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
out/
|
||||||
|
!**/src/main/**/out/
|
||||||
|
!**/src/test/**/out/
|
||||||
|
.intellijPlatform
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
bin/
|
||||||
|
!**/src/main/**/bin/
|
||||||
|
!**/src/test/**/bin/
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
24
.run/Run IDE with Plugin.run.xml
Normal file
24
.run/Run IDE with Plugin.run.xml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Run Plugin" type="GradleRunConfiguration" factoryName="Gradle">
|
||||||
|
<log_file alias="idea.log" path="$PROJECT_DIR$/build/idea-sandbox/system/log/idea.log"/>
|
||||||
|
<ExternalSystemSettings>
|
||||||
|
<option name="executionName"/>
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$"/>
|
||||||
|
<option name="externalSystemIdString" value="GRADLE"/>
|
||||||
|
<option name="scriptParameters" value=""/>
|
||||||
|
<option name="taskDescriptions">
|
||||||
|
<list/>
|
||||||
|
</option>
|
||||||
|
<option name="taskNames">
|
||||||
|
<list>
|
||||||
|
<option value="runIde"/>
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
<option name="vmOptions" value=""/>
|
||||||
|
</ExternalSystemSettings>
|
||||||
|
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||||
|
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||||
|
<DebugAllEnabled>false</DebugAllEnabled>
|
||||||
|
<method v="2"/>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
3
README.md
Normal file
3
README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# References
|
||||||
|
- [the removal commit](https://github.com/JetBrains/intellij-community/commit/336265215c1ae9bf9fd7f9c23ebfabc8fc810743)
|
||||||
|
- [a swing menu library](https://github.com/Vitaliy-Yakovchuk/dbusmenu-swing)
|
76
build.gradle.kts
Normal file
76
build.gradle.kts
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
plugins {
|
||||||
|
java
|
||||||
|
kotlin("jvm") version "1.9.24"
|
||||||
|
id("org.jetbrains.intellij.platform") version "2.0.0-beta9"
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "io.gitlab.jfronny"
|
||||||
|
version = "1.0-SNAPSHOT"
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
maven("https://maven.frohnmeyer-wds.de/artifacts")
|
||||||
|
|
||||||
|
intellijPlatform {
|
||||||
|
defaultRepositories()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val extraResources by configurations.creating
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
intellijPlatform {
|
||||||
|
intellijIdeaCommunity("242.20224.91")
|
||||||
|
instrumentationTools()
|
||||||
|
}
|
||||||
|
implementation("io.gitlab.jfronny:commons-unsafe:2.0.0-SNAPSHOT")
|
||||||
|
extraResources(project(mapOf("path" to ":native", "configuration" to "results")))
|
||||||
|
}
|
||||||
|
|
||||||
|
val copyExtraResources by tasks.creating(Copy::class) {
|
||||||
|
from(extraResources)
|
||||||
|
into(layout.buildDirectory.dir("extraResources"))
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
resources {
|
||||||
|
this.srcDir(copyExtraResources)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class RunToolTask : AbstractExecTask<RunToolTask>(RunToolTask::class.java) {
|
||||||
|
@get:InputFile abstract val inputFile: RegularFileProperty
|
||||||
|
@get:OutputFile abstract val outputFile: RegularFileProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
// Set the JVM compatibility versions
|
||||||
|
withType<JavaCompile> {
|
||||||
|
sourceCompatibility = "21"
|
||||||
|
targetCompatibility = "21"
|
||||||
|
}
|
||||||
|
withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
|
||||||
|
kotlinOptions.jvmTarget = "21"
|
||||||
|
}
|
||||||
|
|
||||||
|
patchPluginXml {
|
||||||
|
sinceBuild.set("242")
|
||||||
|
untilBuild.set("243.*")
|
||||||
|
}
|
||||||
|
|
||||||
|
signPlugin {
|
||||||
|
certificateChain.set(System.getenv("CERTIFICATE_CHAIN"))
|
||||||
|
privateKey.set(System.getenv("PRIVATE_KEY"))
|
||||||
|
password.set(System.getenv("PRIVATE_KEY_PASSWORD"))
|
||||||
|
}
|
||||||
|
|
||||||
|
publishPlugin {
|
||||||
|
token.set(System.getenv("PUBLISH_TOKEN"))
|
||||||
|
}
|
||||||
|
|
||||||
|
runIde {
|
||||||
|
this.jvmArgs("-Dawt.toolkit.name=WLToolkit")
|
||||||
|
}
|
||||||
|
}
|
6
gradle.properties
Normal file
6
gradle.properties
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# Opt-out flag for bundling Kotlin standard library -> https://jb.gg/intellij-platform-kotlin-stdlib
|
||||||
|
kotlin.stdlib.default.dependency=false
|
||||||
|
# Enable Gradle Configuration Cache -> https://docs.gradle.org/current/userguide/configuration_cache.html
|
||||||
|
org.gradle.configuration-cache=false
|
||||||
|
# Enable Gradle Build Cache -> https://docs.gradle.org/current/userguide/build_cache.html
|
||||||
|
org.gradle.caching=true
|
78
native/build.gradle.kts
Normal file
78
native/build.gradle.kts
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import org.gradle.internal.jvm.Jvm
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
`cpp-library`
|
||||||
|
}
|
||||||
|
|
||||||
|
group = rootProject.group
|
||||||
|
version = rootProject.version
|
||||||
|
|
||||||
|
abstract class RunToolTask : AbstractExecTask<RunToolTask>(RunToolTask::class.java) {
|
||||||
|
@get:InputFile abstract val inputFile: RegularFileProperty
|
||||||
|
@get:OutputFile abstract val outputFile: RegularFileProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
library {
|
||||||
|
binaries.configureEach {
|
||||||
|
val compileTask = compileTask.get()
|
||||||
|
val dir = "${Jvm.current().javaHome}/include"
|
||||||
|
compileTask.includes.from(dir)
|
||||||
|
|
||||||
|
val osFamily = targetPlatform.targetMachine.operatingSystemFamily
|
||||||
|
if (osFamily.isMacOs) compileTask.includes.from("$dir/darwin")
|
||||||
|
else if (osFamily.isLinux) compileTask.includes.from("$dir/linux")
|
||||||
|
else if (osFamily.isWindows) compileTask.includes.from("$dir/win32")
|
||||||
|
|
||||||
|
compileTask.source.from(fileTree(mapOf("dir" to "src/main/c", "include" to "**/*.c")))
|
||||||
|
compileTask.source.from(fileTree(mapOf("dir" to layout.buildDirectory.dir("generated"), "include" to "**/*.c")))
|
||||||
|
compileTask.includes.from(layout.buildDirectory.dir("generated"))
|
||||||
|
|
||||||
|
if (toolChain is VisualCpp) {
|
||||||
|
compileTask.compilerArgs.addAll("/TC")
|
||||||
|
} else if (toolChain is GccCompatibleToolChain) {
|
||||||
|
compileTask.compilerArgs.addAll("-x", "c", "-std=c11")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val results by configurations.creating {
|
||||||
|
isCanBeConsumed = true
|
||||||
|
isCanBeResolved = false
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEvaluate {
|
||||||
|
// val jar by tasks.creating(Jar::class) {
|
||||||
|
// destinationDirectory = layout.buildDirectory
|
||||||
|
// archiveClassifier.set("native")
|
||||||
|
// dependsOn()
|
||||||
|
// from()
|
||||||
|
// }
|
||||||
|
artifacts {
|
||||||
|
add("results", layout.buildDirectory.file("lib/main/release/libnative.so")) {
|
||||||
|
builtBy(tasks.named("assembleRelease"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
val generateAppmenuHeader by registering(RunToolTask::class) {
|
||||||
|
group = "custom"
|
||||||
|
inputFile = file("src/main/protocols/appmenu.xml")
|
||||||
|
outputFile = layout.buildDirectory.file("generated/appmenu.h")
|
||||||
|
commandLine("wayland-scanner", "client-header", inputFile.asFile.get().absolutePath, outputFile.asFile.get().absolutePath)
|
||||||
|
}
|
||||||
|
val generateAppmenuGlue by registering(RunToolTask::class) {
|
||||||
|
group = "custom"
|
||||||
|
inputFile = file("src/main/protocols/appmenu.xml")
|
||||||
|
outputFile = layout.buildDirectory.file("generated/appmenu.c")
|
||||||
|
commandLine("wayland-scanner", "private-code", inputFile.asFile.get().absolutePath, outputFile.asFile.get().absolutePath)
|
||||||
|
}
|
||||||
|
afterEvaluate {
|
||||||
|
named("compileDebugCpp") {
|
||||||
|
dependsOn(generateAppmenuHeader, generateAppmenuGlue)
|
||||||
|
}
|
||||||
|
named("compileReleaseCpp") {
|
||||||
|
dependsOn(generateAppmenuHeader, generateAppmenuGlue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
native/src/main/c/native.c
Normal file
67
native/src/main/c/native.c
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#include <jni.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "wayland-client-protocol.h"
|
||||||
|
#include "appmenu.h"
|
||||||
|
|
||||||
|
struct WLFrame {
|
||||||
|
jobject pad1;
|
||||||
|
struct wl_surface *wl_surface;
|
||||||
|
// more stuff follows, but we don't care about it
|
||||||
|
};
|
||||||
|
|
||||||
|
struct org_kde_kwin_appmenu_manager *org_kde_kwin_appmenu_manager = NULL;
|
||||||
|
|
||||||
|
static void registry_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) {
|
||||||
|
if (strcmp(interface, "org_kde_kwin_appmenu_manager") == 0) {
|
||||||
|
org_kde_kwin_appmenu_manager = wl_registry_bind(registry, name, &org_kde_kwin_appmenu_manager_interface, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void registry_global_remove(void *data, struct wl_registry *registry, uint32_t name) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_registry_listener wl_registry_listener = {
|
||||||
|
.global = registry_global,
|
||||||
|
.global_remove = registry_global_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_io_gitlab_jfronny_globalmenu_Native_init(JNIEnv *env, jobject obj, jlong ptr) {
|
||||||
|
struct wl_display *wl_display = (struct wl_display *) ptr;
|
||||||
|
struct wl_registry *wl_registry = wl_display_get_registry(wl_display);
|
||||||
|
if (wl_registry == NULL) {
|
||||||
|
(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/RuntimeException"), "Failed to get registry");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_registry_add_listener(wl_registry, &wl_registry_listener, NULL);
|
||||||
|
if (wl_display_roundtrip(wl_display) < 0) {
|
||||||
|
(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/RuntimeException"), "Failed to roundtrip");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jlong JNICALL Java_io_gitlab_jfronny_globalmenu_Native_create(JNIEnv *env, jobject obj, jlong ptr) {
|
||||||
|
if (org_kde_kwin_appmenu_manager == NULL) {
|
||||||
|
(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/RuntimeException"), "Appmenu manager not initialized");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
struct WLFrame *frame = (struct WLFrame *) ptr;
|
||||||
|
return (jlong) (intptr_t) org_kde_kwin_appmenu_manager_create(org_kde_kwin_appmenu_manager, frame->wl_surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_io_gitlab_jfronny_globalmenu_Native_destroy(JNIEnv *env, jobject obj, jlong ptr) {
|
||||||
|
struct org_kde_kwin_appmenu *frame = (struct org_kde_kwin_appmenu *) ptr;
|
||||||
|
org_kde_kwin_appmenu_release(frame);
|
||||||
|
org_kde_kwin_appmenu_destroy(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_io_gitlab_jfronny_globalmenu_Native_setAddress(JNIEnv *env, jobject obj, jlong ptr, jstring serviceName, jstring objectPath) {
|
||||||
|
struct org_kde_kwin_appmenu *frame = (struct org_kde_kwin_appmenu *) ptr;
|
||||||
|
char *service_name = (*env)->GetStringUTFChars(env, serviceName, NULL);
|
||||||
|
char *object_path = (*env)->GetStringUTFChars(env, objectPath, NULL);
|
||||||
|
org_kde_kwin_appmenu_set_address(frame, service_name, object_path);
|
||||||
|
(*env)->ReleaseStringUTFChars(env, serviceName, service_name);
|
||||||
|
(*env)->ReleaseStringUTFChars(env, objectPath, object_path);
|
||||||
|
}
|
35
native/src/main/protocols/appmenu.xml
Normal file
35
native/src/main/protocols/appmenu.xml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<protocol name="appmenu">
|
||||||
|
<copyright><![CDATA[
|
||||||
|
SPDX-FileCopyrightText: 2017 David Edmundson
|
||||||
|
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
]]></copyright>
|
||||||
|
<interface name="org_kde_kwin_appmenu_manager" version="1">
|
||||||
|
<description summary="appmenu dbus address interface">
|
||||||
|
This interface allows a client to link a window (or wl_surface) to an com.canonical.dbusmenu
|
||||||
|
interface registered on DBus.
|
||||||
|
</description>
|
||||||
|
<request name="create">
|
||||||
|
<arg name="id" type="new_id" interface="org_kde_kwin_appmenu"/>
|
||||||
|
<arg name="surface" type="object" interface="wl_surface"/>
|
||||||
|
</request>
|
||||||
|
</interface>
|
||||||
|
<interface name="org_kde_kwin_appmenu" version="1">
|
||||||
|
<description summary="appmenu dbus address interface">
|
||||||
|
The DBus service name and object path where the appmenu interface is present
|
||||||
|
The object should be registered on the session bus before sending this request.
|
||||||
|
If not applicable, clients should remove this object.
|
||||||
|
</description>
|
||||||
|
<request name="set_address">
|
||||||
|
<description summary="initialise or update the location of the AppMenu interface">
|
||||||
|
Set or update the service name and object path.
|
||||||
|
Strings should be formatted in Latin-1 matching the relevant DBus specifications.
|
||||||
|
</description>
|
||||||
|
<arg name="service_name" type="string" />
|
||||||
|
<arg name="object_path" type="string" />
|
||||||
|
</request>
|
||||||
|
<request name="release" type="destructor">
|
||||||
|
<description summary="release the appmenu object"/>
|
||||||
|
</request>
|
||||||
|
</interface>
|
||||||
|
</protocol>
|
10
settings.gradle.kts
Normal file
10
settings.gradle.kts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.name = "globalmenu"
|
||||||
|
|
||||||
|
include("native")
|
26
src/main/java/io/gitlab/jfronny/globalmenu/Native.java
Normal file
26
src/main/java/io/gitlab/jfronny/globalmenu/Native.java
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package io.gitlab.jfronny.globalmenu;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
public class Native {
|
||||||
|
public native void init(long displayPtr);
|
||||||
|
public native long create(long ptr);
|
||||||
|
public native void destroy(long ptr);
|
||||||
|
public native void setAddress(long ptr, String serviceName, String objectPath);
|
||||||
|
|
||||||
|
static {
|
||||||
|
if (System.getProperty("os.name").toLowerCase().contains("linux")) {
|
||||||
|
try (InputStream is = Native.class.getResourceAsStream("/libnative.so")) {
|
||||||
|
Path path = Files.createTempFile("libnative", ".so");
|
||||||
|
Files.copy(is, path, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
System.load(path.toString());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("Linux is required for the global menu plugin");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package io.gitlab.jfronny.globalmenu
|
||||||
|
|
||||||
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
|
|
||||||
|
object GlobalMenu {
|
||||||
|
val Log: Logger = Logger.getInstance(GlobalMenu::class.java)
|
||||||
|
val Native = Native()
|
||||||
|
val Cleaner = java.lang.ref.Cleaner.create()
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package io.gitlab.jfronny.globalmenu
|
||||||
|
|
||||||
|
import com.intellij.openapi.application.Application
|
||||||
|
import com.intellij.openapi.application.ApplicationActivationListener
|
||||||
|
import com.intellij.openapi.wm.IdeFrame
|
||||||
|
import com.intellij.openapi.wm.impl.IdeFrameImpl
|
||||||
|
import com.intellij.openapi.wm.impl.ProjectFrameHelper
|
||||||
|
import javax.swing.JMenuBar
|
||||||
|
|
||||||
|
class GlobalMenuService(private val app: Application) : ApplicationActivationListener {
|
||||||
|
override fun applicationActivated(ideFrame: IdeFrame) {
|
||||||
|
super.applicationActivated(ideFrame)
|
||||||
|
ideFrame.project?.let { project ->
|
||||||
|
println(ideFrame.javaClass)
|
||||||
|
if (ideFrame is ProjectFrameHelper) {
|
||||||
|
// ideFrame.frame.jMenuBar = JMenuBar()
|
||||||
|
// ideFrame.rootPane.jMenuBar
|
||||||
|
visualize(ideFrame.rootPane.jMenuBar, ideFrame.rootPane.peer)
|
||||||
|
} else if (ideFrame is IdeFrameImpl) {
|
||||||
|
visualize(ideFrame.jMenuBar, ideFrame.peer)
|
||||||
|
}
|
||||||
|
GlobalMenu.Log.warn("Activated: ${project.name}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visualize(menu: JMenuBar, peer: Peer) {
|
||||||
|
GlobalMenu.Log.warn("Using peer: $peer")
|
||||||
|
//TODO send to compositor
|
||||||
|
for (i in 0 until menu.menuCount) {
|
||||||
|
val submenu = menu.getMenu(i)
|
||||||
|
for (j in 0 until submenu.itemCount) {
|
||||||
|
val component = submenu.getItem(j)
|
||||||
|
GlobalMenu.Log.warn("${submenu.text}.${component?.text}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
peer.performLocked {
|
||||||
|
val ptr = GlobalMenu.Native.create(peer.nativePtr)
|
||||||
|
// peer.registerCleaner {
|
||||||
|
// GlobalMenu.Native.destroy(ptr)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package io.gitlab.jfronny.globalmenu
|
||||||
|
|
||||||
|
import com.intellij.ide.AppLifecycleListener
|
||||||
|
|
||||||
|
class InitializationComponent : AppLifecycleListener {
|
||||||
|
override fun appFrameCreated(commandLineArgs: MutableList<String>) {
|
||||||
|
GlobalMenu.Native.init(getDisplayPtr())
|
||||||
|
}
|
||||||
|
}
|
14
src/main/kotlin/io/gitlab/jfronny/globalmenu/Lambda.kt
Normal file
14
src/main/kotlin/io/gitlab/jfronny/globalmenu/Lambda.kt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package io.gitlab.jfronny.globalmenu
|
||||||
|
|
||||||
|
import java.util.function.BiConsumer
|
||||||
|
import java.util.function.BiFunction
|
||||||
|
import java.util.function.Function
|
||||||
|
import java.util.function.Supplier
|
||||||
|
|
||||||
|
val <T, R> Function<T, R>.unchecked: Function<Any, R> get() = Function { it: Any -> apply(it as T) }
|
||||||
|
val <T1, T2> BiConsumer<T1, T2>.unchecked1: BiConsumer<Any, T2> get() = BiConsumer { t1: Any, t2: T2 -> accept(t1 as T1, t2) }
|
||||||
|
operator fun Runnable.invoke() = run()
|
||||||
|
operator fun <T> Supplier<T>.invoke() = get()
|
||||||
|
operator fun <T, R> Function<T, R>.invoke(t: T): R = apply(t)
|
||||||
|
operator fun <T1, T2, R> BiFunction<T1, T2, R>.invoke(t1: T1, t2: T2): R = apply(t1, t2)
|
||||||
|
operator fun <T1, T2> BiConsumer<T1, T2>.invoke(t1: T1, t2: T2) = accept(t1, t2)
|
38
src/main/kotlin/io/gitlab/jfronny/globalmenu/Reflect.kt
Normal file
38
src/main/kotlin/io/gitlab/jfronny/globalmenu/Reflect.kt
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package io.gitlab.jfronny.globalmenu
|
||||||
|
|
||||||
|
import io.gitlab.jfronny.commons.unsafe.reflect.Reflect
|
||||||
|
import io.gitlab.jfronny.commons.unsafe.reflect.impl.CoreReflect
|
||||||
|
import java.awt.Component
|
||||||
|
import java.lang.reflect.AccessibleObject
|
||||||
|
import java.lang.reflect.Field
|
||||||
|
|
||||||
|
private val accessibleSetter = CoreReflect.lookup(AccessibleObject::class.java).findSetter(AccessibleObject::class.java, "override", Boolean::class.java)
|
||||||
|
private fun setAccessible(field: Field) {
|
||||||
|
accessibleSetter.invoke(field, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val peerField = Component::class.java.getDeclaredField("peer").apply { isAccessible = true }
|
||||||
|
|
||||||
|
val Component.peer: Peer get() = Peer(peerField.get(this))
|
||||||
|
|
||||||
|
private val componentPeerClass = Class.forName("sun.awt.wl.WLComponentPeer")
|
||||||
|
private val performLockedMethod = Reflect.instanceProcedure(componentPeerClass, "performLocked", Runnable::class.java).unchecked1
|
||||||
|
private val nativePtrField = componentPeerClass.getDeclaredField("nativePtr").apply { setAccessible(this) }
|
||||||
|
|
||||||
|
class Peer(private val inner: Any) {
|
||||||
|
val nativePtr: Long get() = nativePtrField.getLong(inner)
|
||||||
|
fun performLocked(runnable: Runnable) {
|
||||||
|
performLockedMethod(inner, runnable)
|
||||||
|
}
|
||||||
|
fun registerCleaner(runnable: Runnable) {
|
||||||
|
GlobalMenu.Cleaner.register(this, runnable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val wlDisplayClass = Class.forName("sun.awt.wl.WLDisplay")
|
||||||
|
private val getInstanceMethod = Reflect.staticFunction(wlDisplayClass, "getInstance", wlDisplayClass)
|
||||||
|
private val getDisplayPtrMethod = Reflect.instanceFunction(wlDisplayClass, "getDisplayPtr", Long::class.java).unchecked
|
||||||
|
fun getDisplayPtr(): Long {
|
||||||
|
val display = getInstanceMethod()
|
||||||
|
return getDisplayPtrMethod(display) as Long
|
||||||
|
}
|
32
src/main/resources/META-INF/plugin.xml
Normal file
32
src/main/resources/META-INF/plugin.xml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<!-- Plugin Configuration File. Read more: https://plugins.jetbrains.com/docs/intellij/plugin-configuration-file.html -->
|
||||||
|
<idea-plugin>
|
||||||
|
<!-- Unique identifier of the plugin. It should be FQN. It cannot be changed between the plugin versions. -->
|
||||||
|
<id>io.gitlab.jfronny.globalmenu</id>
|
||||||
|
|
||||||
|
<!-- Public plugin name should be written in Title Case.
|
||||||
|
Guidelines: https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html#plugin-name -->
|
||||||
|
<name>Global Menu</name>
|
||||||
|
|
||||||
|
<!-- A displayed Vendor name or Organization ID displayed on the Plugins Page. -->
|
||||||
|
<vendor email="projects.contact@frohnmeyer-wds.de" url="https://jfronny.gitlab.io">JFronny</vendor>
|
||||||
|
|
||||||
|
<!-- Description of the plugin displayed on the Plugin Page and IDE Plugin Manager.
|
||||||
|
Simple HTML elements (text formatting, paragraphs, and lists) can be added inside of <![CDATA[ ]]> tag.
|
||||||
|
Guidelines: https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html#plugin-description -->
|
||||||
|
<description><![CDATA[
|
||||||
|
Reenables the global menu on Linux systems.
|
||||||
|
]]></description>
|
||||||
|
|
||||||
|
<!-- Product and plugin compatibility requirements.
|
||||||
|
Read more: https://plugins.jetbrains.com/docs/intellij/plugin-compatibility.html -->
|
||||||
|
<depends>com.intellij.modules.platform</depends>
|
||||||
|
|
||||||
|
<!-- Extension points defined by the plugin.
|
||||||
|
Read more: https://plugins.jetbrains.com/docs/intellij/plugin-extension-points.html -->
|
||||||
|
<extensions defaultExtensionNs="com.intellij">
|
||||||
|
</extensions>
|
||||||
|
<applicationListeners>
|
||||||
|
<listener class="io.gitlab.jfronny.globalmenu.GlobalMenuService" topic="com.intellij.openapi.application.ApplicationActivationListener"/>
|
||||||
|
<listener class="io.gitlab.jfronny.globalmenu.InitializationComponent" topic="com.intellij.ide.AppLifecycleListener"/>
|
||||||
|
</applicationListeners>
|
||||||
|
</idea-plugin>
|
12
src/main/resources/META-INF/pluginIcon.svg
Normal file
12
src/main/resources/META-INF/pluginIcon.svg
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M32.0845 7.94025V4H24.0203V7.9896H16.029V4H7.91553V7.94025H4V36H16.0044V32.0045C16.0058 30.9457 16.4274 29.9308 17.1766 29.1826C17.9258 28.4345 18.9412 28.0143 20 28.0143C21.0588 28.0143 22.0743 28.4345 22.8234 29.1826C23.5726 29.9308 23.9942 30.9457 23.9956 32.0045V36H36V7.94025H32.0845Z"
|
||||||
|
fill="url(#paint0_linear)"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear" x1="2.94192" y1="4.89955" x2="37.7772" y2="39.7345"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0.15937" stop-color="#3BEA62"/>
|
||||||
|
<stop offset="0.5404" stop-color="#3C99CC"/>
|
||||||
|
<stop offset="0.93739" stop-color="#6B57FF"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 818 B |
Loading…
Reference in New Issue
Block a user