Skip to content

Commit 9e20eac

Browse files
committed
PoC - Use Foreign Function & Memory API for GTK3 OS Support
1 parent 419c6fe commit 9e20eac

File tree

29 files changed

+1705
-6
lines changed

29 files changed

+1705
-6
lines changed

.mvn/extensions.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
<extension>
44
<groupId>org.eclipse.tycho</groupId>
55
<artifactId>tycho-build</artifactId>
6-
<version>${tycho.version}</version>
6+
<version>5.0.0</version>
77
</extension>
88
</extensions>

.mvn/maven.config

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
-Dtycho.version=5.0.0
21
-Djdk.xml.totalEntitySizeLimit=1000000
32
-Djdk.xml.maxGeneralEntitySizeLimit=1000000

org.eclipse.wb.core.java/src/org/eclipse/wb/internal/core/editor/multi/DesignerEditorContributor.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2011 Google, Inc.
2+
* Copyright (c) 2011, 2025 Google, Inc. and others
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License 2.0 which is available at
@@ -31,8 +31,8 @@ public class DesignerEditorContributor extends CompilationUnitEditorActionContri
3131
@Override
3232
public void setActiveEditor(IEditorPart part) {
3333
super.setActiveEditor(part);
34-
if (part != null) {
35-
((DesignerEditor) part).activated();
34+
if (part instanceof DesignerEditor designerPart) {
35+
designerPart.activated();
3636
}
3737
}
3838
}

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
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: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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+
public class GDK extends Native {
26+
protected static final SymbolLookup GDK;
27+
28+
static {
29+
if (isGtk4()) {
30+
GDK = SymbolLookup.libraryLookup("libgdk-4.so.0", Arena.ofAuto());
31+
} else {
32+
GDK = SymbolLookup.libraryLookup("libgdk-3.so.0", Arena.ofAuto());
33+
}
34+
}
35+
36+
private static class InstanceHolder {
37+
private static final MethodHandle gdk_cairo_region = createHandle(GDK, "gdk_cairo_region",
38+
FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.ADDRESS));
39+
}
40+
41+
public static void gdk_cairo_region(CairoContext cr, CairoRegion region) {
42+
MemorySegment segment1 = MemorySegment.ofAddress(cr.handle());
43+
MemorySegment segment2 = MemorySegment.ofAddress(region.handle());
44+
runSafe(() -> InstanceHolder.gdk_cairo_region.invoke(segment1, segment2));
45+
}
46+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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 java.lang.foreign.Arena;
16+
import java.lang.foreign.FunctionDescriptor;
17+
import java.lang.foreign.MemorySegment;
18+
import java.lang.foreign.SymbolLookup;
19+
import java.lang.foreign.ValueLayout;
20+
import java.lang.invoke.MethodHandle;
21+
22+
public abstract class GTK extends Native {
23+
protected static final SymbolLookup GTK;
24+
25+
static {
26+
if (isGtk4()) {
27+
GTK = SymbolLookup.libraryLookup("libgtk-4.so.0", Arena.ofAuto());
28+
} else {
29+
GTK = SymbolLookup.libraryLookup("libgtk-3.so.0", Arena.ofAuto());
30+
}
31+
}
32+
33+
private static class InstanceHolder {
34+
private static final MethodHandle gtk_get_major_version = createHandle(GTK, "gtk_get_major_version",
35+
FunctionDescriptor.of(ValueLayout.JAVA_INT));
36+
37+
private static final MethodHandle gtk_get_minor_version = createHandle(GTK, "gtk_get_minor_version",
38+
FunctionDescriptor.of(ValueLayout.JAVA_INT));
39+
40+
private static final MethodHandle gtk_get_micro_version = createHandle(GTK, "gtk_get_micro_version",
41+
FunctionDescriptor.of(ValueLayout.JAVA_INT));
42+
43+
private static final MethodHandle gtk_widget_get_allocation = createHandle(GTK, "gtk_widget_get_allocation",
44+
FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.ADDRESS));
45+
46+
private static final MethodHandle gtk_widget_hide = createHandle(GTK, "gtk_widget_hide",
47+
FunctionDescriptor.ofVoid(ValueLayout.ADDRESS));
48+
}
49+
50+
/**
51+
* @return The major version number of the GTK+ library.
52+
*/
53+
public static int gtk_get_major_version() {
54+
return (int) callSafe(() -> InstanceHolder.gtk_get_major_version.invoke());
55+
}
56+
57+
/**
58+
* @return The minor version number of the GTK+ library.
59+
*/
60+
public static int gtk_get_minor_version() {
61+
return (int) callSafe(() -> InstanceHolder.gtk_get_minor_version.invoke());
62+
}
63+
64+
/**
65+
* @return The micro version number of the GTK+ library.
66+
*/
67+
public static int gtk_get_micro_version() {
68+
return (int) callSafe(() -> InstanceHolder.gtk_get_micro_version.invoke());
69+
}
70+
71+
/**
72+
* Retrieves the widget’s allocation.
73+
*
74+
* Note, when implementing a {@code GtkContainer}: a widget’s allocation will be
75+
* its “adjusted” allocation, that is, the widget’s parent container typically
76+
* calls {@code gtk_widget_size_allocate()} with an allocation, and that
77+
* allocation is then adjusted (to handle margin and alignment for example)
78+
* before assignment to the widget.
79+
*
80+
* {@code gtk_widget_get_allocation()} returns the adjusted allocation that was
81+
* actually assigned to the widget. The adjusted allocation is guaranteed to be
82+
* completely contained within the {@code gtk_widget_size_allocate()}
83+
* allocation, however.
84+
*
85+
* So a {@code GtkContainer} is guaranteed that its children stay inside the
86+
* assigned bounds, but not that they have exactly the bounds the container
87+
* assigned. There is no way to get the original allocation assigned by
88+
* {@code gtk_widget_size_allocate()}, since it isn’t stored; if a container
89+
* implementation needs that information it will have to track it itself.
90+
*/
91+
public static void gtk_widget_get_allocation(GtkWidget widget, GtkAllocation allocation) {
92+
MemorySegment segment = MemorySegment.ofAddress(widget.handle());
93+
runSafe(() -> InstanceHolder.gtk_widget_get_allocation.invoke(segment, allocation.segment()));
94+
}
95+
96+
/**
97+
* Reverses the effects of {@code gtk_widget_show()}, causing the {@code widget}
98+
* to be hidden (invisible to the user).
99+
*/
100+
public static void gtk_widget_hide(GtkWidget widget) {
101+
MemorySegment segment = MemorySegment.ofAddress(widget.handle());
102+
runSafe(() -> InstanceHolder.gtk_widget_hide.invoke(segment));
103+
}
104+
}

0 commit comments

Comments
 (0)