Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;

////////////////////////////////////////////////////////////////////////////
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -148,7 +149,30 @@ private void makeShots0(final Shell shell) {
* model. Can be <code>null</code>.
* @return the GdkPixmap* or cairo_surface_t* of {@link Shell}.
*/
protected abstract Image makeShot(Shell shell, BiConsumer<GtkWidget, Image> callback);
protected final Image makeShot(Shell shell, BiConsumer<GtkWidget, Image> callback) {
return traverse(shell, callback);
}

private Image traverse(Widget widget, BiConsumer<GtkWidget, Image> 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<GtkWidget, Image> callback);

private boolean bindImage(final Control control, final Image image) {
if (control.getData(OSSupport.WBP_NEED_IMAGE) != null && control.getData(OSSupport.WBP_IMAGE) == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -40,29 +36,6 @@
public class GTK3ScreenshotMaker extends ScreenshotMaker {

@Override
protected Image makeShot(Shell shell, BiConsumer<GtkWidget, Image> callback) {
return traverse(shell, callback);
}

private Image traverse(Widget widget, BiConsumer<GtkWidget, Image> 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<GtkWidget, Image> callback) {
GdkWindow window = GTK3.gtk_widget_get_window(widget);
if (!GDK3.gdk_window_is_visible(window)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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 <a href=
* "https://docs.gtk.org/gtk4/vfunc.Widget.snapshot.html">Gtk.WidgetClass.snapshot</a>.
*
* For pointer events, see <a href=
* "https://docs.gtk.org/gtk4/method.Widget.contains.html">gtk_widget_contains()</a>.
*
* To learn more about widget sizes, see the coordinate system <a
* href="https://docs.gtk.org/gtk4/coordinates.html" overview</a>.
*
* @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 <a href=
* "https://docs.gtk.org/gtk4/vfunc.Widget.snapshot.html">Gtk.WidgetClass.snapshot</a>.
*
* For pointer events, see <a href=
* "https://docs.gtk.org/gtk4/method.Widget.contains.html">gtk_widget_contains()</a>.
*
* To learn more about widget sizes, see the coordinate system <a
* href="https://docs.gtk.org/gtk4/coordinates.html" overview</a>.
*
* @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));
}
}
Loading
Loading