JBR-7087 Wayland: Desktop support via GNOME

This commit is contained in:
Maxim Kartashev
2025-03-12 13:42:36 +04:00
parent 2308ce139b
commit 8c769bbc7c
7 changed files with 268 additions and 122 deletions

View File

@@ -0,0 +1,124 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025, JetBrains s.r.o.. All rights reserved.
* 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 sun.awt.SunToolkit;
import sun.awt.UNIXToolkit;
import java.awt.Desktop;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.awt.Desktop.Action;
import java.awt.peer.DesktopPeer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class WLDesktopPeer implements DesktopPeer {
// supportedActions may be changed from native within the init() call
private static final List<Desktop.Action> supportedActions
= new ArrayList<>(Arrays.asList(Action.OPEN, Action.MAIL, Action.BROWSE));
private static boolean nativeLibraryLoaded = false;
private static boolean initExecuted = false;
private static void initWithLock(){
SunToolkit.awtLock();
try {
if (!initExecuted) {
nativeLibraryLoaded = init(
UNIXToolkit.getEnabledGtkVersion().getNumber(),
UNIXToolkit.isGtkVerbose());
}
} finally {
initExecuted = true;
SunToolkit.awtUnlock();
}
}
WLDesktopPeer(){
initWithLock();
}
static boolean isDesktopSupported() {
initWithLock();
return nativeLibraryLoaded && !supportedActions.isEmpty();
}
public boolean isSupported(Action type) {
return supportedActions.contains(type);
}
public void open(File file) throws IOException {
try {
launch(file.toURI());
} catch (MalformedURLException e) {
throw new IOException(file.toString());
}
}
public void edit(File file) throws IOException {
throw new UnsupportedOperationException("The current platform " +
"doesn't support the EDIT action.");
}
public void print(File file) throws IOException {
throw new UnsupportedOperationException("The current platform " +
"doesn't support the PRINT action.");
}
public void mail(URI uri) throws IOException {
launch(uri);
}
public void browse(URI uri) throws IOException {
launch(uri);
}
private void launch(URI uri) throws IOException {
byte[] uriByteArray = ( uri.toString() + '\0' ).getBytes();
boolean result = false;
SunToolkit.awtLock();
try {
if (!nativeLibraryLoaded) {
throw new IOException("Failed to load native libraries.");
}
result = gnome_url_show(uriByteArray);
} finally {
SunToolkit.awtUnlock();
}
if (!result) {
throw new IOException("Failed to show URI:" + uri);
}
}
private native boolean gnome_url_show(byte[] url);
private static native boolean init(int gtkVersion, boolean verbose);
}

View File

@@ -985,18 +985,12 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
*/
@Override
public boolean isDesktopSupported() {
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Not implemented: WLToolkit.isDesktopSupported()");
}
return false;
return WLDesktopPeer.isDesktopSupported();
}
@Override
public DesktopPeer createDesktopPeer(Desktop target) {
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Not implemented: WLToolkit.createDesktopPeer()");
}
return null;
return new WLDesktopPeer();
}
@Override

View File

