From 4ab1cc9a6bf210325114ea85465839d4bc01f84f Mon Sep 17 00:00:00 2001 From: JFronny Date: Mon, 22 Jul 2024 16:42:15 +0200 Subject: [PATCH] feat: implement SSD support --- native/src/main/c/native.c | 5 ++- .../jfronny/globalmenu/GlobalMenuService.kt | 35 ++++++++++++++++--- .../io/gitlab/jfronny/globalmenu/Lambda.kt | 8 ++--- .../io/gitlab/jfronny/globalmenu/Reflect.kt | 18 ++++++++-- 4 files changed, 54 insertions(+), 12 deletions(-) diff --git a/native/src/main/c/native.c b/native/src/main/c/native.c index 898d9f8..3b4caf0 100644 --- a/native/src/main/c/native.c +++ b/native/src/main/c/native.c @@ -15,7 +15,6 @@ struct WLFrame { void *pad5; void *pad6; void *pad7; - void *pad8; jboolean toplevel; union { struct xdg_toplevel *xdg_toplevel; @@ -101,6 +100,10 @@ JNIEXPORT jlong JNICALL Java_io_gitlab_jfronny_globalmenu_Native_createDecoratio (*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/RuntimeException"), "Not a toplevel"); return 0; } + if (frame->xdg_toplevel == NULL) { + (*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/RuntimeException"), "No xdg_toplevel extracted"); + return 0; + } return (jlong) (intptr_t) zxdg_decoration_manager_v1_get_toplevel_decoration(zxdg_decoration_manager_v1, frame->xdg_toplevel); } diff --git a/src/main/kotlin/io/gitlab/jfronny/globalmenu/GlobalMenuService.kt b/src/main/kotlin/io/gitlab/jfronny/globalmenu/GlobalMenuService.kt index 9139c63..ab96951 100644 --- a/src/main/kotlin/io/gitlab/jfronny/globalmenu/GlobalMenuService.kt +++ b/src/main/kotlin/io/gitlab/jfronny/globalmenu/GlobalMenuService.kt @@ -4,6 +4,7 @@ import com.canonical.appmenu.Registrar import com.intellij.openapi.Disposable import com.intellij.openapi.application.Application import com.intellij.openapi.application.ApplicationActivationListener +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.util.Disposer import com.intellij.openapi.wm.IdeFrame import com.intellij.openapi.wm.impl.IdeFrameImpl @@ -16,6 +17,10 @@ import org.freedesktop.dbus.DBusPath import org.freedesktop.dbus.connections.impl.DBusConnection import org.freedesktop.dbus.connections.impl.DBusConnectionBuilder import org.freedesktop.dbus.types.UInt32 +import java.awt.Dimension +import java.awt.Window +import javax.swing.FocusManager +import javax.swing.JFrame import javax.swing.JMenuBar class GlobalMenuService(private val app: Application) : ApplicationActivationListener { @@ -23,22 +28,44 @@ class GlobalMenuService(private val app: Application) : ApplicationActivationLis super.applicationActivated(ideFrame) if (!GlobalMenu.Native.isSupported) return val peer: Peer - val menuBar: JMenuBar + val menuBar: JMenuBar? + val frame: JFrame + GlobalMenu.Log.warn(ideFrame.toString()) when (ideFrame) { is ProjectFrameHelper -> { peer = ideFrame.rootPane.peer menuBar = ideFrame.rootPane.jMenuBar + frame = ideFrame.frame } is IdeFrameImpl -> { peer = ideFrame.peer menuBar = ideFrame.jMenuBar + frame = ideFrame } else -> return } - if (GlobalMenu.Native.isMenuSupported && GMSettings.getInstance().state.menu) addGlobalMenu(menuBar, peer) - else connection?.unExportObject(DbusmenuImpl.getMenuPath(peer.nativePtr)) - if (GlobalMenu.Native.isDecorationSupported && GMSettings.getInstance().state.decorations) { + onActivate(peer, frame, menuBar) + } + private fun onActivate(peer: Peer, frame: Window, menuBar: JMenuBar?) { + if (GlobalMenu.Native.isMenuSupported && GMSettings.getInstance().state.menu && menuBar != null) addGlobalMenu(menuBar, peer) + else connection?.unExportObject(DbusmenuImpl.getMenuPath(peer.nativePtr)) + if (GlobalMenu.Native.isDecorationSupported && GMSettings.getInstance().state.decorations && peer is WLPeer) { + if (peer.decorated) { + // Disable client-side decorations and enable server-side decorations + peer.decorated = false + frame.size = Dimension(frame.width, frame.height + 1) + val decoration = GlobalMenu.Native.createDecoration(peer.nativePtr) +// Disposer.register(lastMenu!!) { GlobalMenu.Native.destroyDecoration(decoration) + GlobalMenu.Native.setDecoration(decoration, 2) + } + } + ApplicationManager.getApplication().invokeLater { + val frame1 = FocusManager.getCurrentManager().focusedWindow + if (frame != frame1) onActivate(frame1.peer, frame1, when (frame1) { + is JFrame -> frame1.jMenuBar + else -> null + }) } } diff --git a/src/main/kotlin/io/gitlab/jfronny/globalmenu/Lambda.kt b/src/main/kotlin/io/gitlab/jfronny/globalmenu/Lambda.kt index bc394a0..367531e 100644 --- a/src/main/kotlin/io/gitlab/jfronny/globalmenu/Lambda.kt +++ b/src/main/kotlin/io/gitlab/jfronny/globalmenu/Lambda.kt @@ -1,14 +1,14 @@ package io.gitlab.jfronny.globalmenu -import java.util.function.BiConsumer -import java.util.function.BiFunction +import java.util.function.* import java.util.function.Function -import java.util.function.Supplier val Function.unchecked: Function get() = Function { it: Any -> apply(it as T) } +val Consumer.unchecked: Consumer get() = Consumer { accept(it as T) } val BiConsumer.unchecked1: BiConsumer get() = BiConsumer { t1: Any, t2: T2 -> accept(t1 as T1, t2) } operator fun Runnable.invoke() = run() operator fun Supplier.invoke() = get() operator fun Function.invoke(t: T): R = apply(t) operator fun BiFunction.invoke(t1: T1, t2: T2): R = apply(t1, t2) -operator fun BiConsumer.invoke(t1: T1, t2: T2) = accept(t1, t2) \ No newline at end of file +operator fun BiConsumer.invoke(t1: T1, t2: T2) = accept(t1, t2) +operator fun Consumer.invoke(t: T) = accept(t) \ No newline at end of file diff --git a/src/main/kotlin/io/gitlab/jfronny/globalmenu/Reflect.kt b/src/main/kotlin/io/gitlab/jfronny/globalmenu/Reflect.kt index 60f6bf5..9d6387e 100644 --- a/src/main/kotlin/io/gitlab/jfronny/globalmenu/Reflect.kt +++ b/src/main/kotlin/io/gitlab/jfronny/globalmenu/Reflect.kt @@ -3,6 +3,7 @@ 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.awt.Insets import java.lang.reflect.AccessibleObject import java.lang.reflect.Field @@ -42,14 +43,25 @@ class WLPeer(private val inner: Any) : Peer { fun performLocked(runnable: Runnable) { performLockedMethod(inner, runnable) } - fun registerCleaner(runnable: Runnable) { - GlobalMenu.Cleaner.register(this, runnable) - } + var decorated: Boolean + get() = containerPeerClass.isInstance(inner) && getInsetsMethod(inner).top > 0 + set(value) { + val decoration = decorationField.get(inner) + isUndecoratedField.setBoolean(decoration, !value) + markRepaintNeededMethod(decoration) + } companion object { 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) } + private val containerPeerClass = Class.forName("java.awt.peer.ContainerPeer") + private val getInsetsMethod = Reflect.instanceFunction(containerPeerClass, "getInsets", Insets::class.java).unchecked + private val decoratedPeerClass = Class.forName("sun.awt.wl.WLDecoratedPeer") + private val decorationField = decoratedPeerClass.getDeclaredField("decoration").apply { setAccessible(this) } + private val frameDecorationClass = Class.forName("sun.awt.wl.WLFrameDecoration") + private val markRepaintNeededMethod = Reflect.instanceProcedure(frameDecorationClass, "markRepaintNeeded").unchecked + private val isUndecoratedField = frameDecorationClass.getDeclaredField("isUndecorated").apply { setAccessible(this) } } }