diff --git a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/GDK.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/GDK.java
index f0eba986f..59017fb8a 100644
--- a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/GDK.java
+++ b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/GDK.java
@@ -29,7 +29,7 @@ public class GDK extends Native {
static {
if (isGtk4()) {
- GDK = SymbolLookup.libraryLookup("libgdk-4.so.0", Arena.ofAuto());
+ GDK = GTK.GTK;
} else {
GDK = SymbolLookup.libraryLookup("libgdk-3.so.0", Arena.ofAuto());
}
diff --git a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/GObject.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/GObject.java
new file mode 100644
index 000000000..0912d5f7a
--- /dev/null
+++ b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/GObject.java
@@ -0,0 +1,22 @@
+package org.eclipse.wb.internal.os.linux;
+
+import java.lang.foreign.MemorySegment;
+
+/**
+ * The base type system and object class
+ */
+public class GObject {
+ private final MemorySegment segment;
+
+ protected GObject(long handle) {
+ this(handle == 0L ? MemorySegment.NULL : MemorySegment.ofAddress(handle));
+ }
+
+ protected GObject(MemorySegment segment) {
+ this.segment = segment;
+ }
+
+ public MemorySegment segment() {
+ return segment;
+ }
+}
diff --git a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/GTK.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/GTK.java
index b888a7c70..76239f783 100644
--- a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/GTK.java
+++ b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/GTK.java
@@ -26,7 +26,7 @@ public abstract class GTK extends Native {
static {
if (isGtk4()) {
- GTK = SymbolLookup.libraryLookup("libgtk-4.so.0", Arena.ofAuto());
+ GTK = SymbolLookup.libraryLookup("libgtk-4.so.1", Arena.ofAuto());
} else {
GTK = SymbolLookup.libraryLookup("libgtk-3.so.0", Arena.ofAuto());
}
diff --git a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/GtkWidget.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/GtkWidget.java
index fac3dd0d5..e70b9e818 100644
--- a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/GtkWidget.java
+++ b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/GtkWidget.java
@@ -16,33 +16,27 @@
import org.eclipse.swt.widgets.Widget;
-import java.lang.foreign.MemorySegment;
import java.util.Objects;
/**
* GtkWidget is the base class all widgets in GTK+ derive from. It manages the
* widget lifecycle, states and style.
*/
-public class GtkWidget {
- private final MemorySegment segment;
+public class GtkWidget extends GObject {
protected GtkWidget(long handle) {
- segment = handle == 0L ? MemorySegment.NULL : MemorySegment.ofAddress(handle);
- }
-
- public MemorySegment segment() {
- return segment;
+ super(handle);
}
@Override
public int hashCode() {
- return Objects.hash(segment.address());
+ return Objects.hash(segment().address());
}
@Override
public boolean equals(Object o) {
if (o instanceof GtkWidget other) {
- return segment.address() == other.segment.address();
+ return segment().address() == other.segment().address();
}
return false;
}
diff --git a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/OS.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/OS.java
new file mode 100644
index 000000000..a17a9c4f1
--- /dev/null
+++ b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/OS.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2026 Patrick Ziegler and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * https://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Patrick Ziegler - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.os.linux;
+
+import java.lang.foreign.Arena;
+import java.lang.foreign.FunctionDescriptor;
+import java.lang.foreign.SymbolLookup;
+import java.lang.foreign.ValueLayout;
+import java.lang.invoke.MethodHandle;
+
+/**
+ * This class contains native functions for various libraries.
+ */
+public class OS extends Native {
+
+ protected static final SymbolLookup GOBJECT;
+
+ static {
+ GOBJECT = SymbolLookup.libraryLookup("libgobject-2.0.so.0", Arena.ofAuto());
+ }
+
+ private static class InstanceHolder {
+ private static final MethodHandle g_object_unref = createHandle(GOBJECT, "g_object_unref",
+ FunctionDescriptor.ofVoid(ValueLayout.ADDRESS));
+ }
+
+ public static void g_object_unref(GObject object) {
+ runSafe(() -> InstanceHolder.g_object_unref.invoke(object.segment()));
+ }
+}
diff --git a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/OSSupportLinux.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/OSSupportLinux.java
index 5501b4862..4eb854f5c 100644
--- a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/OSSupportLinux.java
+++ b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/OSSupportLinux.java
@@ -17,6 +17,7 @@
import org.eclipse.wb.internal.os.linux.gtk3.GTK3;
import org.eclipse.wb.internal.os.linux.gtk3.GTK3ScreenshotMaker;
import org.eclipse.wb.internal.os.linux.gtk3.GtkWindow;
+import org.eclipse.wb.internal.os.linux.gtk4.GTK4ScreenshotMaker;
import org.eclipse.wb.internal.swt.VisualDataMockupProvider;
import org.eclipse.wb.os.OSSupport;
@@ -44,7 +45,7 @@
public final class OSSupportLinux extends OSSupport {
private static Version SWT_VERSION_3_126 = new Version(3, 126, 0);
private final VisualDataMockupProvider mockupProvider = new VisualDataMockupProvider();
- private final ScreenshotMaker screenshotMaker = new GTK3ScreenshotMaker();
+ private final ScreenshotMaker screenshotMaker = GTK.isGtk4() ? new GTK4ScreenshotMaker() : new GTK3ScreenshotMaker();
private Shell m_eclipseShell;
////////////////////////////////////////////////////////////////////////////
diff --git a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/ScreenshotMaker.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/ScreenshotMaker.java
index 9ae5e4ded..3634b1f29 100644
--- a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/ScreenshotMaker.java
+++ b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/ScreenshotMaker.java
@@ -26,6 +26,7 @@
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Widget;
import java.lang.foreign.MemorySegment;
import java.util.HashMap;
@@ -148,7 +149,30 @@ private void makeShots0(final Shell shell) {
* model. Can be null.
* @return the GdkPixmap* or cairo_surface_t* of {@link Shell}.
*/
- protected abstract Image makeShot(Shell shell, BiConsumer callback);
+ protected final Image makeShot(Shell shell, BiConsumer callback) {
+ return traverse(shell, callback);
+ }
+
+ private Image traverse(Widget widget, BiConsumer callback) {
+ Image image = getImageSurface(GtkWidget.from(widget), callback);
+ if (image == null) {
+ return null;
+ }
+ if (widget instanceof Composite composite) {
+ for (Control childWidget : composite.getChildren()) {
+ Image childImage = traverse(childWidget, callback);
+ if (childImage == null) {
+ continue;
+ }
+ if (callback == null) {
+ childImage.dispose();
+ }
+ }
+ }
+ return image;
+ }
+
+ protected abstract Image getImageSurface(GtkWidget widget, BiConsumer callback);
private boolean bindImage(final Control control, final Image image) {
if (control.getData(OSSupport.WBP_NEED_IMAGE) != null && control.getData(OSSupport.WBP_IMAGE) == null) {
diff --git a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/cairo/CairoContext.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/cairo/CairoContext.java
index 6d35703dc..84e03daa0 100644
--- a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/cairo/CairoContext.java
+++ b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/cairo/CairoContext.java
@@ -12,6 +12,10 @@
*******************************************************************************/
package org.eclipse.wb.internal.os.linux.cairo;
+import org.eclipse.wb.internal.core.utils.reflect.ReflectionUtils;
+
+import org.eclipse.swt.graphics.GC;
+
import java.lang.foreign.MemorySegment;
/**
@@ -22,5 +26,18 @@
* and all drawing with cairo is always done to a cairo_t object.
*/
public record CairoContext(MemorySegment segment) {
+ /**
+ * Creates a new CairoContext instance associated with the GC object.
+ *
+ * @param gc The context to paint on.
+ * @return The Cairo context backed by the given GC.
+ */
+ public static CairoContext from(GC gc) {
+ long handle = getHandleValue(gc, "handle");
+ return new CairoContext(MemorySegment.ofAddress(handle));
+ }
+ private static long getHandleValue(GC gc, String fieldName) {
+ return ReflectionUtils.getFieldLong(gc, fieldName);
+ }
}
diff --git a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk3/GTK3ScreenshotMaker.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk3/GTK3ScreenshotMaker.java
index 615532eab..fbb35b445 100644
--- a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk3/GTK3ScreenshotMaker.java
+++ b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk3/GTK3ScreenshotMaker.java
@@ -27,10 +27,6 @@
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Widget;
import java.util.function.BiConsumer;
@@ -40,29 +36,6 @@
public class GTK3ScreenshotMaker extends ScreenshotMaker {
@Override
- protected Image makeShot(Shell shell, BiConsumer callback) {
- return traverse(shell, callback);
- }
-
- private Image traverse(Widget widget, BiConsumer callback) {
- Image image = getImageSurface(GtkWidget.from(widget), callback);
- if (image == null) {
- return null;
- }
- if (widget instanceof Composite composite) {
- for (Control childWidget : composite.getChildren()) {
- Image childImage = traverse(childWidget, callback);
- if (childImage == null) {
- continue;
- }
- if (callback == null) {
- childImage.dispose();
- }
- }
- }
- return image;
- }
-
protected Image getImageSurface(GtkWidget widget, BiConsumer callback) {
GdkWindow window = GTK3.gtk_widget_get_window(widget);
if (!GDK3.gdk_window_is_visible(window)) {
diff --git a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk3/GdkWindow.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk3/GdkWindow.java
index 38392d28d..2a8fad4e5 100644
--- a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk3/GdkWindow.java
+++ b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk3/GdkWindow.java
@@ -12,11 +12,15 @@
*******************************************************************************/
package org.eclipse.wb.internal.os.linux.gtk3;
+import org.eclipse.wb.internal.os.linux.GObject;
+
import java.lang.foreign.MemorySegment;
/**
* A GDK window.
*/
-public record GdkWindow(MemorySegment segment) {
-
+public class GdkWindow extends GObject {
+ protected GdkWindow(MemorySegment segment) {
+ super(segment);
+ }
}
diff --git a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk4/GTK4.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk4/GTK4.java
new file mode 100644
index 000000000..4d51f33a2
--- /dev/null
+++ b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk4/GTK4.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * Copyright (c) 2026 Patrick Ziegler and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * https://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Patrick Ziegler - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.os.linux.gtk4;
+
+import org.eclipse.wb.internal.os.linux.GTK;
+import org.eclipse.wb.internal.os.linux.GtkWidget;
+import org.eclipse.wb.internal.os.linux.cairo.CairoContext;
+
+import java.lang.foreign.FunctionDescriptor;
+import java.lang.foreign.MemorySegment;
+import java.lang.foreign.ValueLayout;
+import java.lang.invoke.MethodHandle;
+
+/**
+ * The GTK toolkit compatible with GTK 4.x
+ */
+public class GTK4 extends GTK {
+
+ private static class InstanceHolder {
+ private static final MethodHandle gtk_widget_get_height = createHandle(GTK, "gtk_widget_get_height",
+ FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS));
+
+ private static final MethodHandle gtk_widget_get_width = createHandle(GTK, "gtk_widget_get_width",
+ FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS));
+
+ private static final MethodHandle gtk_widget_paintable_new = createHandle(GTK, "gtk_widget_paintable_new",
+ FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS));
+
+ private static final MethodHandle gtk_snapshot_new = createHandle(GTK, "gtk_snapshot_new",
+ FunctionDescriptor.of(ValueLayout.ADDRESS));
+
+ private static final MethodHandle gtk_snapshot_free_to_node = createHandle(GTK, "gtk_snapshot_free_to_node",
+ FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS));
+
+ private static final MethodHandle gsk_render_node_draw = createHandle(GTK, "gsk_render_node_draw",
+ FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.ADDRESS));
+
+ private static final MethodHandle gsk_render_node_unref = createHandle(GTK, "gsk_render_node_unref",
+ FunctionDescriptor.ofVoid(ValueLayout.ADDRESS));
+
+ private static final MethodHandle gdk_paintable_snapshot = createHandle(GTK, "gdk_paintable_snapshot",
+ FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.JAVA_DOUBLE, ValueLayout.JAVA_DOUBLE));
+ }
+
+ /**
+ * Returns the content height of the widget. This function returns the height
+ * passed to its size-allocate implementation, which is the height you should be
+ * using in Gtk.WidgetClass.snapshot.
+ *
+ * For pointer events, see gtk_widget_contains().
+ *
+ * To learn more about widget sizes, see the coordinate system .
+ *
+ * @param widget
+ * @return The height of {@code widget}.
+ */
+ public static int gtk_widget_get_height(GtkWidget widget) {
+ return (int) callSafe(() -> InstanceHolder.gtk_widget_get_height.invoke(widget.segment()));
+ }
+
+ /**
+ * Returns the content width of the widget. This function returns the width
+ * passed to its size-allocate implementation, which is the width you should be
+ * using in Gtk.WidgetClass.snapshot.
+ *
+ * For pointer events, see gtk_widget_contains().
+ *
+ * To learn more about widget sizes, see the coordinate system .
+ *
+ * @param widget
+ * @return The width of {@code widget}.
+ */
+ public static int gtk_widget_get_width(GtkWidget widget) {
+ return (int) callSafe(() -> InstanceHolder.gtk_widget_get_width.invoke(widget.segment()));
+ }
+
+ public static GtkPaintable gtk_widget_paintable_new(GtkWidget widget) {
+ MemorySegment segment = (MemorySegment) callSafe(() -> InstanceHolder.gtk_widget_paintable_new.invoke(widget.segment()));
+ return new GtkPaintable(segment);
+ }
+
+ public static GtkSnapshot gtk_snapshot_new() {
+ MemorySegment segment = (MemorySegment) callSafe(() -> InstanceHolder.gtk_snapshot_new.invoke());
+ return new GtkSnapshot(segment);
+ }
+
+ public static GskRenderNode gtk_snapshot_free_to_node(GtkSnapshot snapshot) {
+ MemorySegment segment = (MemorySegment) callSafe(() -> InstanceHolder.gtk_snapshot_free_to_node.invoke(snapshot.segment()));
+ return new GskRenderNode(segment);
+ }
+
+ public static void gsk_render_node_draw(GskRenderNode node, CairoContext cr) {
+ runSafe(() -> InstanceHolder.gsk_render_node_draw.invoke(node.segment(), cr.segment()));
+ }
+
+ public static void gsk_render_node_unref(GskRenderNode node) {
+ runSafe(() -> InstanceHolder.gsk_render_node_unref.invoke(node.segment()));
+ }
+
+ public static void gdk_paintable_snapshot(GdkPaintable paintable, GdkSnapshot snapshot, double width, double height) {
+ runSafe(() -> InstanceHolder.gdk_paintable_snapshot.invoke(paintable.segment(), snapshot.segment(), width, height));
+ }
+}
diff --git a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk4/GTK4ScreenshotMaker.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk4/GTK4ScreenshotMaker.java
new file mode 100644
index 000000000..036706f52
--- /dev/null
+++ b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk4/GTK4ScreenshotMaker.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2026 Patrick Ziegler and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * https://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Patrick Ziegler - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.os.linux.gtk4;
+
+import org.eclipse.wb.internal.os.linux.GtkWidget;
+import org.eclipse.wb.internal.os.linux.OS;
+import org.eclipse.wb.internal.os.linux.ScreenshotMaker;
+import org.eclipse.wb.internal.os.linux.cairo.CairoContext;
+
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Display;
+
+import java.lang.foreign.MemorySegment;
+import java.util.function.BiConsumer;
+
+public class GTK4ScreenshotMaker extends ScreenshotMaker {
+
+ @Override
+ protected Image getImageSurface(GtkWidget widget, BiConsumer callback) {
+ GtkPaintable widgetPaintable = GTK4.gtk_widget_paintable_new(widget);
+ if (MemorySegment.NULL.equals(widgetPaintable.segment())) {
+ return null;
+ }
+
+ int width = GTK4.gtk_widget_get_width(widget);
+ int height = GTK4.gtk_widget_get_height(widget);
+
+ Image image = new Image(Display.getCurrent(), width, height);
+
+ GtkSnapshot snapshot = GTK4.gtk_snapshot_new();
+ if (MemorySegment.NULL.equals(snapshot.segment())) {
+ return image;
+ }
+
+ GC gc = new GC(image);
+
+ try {
+ GTK4.gdk_paintable_snapshot(widgetPaintable, snapshot, width, height);
+
+ GskRenderNode renderNode = GTK4.gtk_snapshot_free_to_node(snapshot);
+ snapshot = null; // freed by gtk_snapshot_free_to_node
+
+ if (MemorySegment.NULL.equals(renderNode.segment())) {
+ return image;
+ }
+
+ try {
+ GTK4.gsk_render_node_draw(renderNode, CairoContext.from(gc));
+ GTK4.gsk_render_node_unref(renderNode);
+ } finally {
+ gc.dispose();
+ }
+
+ return image;
+ } finally {
+ gc.dispose();
+
+ callback.accept(widget, image);
+ if (snapshot != null) {
+ OS.g_object_unref(snapshot);
+ }
+
+ OS.g_object_unref(widgetPaintable);
+ }
+ }
+}
diff --git a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk4/GdkPaintable.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk4/GdkPaintable.java
new file mode 100644
index 000000000..def76143f
--- /dev/null
+++ b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk4/GdkPaintable.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2026 Patrick Ziegler and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * https://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Patrick Ziegler - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.os.linux.gtk4;
+
+import org.eclipse.wb.internal.os.linux.GObject;
+
+import java.lang.foreign.MemorySegment;
+
+public class GdkPaintable extends GObject {
+ public GdkPaintable(MemorySegment segment) {
+ super(segment);
+ }
+}
diff --git a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk4/GdkSnapshot.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk4/GdkSnapshot.java
new file mode 100644
index 000000000..4adb3a0cb
--- /dev/null
+++ b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk4/GdkSnapshot.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2026 Patrick Ziegler and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * https://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Patrick Ziegler - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.os.linux.gtk4;
+
+import org.eclipse.wb.internal.os.linux.GObject;
+
+import java.lang.foreign.MemorySegment;
+
+public class GdkSnapshot extends GObject {
+ public GdkSnapshot(MemorySegment segment) {
+ super(segment);
+ }
+}
diff --git a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk4/GskRenderNode.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk4/GskRenderNode.java
new file mode 100644
index 000000000..02a8f3508
--- /dev/null
+++ b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk4/GskRenderNode.java
@@ -0,0 +1,19 @@
+/*******************************************************************************
+ * Copyright (c) 2026 Patrick Ziegler and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * https://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Patrick Ziegler - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.os.linux.gtk4;
+
+import java.lang.foreign.MemorySegment;
+
+public record GskRenderNode(MemorySegment segment) {
+
+}
diff --git a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk4/GtkPaintable.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk4/GtkPaintable.java
new file mode 100644
index 000000000..6daf01812
--- /dev/null
+++ b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk4/GtkPaintable.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2026 Patrick Ziegler and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * https://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Patrick Ziegler - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.os.linux.gtk4;
+
+import java.lang.foreign.MemorySegment;
+
+/**
+ * A {@code GdkPaintable} that displays the contents of a widget.
+ *
+ * {@code GtkWidgetPaintable} will also take care of the widget not being in a
+ * state where it can be drawn (like when it isn’t shown) and just draw nothing
+ * or where it does not have a size (like when it is hidden) and report no size
+ * in that case.
+ *
+ * Of course, {@code GtkWidgetPaintable} allows you to monitor widgets for size
+ * changes by emitting the GdkPaintable::invalidate-size
+ * signal whenever the size of the widget changes as well as for visual changes
+ * by emitting the GdkPaintable::invalidate-contents
+ * signal whenever the widget changes.
+ *
+ * You can use a {@code GtkWidgetPaintable} everywhere a {@code GdkPaintable} is
+ * allowed, including using it on a {@code GtkPicture} (or one of its parents)
+ * that it was set on itself via gtk_picture_set_paintable(). The paintable will
+ * take care of recursion when this happens. If you do this however, ensure that
+ * the GtkPicture:can-shrink property is
+ * set to {@code TRUE} or you might end up with an infinitely growing widget.
+ *
+ */
+public class GtkPaintable extends GdkPaintable {
+ public GtkPaintable(MemorySegment segment) {
+ super(segment);
+ }
+}
diff --git a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk4/GtkSnapshot.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk4/GtkSnapshot.java
new file mode 100644
index 000000000..66112130d
--- /dev/null
+++ b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk4/GtkSnapshot.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2026 Patrick Ziegler and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * https://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Patrick Ziegler - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.os.linux.gtk4;
+
+import java.lang.foreign.MemorySegment;
+
+public class GtkSnapshot extends GdkSnapshot {
+ public GtkSnapshot(MemorySegment segment) {
+ super(segment);
+ }
+}