@@ -0,0 +1,132 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025, JetBrains s.r.o.. All rights reserved.
* 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.
*/
#ifdef HEADLESS
#error This file should not be included in headless library
#endif
#include <dlfcn.h>
#include <jni.h>
#include <jvm_md.h>
#include "Trace.h"
#include "jni_util.h"
#include "gtk_interface.h"
typedef gboolean (GNOME_URL_SHOW_TYPE)(const char *, void **);
typedef gboolean (GNOME_VFS_INIT_TYPE)(void);
static GNOME_URL_SHOW_TYPE *gnome_url_show = NULL;
static gboolean gnome_load(void) {
void *vfs_handle;
void *gnome_handle;
const char *errmsg;
GNOME_VFS_INIT_TYPE *gnome_vfs_init;
vfs_handle = dlopen(VERSIONED_JNI_LIB_NAME("gnomevfs-2", "0"), RTLD_LAZY);
if (vfs_handle == NULL) {
vfs_handle = dlopen(JNI_LIB_NAME("gnomevfs-2"), RTLD_LAZY);
if (vfs_handle == NULL) {
J2dTraceLn(J2D_TRACE_ERROR, "can not load libgnomevfs-2.so");
return FALSE;
}
}
dlerror(); /* Clear errors */
gnome_vfs_init = dlsym(vfs_handle, "gnome_vfs_init");
if (gnome_vfs_init == NULL){
J2dTraceLn(J2D_TRACE_ERROR, "dlsym( gnome_vfs_init) returned NULL");
return FALSE;
}
if ((errmsg = dlerror()) != NULL) {
J2dTraceLn1(J2D_TRACE_ERROR, "can not find symbol gnome_vfs_init %s", errmsg);
return FALSE;
}
// call gonme_vfs_init()
(*gnome_vfs_init)();
gnome_handle = dlopen(VERSIONED_JNI_LIB_NAME("gnome-2", "0"), RTLD_LAZY);
if (gnome_handle == NULL) {
gnome_handle = dlopen(JNI_LIB_NAME("gnome-2"), RTLD_LAZY);
if (gnome_handle == NULL) {
J2dTraceLn(J2D_TRACE_ERROR, "can not load libgnome-2.so");
return FALSE;
}
}
dlerror(); /* Clear errors */
gnome_url_show = dlsym(gnome_handle, "gnome_url_show");
if ((errmsg = dlerror()) != NULL) {
J2dTraceLn(J2D_TRACE_ERROR, "can not find symble gnome_url_show");
return FALSE;
}
return TRUE;
}
static gboolean gtk_has_been_loaded = FALSE;
static gboolean gnome_has_been_loaded = FALSE;
JNIEXPORT jboolean JNICALL Java_sun_awt_wl_WLDesktopPeer_init
(JNIEnv *env, jclass cls, jint version, jboolean verbose)
{
if (gtk_has_been_loaded || gnome_has_been_loaded) {
return JNI_TRUE;
}
if (gtk_load(env, version, verbose) && gtk->show_uri_load(env)) {
gtk_has_been_loaded = TRUE;
return JNI_TRUE;
} else if (gnome_load()) {
gnome_has_been_loaded = TRUE;
return JNI_TRUE;
}
return JNI_FALSE;
}
JNIEXPORT jboolean JNICALL Java_sun_awt_wl_WLDesktopPeer_gnome_1url_1show
(JNIEnv *env, jobject obj, jbyteArray url_j)
{
gboolean success = FALSE;
const gchar* url_c = (gchar*)(*env)->GetByteArrayElements(env, url_j, NULL);
if (url_c == NULL) {
JNU_ThrowOutOfMemoryError(env, 0);
return JNI_FALSE;
}
if (gtk_has_been_loaded) {
gtk->gdk_threads_enter();
success = gtk->gtk_show_uri_on_window(NULL, url_c, GDK_CURRENT_TIME, NULL);
gtk->gdk_threads_leave();
} else if (gnome_has_been_loaded) {
success = (*gnome_url_show)(url_c, NULL);
}
(*env)->ReleaseByteArrayElements(env, url_j, (jbyte*)url_c, 0);
return success ? JNI_TRUE : JNI_FALSE;
}

View File

