Skip to content

Commit 5cc0f55

Browse files
committed
[GTK] Use Foreign Function & Memory API for GTK3 OS Support
This converts the GTK-specific "org.eclipse.wb.os.linux" plugin into a multi-release jar, to take advantage of the foreign function and memory API, while still remaining compatible with older Java versions. Rather than calling the native methods via our shared library, the invocations are delegated to the Java linker. If any exception is thrown in native code, this then leads to a `Throwable` being thrown, rather than a crash of the entire IDE.
1 parent 8c73a4a commit 5cc0f55

File tree

38 files changed

+2032
-173
lines changed

38 files changed

+2032
-173
lines changed

.github/workflows/maven.yaml

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,8 @@ jobs:
2323
fail-fast: false
2424
matrix:
2525
os: [ubuntu-latest, windows-latest]
26-
java: [ 21, 25 ]
27-
include:
28-
- java: 21
29-
target: 2024-06
30-
- java: 25
31-
target: master
26+
java: [ 25 ]
27+
target: [2024-06, master]
3228
runs-on: ${{ matrix.os }}
3329
name: OS ${{ matrix.os }} Java ${{ matrix.java }} ${{ matrix.target }} compile
3430
timeout-minutes: 90

.mvn/maven.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
-Dtycho.version=5.0.0
1+
-Dtycho.version=5.0.1
22
-Djdk.xml.totalEntitySizeLimit=1000000
33
-Djdk.xml.maxGeneralEntitySizeLimit=1000000

org.eclipse.wb.doc.user/html-src/whatsnew/v123.asciidoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,10 @@ endif::[]
44

55
= What's New - v1.23.0
66

7+
== General
8+
9+
- [Linux] FFMA when using Java 24 or higher
10+
11+
The Linux-specific fragment will use the Foreign Function and Memory API when used with Java 24 or higher.
12+
713
What's new - xref:v122.adoc[*v1.22.0*]

org.eclipse.wb.os.linux/.classpath

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<classpath>
3-
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-21"/>
3+
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-24"/>
44
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
55
<classpathentry kind="src" path="src"/>
6+
<classpathentry kind="src" path="src_java24">
7+
<attributes>
8+
<attribute name="release" value="24"/>
9+
</attributes>
10+
</classpathentry>
611
<classpathentry kind="output" path="bin"/>
712
</classpath>

org.eclipse.wb.os.linux/META-INF/MANIFEST.MF

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ Bundle-ActivationPolicy: lazy
1818
Bundle-Localization: plugin
1919
Import-Package: org.apache.commons.io;version="[2.16.1,3.0.0)",
2020
org.apache.commons.lang3;version="[3.14.0,4.0.0)",
21+
org.apache.commons.lang3.function;version="[3.14.0,4.0.0)",
2122
org.eclipse.wb.os
2223
Automatic-Module-Name: org.eclipse.wb.os.linux
24+
Multi-Release: true
2325
Provide-Capability: wbp;type=os

org.eclipse.wb.os.linux/native/gtk/gtk3/rcp.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,8 @@ JNIEXPORT void JNICALL OS_NATIVE(_1gtk_1widget_1get_1allocation)
139139
gtk_widget_get_allocation((GtkWidget*)(CHANDLE) jhandle, &allocation);
140140
set_gtk_allocation(envir, jallocation, &allocation);
141141
}
142+
// basline
143+
JNIEXPORT void JNICALL OS_NATIVE(_1gtk_1widget_1get_1allocated_1baseline)
144+
(JNIEnv *envir, jobject that, JHANDLE widget) {
145+
gtk_widget_get_allocated_baseline((GtkWidget*)(CHANDLE) widget);
146+
}
184 Bytes
Binary file not shown.

