mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-06 01:19:28 +01:00
JBR-6187 Wayland: implement server-side decoration support
Use -Dsun.awt.wl.WindowDecorationStyle=server to activate
This commit is contained in:
@@ -37,6 +37,7 @@ WAYLAND_BASIC_PROTOCOL_FILES := \
|
||||
$(WAYLAND_PROTOCOLS_ROOT)/unstable/xdg-output/xdg-output-unstable-v1.xml \
|
||||
$(WAYLAND_PROTOCOLS_ROOT)/unstable/relative-pointer/relative-pointer-unstable-v1.xml \
|
||||
$(WAYLAND_PROTOCOLS_ROOT)/unstable/text-input/text-input-unstable-v3.xml \
|
||||
$(WAYLAND_PROTOCOLS_ROOT)/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml \
|
||||
$(GTK_SHELL1_PROTOCOL_PATH) \
|
||||
#
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ package sun.awt.wl;
|
||||
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Frame;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Point;
|
||||
@@ -130,5 +129,7 @@ public abstract class FrameDecoration {
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract void notifyNativeWindowCreated(long nativePtr);
|
||||
public abstract void notifyNativeWindowToBeHidden(long nativePtr);
|
||||
public abstract void dispose();
|
||||
}
|
||||
|
||||
@@ -294,6 +294,14 @@ public abstract class FullFrameDecorationHelper extends FrameDecoration {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyNativeWindowCreated(long nativePtr) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyNativeWindowToBeHidden(long nativePtr) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
WLToolkit.getDefaultToolkit().removePropertyChangeListener("awt.os.theme.isDark", pcl);
|
||||
|
||||
@@ -74,6 +74,14 @@ public class MinimalFrameDecoration extends FrameDecoration {
|
||||
// Nothing to repaint
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyNativeWindowCreated(long nativePtr) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyNativeWindowToBeHidden(long nativePtr) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
// Nothing to dispose
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2022-2025 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.awt.wl;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Rectangle;
|
||||
|
||||
/**
|
||||
* Decorations based on the xdg-decoration-unstable-v1 protocol.
|
||||
* Supported iff WLToolkit.isSSDAvailable().
|
||||
*
|
||||
* The decoration itself is added by the server on a surface separate from the window itself,
|
||||
* so the window acts as if it is undecorated. For example, there are no insets, no special
|
||||
* repainting is done, etc.
|
||||
*/
|
||||
public class ServerSideFrameDecoration extends FrameDecoration {
|
||||
private long nativeDecorPtr;
|
||||
|
||||
public ServerSideFrameDecoration(WLDecoratedPeer peer) {
|
||||
super(peer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets getContentInsets() {
|
||||
return new Insets(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle getTitleBarBounds() {
|
||||
return new Rectangle(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getMinimumSize() {
|
||||
return new Dimension(0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(Graphics g) {
|
||||
// Nothing to paint here, the Wayland server provides all the painting
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyConfigured(boolean active, boolean maximized, boolean fullscreen) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRepaintNeeded() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markRepaintNeeded(boolean value) {
|
||||
// Nothing to repaint
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyNativeWindowCreated(long nativePtr) {
|
||||
if (!peer.targetIsWlPopup()) {
|
||||
nativeDecorPtr = createToplevelDecorationImpl(nativePtr);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyNativeWindowToBeHidden(long nativePtr) {
|
||||
if (nativeDecorPtr != 0) {
|
||||
disposeImpl(nativeDecorPtr);
|
||||
nativeDecorPtr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
// Native resources must have been already disposed when the window was hidden
|
||||
assert nativeDecorPtr == 0;
|
||||
}
|
||||
|
||||
private native long createToplevelDecorationImpl(long nativeFramePtr);
|
||||
private native void disposeImpl(long nativeDecorPtr);
|
||||
}
|
||||
@@ -113,6 +113,8 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
|
||||
private boolean repositionPopup = false; // protected by stateLock
|
||||
private boolean resizePending = false; // protected by stateLock
|
||||
|
||||
private static final boolean shadowEnabled = Boolean.parseBoolean(System.getProperty("sun.awt.wl.Shadow", "true"));
|
||||
|
||||
static {
|
||||
initIDs();
|
||||
}
|
||||
@@ -121,6 +123,10 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
|
||||
* Standard peer constructor, with corresponding Component
|
||||
*/
|
||||
WLComponentPeer(Component target) {
|
||||
this(target, true);
|
||||
}
|
||||
|
||||
protected WLComponentPeer(Component target, boolean dropShadow) {
|
||||
this.target = target;
|
||||
this.background = target.getBackground();
|
||||
Dimension size = constrainSize(target.getBounds().getSize());
|
||||
@@ -135,14 +141,11 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
|
||||
log.fine("WLComponentPeer: target=" + target + " with size=" + wlSize);
|
||||
}
|
||||
|
||||
boolean shadowEnabled = Boolean.parseBoolean(System.getProperty("sun.awt.wl.Shadow", "true"));
|
||||
if (shadowEnabled) {
|
||||
if (dropShadow && shadowEnabled) {
|
||||
shadow = new ShadowImpl(targetIsWlPopup() ? ShadowImage.POPUP_SHADOW_SIZE : ShadowImage.WINDOW_SHADOW_SIZE);
|
||||
} else {
|
||||
shadow = new NilShadow();
|
||||
}
|
||||
// TODO
|
||||
// setup parent window for target
|
||||
}
|
||||
|
||||
int getDisplayScale() {
|
||||
@@ -387,6 +390,8 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
|
||||
WLRobotPeer.setLocationOfWLSurface(wlSurface, xNative, yNative);
|
||||
}
|
||||
|
||||
notifyNativeWindowCreated(nativePtr);
|
||||
|
||||
shadow.createSurface();
|
||||
|
||||
// From xdg-shell.xml: "After creating a role-specific object and
|
||||
@@ -404,6 +409,8 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
|
||||
} else {
|
||||
performLocked(() -> {
|
||||
if (wlSurface != null) { // may get a "hide" request even though we were never shown
|
||||
notifyNativeWindowToBeHidden(nativePtr);
|
||||
|
||||
nativeHideFrame(nativePtr);
|
||||
|
||||
shadow.hide();
|
||||
@@ -414,6 +421,12 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyNativeWindowCreated(long nativePtr) {
|
||||
}
|
||||
|
||||
protected void notifyNativeWindowToBeHidden(long nativePtr) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if our target should be treated as a popup in Wayland's sense,
|
||||
* i.e. it has to have a parent to position relative to.
|
||||
@@ -440,6 +453,10 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
|
||||
shadow.updateSurfaceData();
|
||||
}
|
||||
|
||||
public boolean isResizable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSurfaceSize() {
|
||||
assert SunToolkit.isAWTLockHeldByCurrentThread();
|
||||
@@ -460,9 +477,15 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
|
||||
|
||||
wlSurface.updateSurfaceSize(surfaceWidth, surfaceHeight);
|
||||
nativeSetWindowGeometry(nativePtr, 0, 0, surfaceWidth, surfaceHeight);
|
||||
nativeSetMinimumSize(nativePtr, surfaceMinSize.width, surfaceMinSize.height);
|
||||
if (surfaceMaxSize != null) {
|
||||
nativeSetMaximumSize(nativePtr, surfaceMaxSize.width, surfaceMaxSize.height);
|
||||
if (isResizable()) {
|
||||
nativeSetMinimumSize(nativePtr, surfaceMinSize.width, surfaceMinSize.height);
|
||||
if (surfaceMaxSize != null) {
|
||||
nativeSetMaximumSize(nativePtr, surfaceMaxSize.width, surfaceMaxSize.height);
|
||||
}
|
||||
} else {
|
||||
// Prevent SSD from resizing windows that are not meant to be resizeable
|
||||
nativeSetMinimumSize(nativePtr, surfaceWidth, surfaceHeight);
|
||||
nativeSetMaximumSize(nativePtr, surfaceWidth, surfaceHeight);
|
||||
}
|
||||
|
||||
if (popupNeedsReposition()) {
|
||||
|
||||
@@ -40,10 +40,37 @@ public abstract class WLDecoratedPeer extends WLWindowPeer {
|
||||
private final boolean showMaximize;
|
||||
private final boolean showMinimize;
|
||||
|
||||
private final static String decorationPreference = System.getProperty("sun.awt.wl.WindowDecorationStyle");
|
||||
private enum DecorationTypePreference {
|
||||
DEFAULT, // The default decorations painted purely in Java
|
||||
GTK, // Decorations are painted with the help of GTK (if available)
|
||||
SERVER; // Wayland server-side decorations (if available)
|
||||
}
|
||||
|
||||
private static final DecorationTypePreference decorationType = determineDecorationPreferenceType();
|
||||
|
||||
private static DecorationTypePreference determineDecorationPreferenceType() {
|
||||
String decorationPreference = System.getProperty("sun.awt.wl.WindowDecorationStyle");
|
||||
if (decorationPreference != null) {
|
||||
if ("builtin".equals(decorationPreference)) {
|
||||
return DecorationTypePreference.DEFAULT;
|
||||
} else if ("gtk".equals(decorationPreference) && isGTKAvailable()) {
|
||||
return DecorationTypePreference.GTK;
|
||||
} else if ("server".equals(decorationPreference) && WLToolkit.isSSDAvailable()) {
|
||||
return DecorationTypePreference.SERVER;
|
||||
} else {
|
||||
return DecorationTypePreference.DEFAULT;
|
||||
}
|
||||
} else {
|
||||
if (!WLToolkit.isKDE() && isGTKAvailable()) {
|
||||
return DecorationTypePreference.GTK;
|
||||
} else {
|
||||
return DecorationTypePreference.DEFAULT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public WLDecoratedPeer(Window target, boolean isUndecorated, boolean showMinimize, boolean showMaximize) {
|
||||
super(target);
|
||||
super(target, decorationType != DecorationTypePreference.SERVER);
|
||||
this.isUndecorated = isUndecorated;
|
||||
this.showMinimize = showMinimize;
|
||||
this.showMaximize = showMaximize;
|
||||
@@ -54,20 +81,12 @@ public abstract class WLDecoratedPeer extends WLWindowPeer {
|
||||
FrameDecoration d;
|
||||
if (isUndecorated) {
|
||||
d = new MinimalFrameDecoration(this);
|
||||
} else if (decorationPreference != null) {
|
||||
if ("builtin".equals(decorationPreference)) {
|
||||
d = new DefaultFrameDecoration(this, showMinimize, showMaximize);
|
||||
} else if ("gtk".equals(decorationPreference) && isGTKAvailable()) {
|
||||
d = new GtkFrameDecoration(this, showMinimize, showMaximize);
|
||||
} else {
|
||||
d = new DefaultFrameDecoration(this, showMinimize, showMaximize);
|
||||
}
|
||||
} else {
|
||||
if (!WLToolkit.isKDE() && isGTKAvailable()) {
|
||||
d = new GtkFrameDecoration(this, showMinimize, showMaximize);
|
||||
} else {
|
||||
d = new DefaultFrameDecoration(this, showMinimize, showMaximize);
|
||||
}
|
||||
d = switch (decorationType) {
|
||||
case DecorationTypePreference.DEFAULT -> new DefaultFrameDecoration(this, showMinimize, showMaximize);
|
||||
case DecorationTypePreference.GTK -> new GtkFrameDecoration(this, showMinimize, showMaximize);
|
||||
case DecorationTypePreference.SERVER -> new ServerSideFrameDecoration(this);
|
||||
};
|
||||
}
|
||||
return d;
|
||||
}
|
||||
@@ -84,9 +103,7 @@ public abstract class WLDecoratedPeer extends WLWindowPeer {
|
||||
}
|
||||
}
|
||||
|
||||
public abstract boolean isResizable();
|
||||
public abstract boolean isInteractivelyResizable();
|
||||
|
||||
public abstract boolean isFrameStateSupported(int state);
|
||||
public abstract void setState(int newState);
|
||||
public abstract int getState();
|
||||
@@ -230,4 +247,14 @@ public abstract class WLDecoratedPeer extends WLWindowPeer {
|
||||
getDecoration().dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notifyNativeWindowCreated(long nativePtr) {
|
||||
decoration.notifyNativeWindowCreated(nativePtr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notifyNativeWindowToBeHidden(long nativePtr) {
|
||||
decoration.notifyNativeWindowToBeHidden(nativePtr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1094,6 +1094,15 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if xdg-decoration-unstable-v1 is supported and false otherwise.
|
||||
*/
|
||||
public static boolean isSSDAvailable() {
|
||||
return isSSDAvailableImpl();
|
||||
}
|
||||
|
||||
private static native boolean isSSDAvailableImpl();
|
||||
|
||||
private native int readEvents();
|
||||
private native void dispatchEventsOnEDT();
|
||||
private native void flushImpl();
|
||||
|
||||
@@ -77,7 +77,11 @@ public class WLWindowPeer extends WLComponentPeer implements WindowPeer, Surface
|
||||
}
|
||||
|
||||
public WLWindowPeer(Window target) {
|
||||
super(target);
|
||||
this(target, true);
|
||||
}
|
||||
|
||||
public WLWindowPeer(Window target, boolean dropShadow) {
|
||||
super(target, dropShadow);
|
||||
|
||||
if (!target.isFontSet()) {
|
||||
target.setFont(getDefaultFont());
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "WLRobotPeer.h"
|
||||
#include "WLGraphicsEnvironment.h"
|
||||
|
||||
#include "xdg-decoration-unstable-v1.h"
|
||||
#ifdef WAKEFIELD_ROBOT
|
||||
#include "wakefield.h"
|
||||
#endif
|
||||
@@ -547,3 +548,26 @@ JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeShowWindowMenu
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL Java_sun_awt_wl_ServerSideFrameDecoration_createToplevelDecorationImpl
|
||||
(JNIEnv *env, jobject obj, jlong ptr)
|
||||
{
|
||||
struct WLFrame *frame = jlong_to_ptr(ptr);
|
||||
if (frame->toplevel) {
|
||||
struct zxdg_toplevel_decoration_v1 * decor = zxdg_decoration_manager_v1_get_toplevel_decoration(
|
||||
xdg_decoration_manager, frame->xdg_toplevel);
|
||||
zxdg_toplevel_decoration_v1_set_mode(decor, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
|
||||
return ptr_to_jlong(decor);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_sun_awt_wl_ServerSideFrameDecoration_disposeImpl
|
||||
(JNIEnv *env, jobject obj, jlong ptr)
|
||||
{
|
||||
struct zxdg_toplevel_decoration_v1 * decor = jlong_to_ptr(ptr);
|
||||
if (decor) {
|
||||
zxdg_toplevel_decoration_v1_destroy(decor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
*/
|
||||
|
||||
#include "relative-pointer-unstable-v1.h"
|
||||
#include "xdg-decoration-unstable-v1.h"
|
||||
#ifdef HEADLESS
|
||||
#error This file should not be included in headless library
|
||||
#endif
|
||||
@@ -82,6 +83,7 @@ struct gtk_shell1* gtk_shell1 = NULL;
|
||||
struct wl_keyboard *wl_keyboard; // optional, check for NULL before use
|
||||
struct wl_pointer *wl_pointer; // optional, check for NULL before use
|
||||
struct zwp_relative_pointer_manager_v1* relative_pointer_manager; // optional, check for NULL before use
|
||||
struct zxdg_decoration_manager_v1* xdg_decoration_manager; // optional, check for NULL before use
|
||||
|
||||
#define MAX_CURSOR_SCALE 100
|
||||
struct wl_cursor_theme *cursor_themes[MAX_CURSOR_SCALE] = {NULL};
|
||||
@@ -632,6 +634,8 @@ registry_global(void *data, struct wl_registry *wl_registry,
|
||||
if (versionToBind <= version) {
|
||||
zwp_text_input_manager = wl_registry_bind(wl_registry, name, &zwp_text_input_manager_v3_interface, versionToBind);
|
||||
}
|
||||
} else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) {
|
||||
xdg_decoration_manager = wl_registry_bind(wl_registry, name, &zxdg_decoration_manager_v1_interface, 1);
|
||||
}
|
||||
|
||||
#ifdef WAKEFIELD_ROBOT
|
||||
@@ -904,6 +908,13 @@ Java_sun_awt_wl_WLToolkit_initIDs(JNIEnv *env, jclass clazz, jlong displayPtr)
|
||||
checkInterfacesPresent(env);
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_sun_awt_wl_WLToolkit_isSSDAvailableImpl
|
||||
(JNIEnv *env, jobject cls)
|
||||
{
|
||||
return xdg_decoration_manager != NULL;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_wl_WLToolkit_dispatchEventsOnEDT
|
||||
(JNIEnv *env, jobject obj)
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
#include "viewporter.h"
|
||||
#include "relative-pointer-unstable-v1.h"
|
||||
#include "text-input-unstable-v3.h"
|
||||
#include "xdg-decoration-unstable-v1.h"
|
||||
|
||||
#include "jvm_md.h"
|
||||
#include "jni_util.h"
|
||||
|
||||
@@ -70,6 +72,7 @@ extern struct zwp_primary_selection_device_manager_v1 *zwp_selection_dm; // opti
|
||||
extern struct zxdg_output_manager_v1 *zxdg_output_manager_v1; // optional, check for NULL before use
|
||||
extern struct zwp_relative_pointer_manager_v1* relative_pointer_manager;
|
||||
extern struct zwp_text_input_manager_v3 *zwp_text_input_manager; // optional, check for NULL before use
|
||||
extern struct zxdg_decoration_manager_v1* xdg_decoration_manager; // optional, check for NULL before use
|
||||
|
||||
JNIEnv *getEnv();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user