@@ -129,17 +129,6 @@ static void update_supported_actions(JNIEnv *env) {
ADD_SUPPORTED_ACTION("OPEN");
/**
* gtk_show_uri() documentation says:
*
* > you need to install gvfs to get support for uri schemes such as http://
* > or ftp://, as only local files are handled by GIO itself.
*
* So OPEN action was safely added here.
* However, it looks like Solaris 11 have gvfs support only for 32-bit
* applications only by default.
*/
fp_g_vfs_get_default = dl_symbol("g_vfs_get_default");
fp_g_vfs_get_supported_uri_schemes = dl_symbol("g_vfs_get_supported_uri_schemes");
dlerror();
@@ -165,24 +154,25 @@ static void update_supported_actions(JNIEnv *env) {
}
}
/**
* Functions for awt_Desktop.c
*/
static gboolean gtk3_show_uri_load(JNIEnv *env) {
gboolean success = FALSE;
dlerror();
fp_gtk_show_uri = dl_symbol("gtk_show_uri");
fp_gtk_show_uri_on_window = dl_symbol("gtk_show_uri_on_window");
const char *dlsym_error = dlerror();
if (dlsym_error) {
#ifdef DEBUG
fprintf (stderr, "Cannot load symbol: %s \n", dlsym_error);
#endif /* DEBUG */
} else if (fp_gtk_show_uri == NULL) {
} else if (fp_gtk_show_uri_on_window == NULL) {
#ifdef DEBUG
fprintf(stderr, "dlsym(gtk_show_uri) returned NULL\n");
fprintf(stderr, "dlsym(gtk_show_uri_on_window) returned NULL\n");
#endif /* DEBUG */
} else {
gtk->gtk_show_uri = fp_gtk_show_uri;
gtk->gtk_show_uri_on_window = fp_gtk_show_uri_on_window;
update_supported_actions(env);
success = TRUE;
}
@@ -211,9 +201,7 @@ static void gtk3_file_chooser_load()
fp_gtk_g_slist_length = dl_symbol("g_slist_length");
}
static void empty() {}
gboolean glib_version_2_68 = FALSE;
static gboolean glib_version_2_68 = FALSE;
GtkApi* gtk3_load(JNIEnv *env, const char* lib_name)
{
@@ -240,7 +228,7 @@ GtkApi* gtk3_load(JNIEnv *env, const char* lib_name)
}
// Make sure all the functions that are used here can be expected to be available.
if (fp_gtk_check_version(3, 20, 0)) {
if (fp_gtk_check_version(3, 22, 0)) {
// GTK 3.20 was released in 2016, expect at least this version.
dlclose(gtk3_libhandle);
gtk3_libhandle = NULL;
@@ -284,8 +272,6 @@ GtkApi* gtk3_load(JNIEnv *env, const char* lib_name)
fp_g_strfreev = dl_symbol("g_strfreev");
/* GDK */
fp_gdk_get_default_root_window =
dl_symbol("gdk_get_default_root_window");
/* Pixbuf */
fp_gdk_pixbuf_new = dl_symbol("gdk_pixbuf_new");
@@ -2726,87 +2712,6 @@ static void transform_detail_string (const gchar *detail, GtkStyleContext *conte
}
}
inline static int scale_down_ceiling(int what, int scale)
{
return (int)ceilf(what / (float)scale);
}
inline static int scale_down_floor(int what, int scale)
{
return (int)floorf(what / (float)scale);
}
static gboolean gtk3_get_drawable_data(JNIEnv *env, jintArray pixelArray,
int x, jint y, jint width, jint height, jint jwidth, int dx, int dy) {
GdkPixbuf *pixbuf;
jint *ary;
int skip_left = 0;
int skip_top = 0;
GdkWindow *root = (*fp_gdk_get_default_root_window)();
int win_scale = (*fp_gdk_window_get_scale_factor)(root);
// Scale the coordinate and size carefully such that the captured area
// is at least as large as requested. We trim off excess later by
// using the skip_* variables.
const int x_scaled = scale_down_floor(x, win_scale);
const int y_scaled = scale_down_floor(y, win_scale);
skip_left = x - x_scaled*win_scale;
skip_top = y - y_scaled*win_scale;
DASSERT(skip_left >= 0 && skip_top >= 0);
const int x_right_scaled = scale_down_ceiling(x + width, win_scale);
const int width_scaled = x_right_scaled - x_scaled;
DASSERT(width_scaled > 0);
const int y_bottom_scaled = scale_down_ceiling(y + height, win_scale);
const int height_scaled = y_bottom_scaled - y_scaled;
DASSERT(height_scaled > 0);
pixbuf = (*fp_gdk_pixbuf_get_from_drawable)(
root, x_scaled, y_scaled, width_scaled, height_scaled);
if (pixbuf) {
int nchan = (*fp_gdk_pixbuf_get_n_channels)(pixbuf);
int stride = (*fp_gdk_pixbuf_get_rowstride)(pixbuf);
if ((*fp_gdk_pixbuf_get_width)(pixbuf) >= width
&& (*fp_gdk_pixbuf_get_height)(pixbuf) >= height
&& (*fp_gdk_pixbuf_get_bits_per_sample)(pixbuf) == 8
&& (*fp_gdk_pixbuf_get_colorspace)(pixbuf) == GDK_COLORSPACE_RGB
&& nchan >= 3
) {
guchar *p, *pix = (*fp_gdk_pixbuf_get_pixels)(pixbuf);
ary = (*env)->GetPrimitiveArrayCritical(env, pixelArray, NULL);
if (ary) {
jint _x, _y;
int index;
for (_y = 0; _y < height; _y++) {
for (_x = 0; _x < width; _x++) {
p = pix + (intptr_t) (_y + skip_top) * stride
+ (_x + skip_left) * nchan;
index = (_y + dy) * jwidth + (_x + dx);
ary[index] = 0xff000000
| (p[0] << 16)
| (p[1] << 8)
| (p[2]);
}
}
(*env)->ReleasePrimitiveArrayCritical(env, pixelArray, ary, 0);
}
}
(*fp_g_object_unref)(pixbuf);
}
return JNI_FALSE;
}
static GdkWindow* gtk3_get_window(void *widget)
{
return fp_gtk_widget_get_window((GtkWidget*)widget);
}
static void gtk3_init(GtkApi* gtk)
{
gtk->version = GTK_3;
@@ -2847,8 +2752,7 @@ static void gtk3_init(GtkApi* gtk)
gtk->get_file_icon_data = &gtk3_get_file_icon_data;
gtk->gdk_threads_enter = fp_gdk_threads_enter;
gtk->gdk_threads_leave = fp_gdk_threads_leave;
gtk->gtk_show_uri = fp_gtk_show_uri;
gtk->get_drawable_data = &gtk3_get_drawable_data;
gtk->gtk_show_uri_on_window = fp_gtk_show_uri_on_window;
gtk->g_free = fp_g_free;
gtk->gtk_file_chooser_get_filename = fp_gtk_file_chooser_get_filename;
@@ -2881,7 +2785,6 @@ static void gtk3_init(GtkApi* gtk)
gtk->gtk_window_present = fp_gtk_window_present;
gtk->gtk_window_move = fp_gtk_window_move;
gtk->gtk_window_resize = fp_gtk_window_resize;
gtk->get_window = &gtk3_get_window;
gtk->g_object_unref = fp_g_object_unref;
gtk->g_list_append = fp_g_list_append;

View File

@@ -291,8 +291,6 @@ static gchar* (*fp_gtk_check_version)(guint required_major, guint
static void (*fp_g_free)(gpointer mem);
static void (*fp_g_object_unref)(gpointer object);
static GdkWindow *(*fp_gdk_get_default_root_window) (void);
static int (*fp_gdk_window_get_scale_factor) (GdkWindow *window);
static int (*fp_gdk_pixbuf_get_bits_per_sample)(const GdkPixbuf *pixbuf);
static guchar *(*fp_gdk_pixbuf_get_pixels)(const GdkPixbuf *pixbuf);
@@ -357,7 +355,7 @@ static void (*fp_g_list_free_full) (GList *list, GDestroyNotify free_func);
static void (*fp_gdk_threads_enter)(void);
static void (*fp_gdk_threads_leave)(void);
static gboolean (*fp_gtk_show_uri)(GdkScreen *screen, const gchar *uri,
static gboolean (*fp_gtk_show_uri_on_window)(GdkWindow *parent, const gchar *uri,
guint32 timestamp, GError **error);
// Implementation functions prototypes

View File

@@ -601,11 +601,8 @@ typedef struct GtkApi {
GError **error, jmethodID icon_upcall_method, jobject this);
void (*gdk_threads_enter)(void);
void (*gdk_threads_leave)(void);
gboolean (*gtk_show_uri)(GdkScreen *screen, const gchar *uri,
gboolean (*gtk_show_uri_on_window)(GdkWindow *parent, const gchar *uri,
guint32 timestamp, GError **error);
gboolean (*get_drawable_data)(JNIEnv *env, jintArray pixelArray,
jint x, jint y, jint width, jint height,
jint jwidth, int dx, int dy);
void (*g_free)(gpointer mem);
gchar* (*gtk_file_chooser_get_filename)(GtkFileChooser *chooser);
@@ -641,7 +638,6 @@ typedef struct GtkApi {
void (*gtk_window_present)(void *window);
void (*gtk_window_move)(void *window, gint x, gint y);
void (*gtk_window_resize)(void *window, gint width, gint height);
GdkWindow *(*get_window)(void *widget);
void (*g_object_unref)(gpointer object);
GList* (*g_list_append) (GList *list, gpointer data);

View File

@@ -846,7 +846,6 @@ jdk_awt_wayland = \
-java/awt/datatransfer/SystemSelection/SystemSelectionSwingTest.java \
-java/awt/datatransfer/UnicodeTransferTest/UnicodeTransferTest.java \
-java/awt/Debug \
-java/awt/Desktop \
-java/awt/Dialog \
-java/awt/dnd \
-java/awt/event/ComponentEvent/ComponentItemEventTest.java \