org.eclipse.wb.os.linux/src/org/eclipse/wb/internal/os/linux/OSSupportLinux.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,11 @@ private static native void _gtk_window_set_keep_above(long windowHandle,
681681
*/
682682
private static native boolean _gtk_widget_hide(long windowHandle);
683683

684+
/**
685+
* Returns the baseline that has currently been allocated to the widget.
686+
*/
687+
private static native int _gtk_widget_get_allocated_baseline(long widgetHandle);
688+
684689
/**
685690
* <p>Sends one or more expose events to window. The areas in each expose event
686691
* will cover the entire update area for the window (see
@@ -1057,6 +1062,11 @@ public Image getMenuPopupVisualData(Menu menu, int[] bounds) throws Exception {
10571062
return mockupProvider.mockMenuPopupVisualData(menu, bounds);
10581063
}
10591064

1065+
@Override
1066+
public int getBaseline(Control widget) {
1067+
return _gtk_widget_get_allocated_baseline(findHandleValue(widget));
1068+
}
1069+
10601070
@Override
10611071
protected Image createImage0(long imageHandle) throws Exception {
10621072
return (Image) ReflectionUtils.invokeMethod2(
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2011, 2025 Google, Inc. and others.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License 2.0 which is available at
6+
* https://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*
10+
* Contributors:
11+
* Google, Inc. - initial API and implementation
12+
*******************************************************************************/
13+
package org.eclipse.wb.internal.os.linux;
14+
15+
import org.eclipse.wb.internal.core.utils.reflect.ReflectionUtils;
16+
17+
import org.eclipse.core.runtime.IStatus;
18+
import org.eclipse.core.runtime.Plugin;
19+
import org.eclipse.core.runtime.Status;
20+
import org.eclipse.swt.graphics.Image;
21+
import org.eclipse.swt.widgets.Display;
22+
import org.eclipse.ui.plugin.AbstractUIPlugin;
23+
24+
import org.apache.commons.io.IOUtils;
25+
import org.osgi.framework.BundleContext;
26+
27+
import java.io.InputStream;
28+
import java.net.URL;
29+
import java.util.HashMap;
30+
import java.util.Map;
31+
32+
/**
33+
* The activator class controls the plug-in life cycle.
34+
*
35+
* @author mitin_aa
36+
* @coverage os.linux
37+
*/
38+
public class Activator extends AbstractUIPlugin {
39+
public static final String PLUGIN_ID = "org.eclipse.wb.os.linux";
40+
//
41+
private static Activator m_plugin;
42+
43+
////////////////////////////////////////////////////////////////////////////
44+
//
45+
// Life cycle
46+
//
47+
////////////////////////////////////////////////////////////////////////////
48+
@Override
49+
public void start(BundleContext context) throws Exception {
50+
super.start(context);
51+
m_plugin = this;
52+
}
53+
54+
@Override
55+
public void stop(BundleContext context) throws Exception {
56+
m_plugin = null;
57+
super.stop(context);
58+
}
59+
60+
/**
61+
* Returns the shared instance.
62+
*/
63+
public static Activator getDefault() {
64+
return m_plugin;
65+
}
66+
67+
public static void logError(String text, Throwable error) {
68+
getDefault().getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, text, error));
69+
}
70+
71+
////////////////////////////////////////////////////////////////////////////
72+
//
73+
// Files
74+
//
75+
////////////////////////////////////////////////////////////////////////////
76+
/**
77+
* @return the {@link InputStream} for file from plugin directory.
78+
*/
79+
public static InputStream getFile(String path) {
80+
try {
81+
URL url = new URL(getInstallURL(), path);
82+
return url.openStream();
83+
} catch (Throwable e) {
84+
throw ReflectionUtils.propagate(e);
85+
}
86+
}
87+
88+
/**
89+
* @return the install {@link URL} for this {@link Plugin}.
90+
*/
91+
public static URL getInstallURL() {
92+
return getInstallUrl(getDefault());
93+
}
94+
95+
/**
96+
* @return the install {@link URL} for given {@link Plugin}.
97+
*/
98+
private static URL getInstallUrl(Plugin plugin) {
99+
return plugin.getBundle().getEntry("/");
100+
}
101+
102+
////////////////////////////////////////////////////////////////////////////
103+
//
104+
// Images
105+
//
106+
////////////////////////////////////////////////////////////////////////////
107+
private static final Map<String, Image> m_nameToIconMap = new HashMap<>();
108+
109+
/**
110+
* @return the {@link Image} from "icons" directory.
111+
*/
112+
public static Image getImage(String path) {
113+
Image image = m_nameToIconMap.get(path);
114+
if (image == null) {
115+
InputStream is = getFile("icons/" + path);
116+
try {
117+
image = new Image(Display.getCurrent(), is);
118+
m_nameToIconMap.put(path, image);
119+
} finally {
120+
IOUtils.closeQuietly(is);
121+
}
122+
}
123+
return image;
124+
}
125+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Patrick Ziegler and others.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License 2.0 which is available at
6+
* https://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*
10+
* Contributors:
11+
* Patrick Ziegler - initial API and implementation
12+
*******************************************************************************/
13+
package org.eclipse.wb.internal.os.linux;
14+
15+
import org.eclipse.wb.internal.os.linux.cairo.CairoContext;
16+
import org.eclipse.wb.internal.os.linux.cairo.CairoRegion;
17+
18+
import java.lang.foreign.Arena;
19+
import java.lang.foreign.FunctionDescriptor;
20+
import java.lang.foreign.MemorySegment;
21+
import java.lang.foreign.SymbolLookup;
22+
import java.lang.foreign.ValueLayout;
23+
import java.lang.invoke.MethodHandle;
24+
25+
/**
26+
* The GDK toolkit
27+
*/
28+
public class GDK extends Native {
29+
protected static final SymbolLookup GDK;
30+
31+
static {
32+
if (isGtk4()) {
33+
GDK = SymbolLookup.libraryLookup("libgdk-4.so.0", Arena.ofAuto());
34+
} else {
35+
GDK = SymbolLookup.libraryLookup("libgdk-3.so.0", Arena.ofAuto());
36+
}
37+
}
38+
39+
private static class InstanceHolder {
40+
private static final MethodHandle gdk_cairo_region = createHandle(GDK, "gdk_cairo_region",
41+
FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.ADDRESS));
42+
}
43+
44+
/**
45+
* Adds the given region to the current path of {@code cr}.
46+
*
47+
* @param cr A cairo context.
48+
* @param region A {@code cairo_region_t}.
49+
*/
50+
public static void gdk_cairo_region(CairoContext cr, CairoRegion region) {
51+
MemorySegment segment1 = MemorySegment.ofAddress(cr.handle());
52+
MemorySegment segment2 = MemorySegment.ofAddress(region.handle());
53+
runSafe(() -> InstanceHolder.gdk_cairo_region.invoke(segment1, segment2));
54+
}
55+
}

0 commit comments

Comments
 (0)