diff --git a/org.eclipse.wb.doc.user/html-src/whatsnew/v125.asciidoc b/org.eclipse.wb.doc.user/html-src/whatsnew/v125.asciidoc new file mode 100644 index 000000000..d6d568add --- /dev/null +++ b/org.eclipse.wb.doc.user/html-src/whatsnew/v125.asciidoc @@ -0,0 +1,14 @@ +ifdef::env-github[] +:imagesdir: ../../html/whatsnew +endif::[] + += What's New - v1.25.0 + +== General + +- [Linux] Wayland Support for SWT Designer + +The screenshot mechanism for GTK3 has been adapted to support both X11 and Wayland. _Note_ This support is only +available with Java 24 or higher. + +What's new - xref:v124.adoc[*v1.24.0*] \ No newline at end of file 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.java similarity index 57% rename from org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/cairo/CairoContext.java rename to org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/Cairo.java index 6d35703dc..531dc2dec 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.java @@ -10,7 +10,11 @@ * Contributors: * Patrick Ziegler - initial API and implementation *******************************************************************************/ -package org.eclipse.wb.internal.os.linux.cairo; +package org.eclipse.wb.internal.os.linux; + +import org.eclipse.wb.internal.core.utils.reflect.ReflectionUtils; + +import org.eclipse.swt.graphics.GC; import java.lang.foreign.MemorySegment; @@ -21,6 +25,19 @@ * Cairo contexts, as {@code cairo_t} objects are named, are central to cairo * and all drawing with cairo is always done to a cairo_t object. */ -public record CairoContext(MemorySegment segment) { +public record Cairo(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 Cairo from(GC gc) { + long handle = getHandleValue(gc, "handle"); + return new Cairo(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/GDK.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/GDK.java deleted file mode 100644 index f0eba986f..000000000 --- a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/GDK.java +++ /dev/null @@ -1,52 +0,0 @@ -/******************************************************************************* - * 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 org.eclipse.wb.internal.os.linux.cairo.CairoContext; -import org.eclipse.wb.internal.os.linux.cairo.CairoRegion; - -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; - -/** - * The GDK toolkit - */ -public class GDK extends Native { - protected static final SymbolLookup GDK; - - static { - if (isGtk4()) { - GDK = SymbolLookup.libraryLookup("libgdk-4.so.0", Arena.ofAuto()); - } else { - GDK = SymbolLookup.libraryLookup("libgdk-3.so.0", Arena.ofAuto()); - } - } - - private static class InstanceHolder { - private static final MethodHandle gdk_cairo_region = createHandle(GDK, "gdk_cairo_region", - FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.ADDRESS)); - } - - /** - * Adds the given region to the current path of {@code cr}. - * - * @param cr A cairo context. - * @param region A {@code cairo_region_t}. - */ - public static void gdk_cairo_region(CairoContext cr, CairoRegion region) { - runSafe(() -> InstanceHolder.gdk_cairo_region.invoke(cr.segment(), region.segment())); - } -} diff --git a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/cairo/Cairo.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/cairo/Cairo.java deleted file mode 100644 index 4f29f08d1..000000000 --- a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/cairo/Cairo.java +++ /dev/null @@ -1,187 +0,0 @@ -/******************************************************************************* - * 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.cairo; - -import org.eclipse.wb.internal.os.linux.Native; - -import java.lang.foreign.Arena; -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SymbolLookup; -import java.lang.foreign.ValueLayout; -import java.lang.invoke.MethodHandle; - -/** - * The Cairo toolkit. - */ -public class Cairo extends Native { - private static final SymbolLookup CAIRO = SymbolLookup.libraryLookup("libcairo.so.2", Arena.ofAuto()); - - private static final MethodHandle cairo_clip = createHandle(CAIRO, "cairo_clip", - FunctionDescriptor.ofVoid(ValueLayout.ADDRESS)); - - private static final MethodHandle cairo_create = createHandle(CAIRO, "cairo_create", - FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS)); - - private static final MethodHandle cairo_destroy = createHandle(CAIRO, "cairo_destroy", - FunctionDescriptor.ofVoid(ValueLayout.ADDRESS)); - - private static final MethodHandle cairo_image_surface_create = createHandle(CAIRO, "cairo_image_surface_create", - FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.JAVA_INT, ValueLayout.JAVA_INT, ValueLayout.JAVA_INT)); - - private static final MethodHandle cairo_paint = createHandle(CAIRO, "cairo_paint", - FunctionDescriptor.ofVoid(ValueLayout.ADDRESS)); - - private static final MethodHandle cairo_region_destroy = createHandle(CAIRO, "cairo_region_destroy", - FunctionDescriptor.ofVoid(ValueLayout.ADDRESS)); - - private static final MethodHandle cairo_set_operator = createHandle(CAIRO, "cairo_set_operator", - FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.JAVA_INT)); - - private static final MethodHandle cairo_surface_flush = createHandle(CAIRO, "cairo_surface_flush", - FunctionDescriptor.ofVoid(ValueLayout.ADDRESS)); - - /** - * Establishes a new clip region by intersecting the current clip region with - * the current path as it would be filled by {@code cairo_fill()} and according - * to the current fill rule (see {@code cairo_set_fill_rule()}). - * - * After {@code cairo_clip()}, the current path will be cleared from the cairo - * context. - * - * The current clip region affects all drawing operations by effectively masking - * out any changes to the surface that are outside the current clip region. - * - * Calling {@code cairo_clip()} can only make the clip region smaller, never - * larger. But the current clip is part of the graphics state, so a temporary - * restriction of the clip region can be achieved by calling - * {@code cairo_clip()} within a {@code cairo_save()}/{@code cairo_restore()} - * pair. The only other means of increasing the size of the clip region is - * {@code cairo_reset_clip()}. - * - * @param cr a cairo context - */ - public static void cairo_clip(CairoContext cr) { - runSafe(() -> cairo_clip.invoke(cr.segment())); - } - - /** - * Creates a new {@code cairo_t} with all graphics state parameters set to - * default values and with target as a target surface. The target surface should - * be constructed with a backend-specific function such as - * {@code cairo_image_surface_create()} (or any other - * {@code cairo_backend_surface_create()} variant). - * - * This function references {@code target}, so you can immediately call - * {@code cairo_surface_destroy()} on it if you don't need to maintain a - * separate reference to it. - * - * @param target target surface for the context - * @return a newly allocated {@code cairo_t} with a reference count of 1. The - * initial reference count should be released with - * {@code cairo_destroy()} when you are done using the cairo_t. This - * function never returns NULL. If memory cannot be allocated, a special - * {@code cairo_t} object will be returned on which - * {@code cairo_status()} returns {@code CAIRO_STATUS_NO_MEMORY}. If you - * attempt to target a surface which does not support writing (such as - * {@code cairo_mime_surface_t}) then a {@code CAIRO_STATUS_WRITE_ERROR} - * will be raised. You can use this object normally, but no drawing will - * be done. - */ - public static CairoContext cairo_create(CairoSurface target) { - MemorySegment handle = (MemorySegment) callSafe(() -> cairo_create.invoke(target.segment())); - return new CairoContext(handle); - } - - /** - * Decreases the reference count on {@code cr} by one. If the result is zero, - * then {@code cr} and all associated resources are freed. See - * cairo_reference(). - * - * @param cr a {@code cairo_t} - */ - public static void cairo_destroy(CairoContext cr) { - runSafe(() -> cairo_destroy.invoke(cr.segment())); - } - - /** - * Creates an image surface of the specified format and dimensions. Initially - * the surface contents are set to 0. (Specifically, within each pixel, each - * color or alpha channel belonging to format will be 0. The contents of bits - * within a pixel, but not belonging to the given format are undefined). - * - * @param format format of pixels in the surface to create - * @param width width of the surface, in pixels - * @param height height of the surface, in pixels - * @return a pointer to the newly created surface. The caller owns the surface - * and should call {@code cairo_surface_destroy()} when done with it. - *
- * This function always returns a valid pointer, but it will return a - * pointer to a "nil" surface if an error such as out of memory occurs. - * You can use {@code cairo_surface_status()} to check for this. - *
- */ - public static CairoSurface cairo_image_surface_create(CairoFormat format, int width, int height) { - MemorySegment handle = (MemorySegment) callSafe(() -> cairo_image_surface_create.invoke(format.getValue(), width, height)); - return new CairoSurface(handle); - } - - /** - * A drawing operator that paints the current source everywhere within the - * current clip region. - * - * @param cr a cairo context - */ - public static void cairo_paint(CairoContext cr) { - runSafe(() -> cairo_paint.invoke(cr.segment())); - } - - /** - * Destroys a {@code cairo_region_t} object created with - * {@code cairo_region_create()}, {@code cairo_region_copy()}, or or - * {@code cairo_region_create_rectangle()}. - * - * @param region a {@code cairo_region_t} - */ - public static void cairo_region_destroy(CairoRegion region) { - runSafe(() -> cairo_region_destroy.invoke(region.segment())); - } - - /** - * Sets the compositing operator to be used for all drawing operations. See - * {@code cairo_operator_t} for details on the semantics of each available - * compositing operator. - *- * The default operator is {@code CAIRO_OPERATOR_OVER}. - *
- * - * @param cr a {@code cairo_t} - * @param op a compositing operator, specified as a {@code cairo_operator_t} - */ - public static void cairo_set_operator(CairoContext cr, CairoOperator op) { - runSafe(() -> cairo_set_operator.invoke(cr.segment(), op.getValue())); - } - - /** - * Do any pending drawing for the surface and also restore any temporary - * modifications cairo has made to the surface's state. This function must be - * called before switching from drawing on the surface with cairo to drawing on - * it directly with native APIs, or accessing its memory outside of Cairo. If - * the surface doesn't support direct access, then this function does nothing. - * - * @param surface a {@code cairo_surface_t} - */ - public static void cairo_surface_flush(CairoSurface surface) { - runSafe(() -> cairo_surface_flush.invoke(surface.segment())); - } -} diff --git a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/cairo/CairoFormat.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/cairo/CairoFormat.java deleted file mode 100644 index 498202d0f..000000000 --- a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/cairo/CairoFormat.java +++ /dev/null @@ -1,76 +0,0 @@ -/******************************************************************************* - * 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.cairo; - -/** - * {@code cairo_format_t} is used to identify the memory format of image data. - * - * New entries may be added in future versions. - */ -public enum CairoFormat { - /** - * no such format exists or is supported. - */ - CAIRO_FORMAT_INVALID(-1), - /** - * each pixel is a 32-bit quantity, with alpha in the upper 8 bits, then red, - * then green, then blue. The 32-bit quantities are stored native-endian. - * Pre-multiplied alpha is used. (That is, 50% transparent red is 0x80800000, - * not 0x80ff0000.) (Since 1.0) - */ - CAIRO_FORMAT_ARGB32(0), - /** - * each pixel is a 32-bit quantity, with the upper 8 bits unused. Red, Green, - * and Blue are stored in the remaining 24 bits in that order. (Since 1.0) - */ - CAIRO_FORMAT_RGB24(1), - /** - * each pixel is a 8-bit quantity holding an alpha value. (Since 1.0) - */ - CAIRO_FORMAT_A8(2), - /** - * each pixel is a 1-bit quantity holding an alpha value. Pixels are packed - * together into 32-bit quantities. The ordering of the bits matches the - * endianness of the platform. On a big-endian machine, the first pixel is in - * the uppermost bit, on a little-endian machine the first pixel is in the - * least-significant bit. (Since 1.0) - */ - CAIRO_FORMAT_A1(3), - /** - * each pixel is a 16-bit quantity with red in the upper 5 bits, then green in - * the middle 6 bits, and blue in the lower 5 bits. (Since 1.2) - */ - CAIRO_FORMAT_RGB16_565(4), - /** - * like RGB24 but with 10bpc. (Since 1.12) - */ - CAIRO_FORMAT_RGB30(5), - /** - * 3 floats, R, G, B. (Since 1.17.2) - */ - CAIRO_FORMAT_RGB96F(6), - /** - * 4 floats, R, G, B, A. (Since 1.17.2) - */ - CAIRO_FORMAT_RGBA128F(7); - - private final int value; - - private CairoFormat(int value) { - this.value = value; - } - - public int getValue() { - return value; - } -} diff --git a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/cairo/CairoOperator.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/cairo/CairoOperator.java deleted file mode 100644 index d39418002..000000000 --- a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/cairo/CairoOperator.java +++ /dev/null @@ -1,173 +0,0 @@ -/******************************************************************************* - * 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.cairo; - -/** - * {@code CairoOperator} is used to set the compositing operator for all cairo - * drawing operations. - * - * The default operator is {@link #CAIRO_OPERATOR_OVER}. - * - * The operators marked as unbounded modify their destination even outside of - * the mask layer (that is, their effect is not bound by the mask layer). - * However, their effect can still be limited by way of clipping. - * - * To keep things simple, the operator descriptions here document the behavior - * for when both source and destination are either fully transparent or fully - * opaque. The actual implementation works for translucent layers too. For a - * more detailed explanation of the effects of each operator, including the - * mathematical definitions, see https://cairographics.org/operators/. - */ -public enum CairoOperator { - /** - * clear destination layer (bounded) (Since 1.0) - */ - CAIRO_OPERATOR_CLEAR(0), - /** - * replace destination layer (bounded) (Since 1.0) - */ - CAIRO_OPERATOR_SOURCE(1), - /** - * draw source layer on top of destination layer (bounded) (Since 1.0) - */ - CAIRO_OPERATOR_OVER(2), - /** - * draw source where there was destination content (unbounded) (Since 1.0) - */ - CAIRO_OPERATOR_IN(3), - /** - * draw source where there was no destination content (unbounded) (Since 1.0) - */ - CAIRO_OPERATOR_OUT(4), - /** - * draw source on top of destination content and only there (Since 1.0) - */ - CAIRO_OPERATOR_ATOP(5), - /** - * ignore the source (Since 1.0) - */ - CAIRO_OPERATOR_DEST(6), - /** - * draw destination on top of source (Since 1.0) - */ - CAIRO_OPERATOR_DEST_OVER(7), - /** - * leave destination only where there was source content (unbounded) (Since 1.0) - */ - CAIRO_OPERATOR_DEST_IN(8), - /** - * leave destination only where there was no source content (Since 1.0) - */ - CAIRO_OPERATOR_DEST_OUT(9), - /** - * leave destination on top of source content and only there (unbounded) (Since - * 1.0) - */ - CAIRO_OPERATOR_DEST_ATOP(10), - /** - * source and destination are shown where there is only one of them (Since 1.0) - */ - CAIRO_OPERATOR_XOR(11), - /** - * source and destination layers are accumulated (Since 1.0) - */ - CAIRO_OPERATOR_ADD(12), - /** - * like over, but assuming source and dest are disjoint geometries (Since 1.0) - */ - CAIRO_OPERATOR_SATURATE(13), - /** - * source and destination layers are multiplied. This causes the result to be at - * least as dark as the darker inputs. (Since 1.10) - */ - CAIRO_OPERATOR_MULTIPLY(14), - /** - * source and destination are complemented and multiplied. This causes the - * result to be at least as light as the lighter inputs. (Since 1.10) - */ - CAIRO_OPERATOR_SCREEN(15), - /** - * multiplies or screens, depending on the lightness of the destination color. - * (Since 1.10) - */ - CAIRO_OPERATOR_OVERLAY(16), - /** - * replaces the destination with the source if it is darker, otherwise keeps the - * source. (Since 1.10) - */ - CAIRO_OPERATOR_DARKEN(17), - /** - * replaces the destination with the source if it is lighter, otherwise keeps - * the source. (Since 1.10) - */ - CAIRO_OPERATOR_LIGHTEN(18), - /** - * brightens the destination color to reflect the source color. (Since 1.10) - */ - CAIRO_OPERATOR_COLOR_DODGE(19), - /** - * darkens the destination color to reflect the source color. (Since 1.10) - */ - CAIRO_OPERATOR_COLOR_BURN(20), - /** - * Multiplies or screens, dependent on source color. (Since 1.10) - */ - CAIRO_OPERATOR_HARD_LIGHT(21), - /** - * Darkens or lightens, dependent on source color. (Since 1.10) - */ - CAIRO_OPERATOR_SOFT_LIGHT(22), - /** - * Takes the difference of the source and destination color. (Since 1.10) - */ - CAIRO_OPERATOR_DIFFERENCE(23), - /** - * Produces an effect similar to difference, but with lower contrast. (Since - * 1.10) - */ - CAIRO_OPERATOR_EXCLUSION(24), - /** - * Creates a color with the hue of the source and the saturation and luminosity - * of the target. (Since 1.10) - */ - CAIRO_OPERATOR_HSL_HUE(25), - /** - * Creates a color with the saturation of the source and the hue and luminosity - * of the target. Painting with this mode onto a gray area produces no change. - * (Since 1.10) - */ - CAIRO_OPERATOR_HSL_SATURATION(26), - /** - * Creates a color with the hue and saturation of the source and the luminosity - * of the target. This preserves the gray levels of the target and is useful for - * coloring monochrome images or tinting color images. (Since 1.10) - */ - CAIRO_OPERATOR_HSL_COLOR(27), - /** - * Creates a color with the luminosity of the source and the hue and saturation - * of the target. This produces an inverse effect to - * {@link CAIRO_OPERATOR_HSL_COLOR} . (Since 1.10) - */ - CAIRO_OPERATOR_HSL_LUMINOSITY(28); - - private final int value; - - private CairoOperator(int value) { - this.value = value; - } - - public int getValue() { - return value; - } -} diff --git a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/cairo/CairoRegion.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/cairo/CairoRegion.java deleted file mode 100644 index 6a17b0912..000000000 --- a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/cairo/CairoRegion.java +++ /dev/null @@ -1,22 +0,0 @@ -/******************************************************************************* - * 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.cairo; - -import java.lang.foreign.MemorySegment; - -/** - * A {@code cairo_region_t}. - */ -public record CairoRegion(MemorySegment segment) { - -} diff --git a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/cairo/CairoSurface.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/cairo/CairoSurface.java deleted file mode 100644 index dc79ea413..000000000 --- a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/cairo/CairoSurface.java +++ /dev/null @@ -1,22 +0,0 @@ -/******************************************************************************* - * 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.cairo; - -import java.lang.foreign.MemorySegment; - -/** - * Base class for surfaces. - */ -public record CairoSurface(MemorySegment segment) { - -} diff --git a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk3/GDK3.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk3/GDK3.java deleted file mode 100644 index e0405f65e..000000000 --- a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk3/GDK3.java +++ /dev/null @@ -1,134 +0,0 @@ -/******************************************************************************* - * 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.gtk3; - -import org.eclipse.wb.internal.os.linux.GDK; -import org.eclipse.wb.internal.os.linux.cairo.CairoContext; -import org.eclipse.wb.internal.os.linux.cairo.CairoRegion; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; -import java.lang.invoke.MethodHandle; - -/** - * The GDK toolkit compatible with GDK 3.x - */ -public class GDK3 extends GDK { - - private static class InstanceHolder { - private static final MethodHandle gdk_cairo_set_source_window = createHandle(GDK, "gdk_cairo_set_source_window", - FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.JAVA_DOUBLE, ValueLayout.JAVA_DOUBLE)); - - private static final MethodHandle gdk_window_get_height = createHandle(GDK, "gdk_window_get_height", - FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS)); - - private static final MethodHandle gdk_window_get_visible_region = createHandle(GDK, "gdk_window_get_visible_region", - FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS)); - - private static final MethodHandle gdk_window_get_width = createHandle(GDK, "gdk_window_get_width", - FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS)); - - private static final MethodHandle gdk_window_is_visible = createHandle(GDK, "gdk_window_is_visible", - FunctionDescriptor.of(ValueLayout.JAVA_BOOLEAN, ValueLayout.ADDRESS)); - - private static final MethodHandle gdk_window_process_updates = createHandle(GDK, "gdk_window_process_updates", - FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.JAVA_BOOLEAN)); - } - - /** - * Sets the given window as the source pattern for {@code cr}. - * - * The pattern has an extend mode of {@code CAIRO_EXTEND_NONE} and is aligned so - * that the origin of window is {@code x}, {@code y}. The window contains all - * its subwindows when rendering. - * - * Note that the contents of window are undefined outside of the visible part of - * {@code window}, so use this function with care. - * - * @param cr A cairo context. - * @param window A {@link GdkWindow}. - * @param x X coordinate of location to place upper left corner of - * {@code window}. - * @param y Y coordinate of location to place upper left corner of - * {@code window}. - */ - public static void gdk_cairo_set_source_window(CairoContext cr, GdkWindow window, double x, double y) { - runSafe(() -> InstanceHolder.gdk_cairo_set_source_window.invoke(cr.segment(), window.segment(), x, y)); - } - - /** - * Returns the height of the given window. - * - * On the X11 platform the returned size is the size reported in the - * most-recently-processed configure event, rather than the current size on the - * X server. - * - * @return The height of {@code window}. - */ - public static int gdk_window_get_height(GdkWindow window) { - return (int) callSafe(() -> InstanceHolder.gdk_window_get_height.invoke(window.segment())); - } - - /** - * Computes the region of the {@code window} that is potentially visible. This - * does not necessarily take into account if the window is obscured by other - * windows, but no area outside of this region is visible. - * - * @return A {@link CairoRegion}. This must be freed with cairo_region_destroy() - * when you are done. - */ - public static CairoRegion gdk_window_get_visible_region(GdkWindow window) { - MemorySegment handle = (MemorySegment) callSafe(() -> InstanceHolder.gdk_window_get_visible_region.invoke(window.segment())); - return new CairoRegion(handle); - } - - /** - * Returns the width of the given window. - * - * On the X11 platform the returned size is the size reported in the - * most-recently-processed configure event, rather than the current size on the - * X server. - * - * @return The width of {@code window}. - */ - public static int gdk_window_get_width(GdkWindow window) { - return (int) callSafe(() -> InstanceHolder.gdk_window_get_width.invoke(window.segment())); - } - - /** - * Checks whether the window has been mapped (with {@code gdk_window_show()} or - * {@code gdk_window_show_unraised())}. - * - * @return {@code true} if the window is mapped. - */ - public static boolean gdk_window_is_visible(GdkWindow window) { - return (boolean) callSafe(() -> InstanceHolder.gdk_window_is_visible.invoke(window.segment())); - } - - /** - * Sends one or more expose events to {@code window}. The areas in each expose - * event will cover the entire update area for the window (see - * {@code gdk_window_invalidate_region()} for details). Normally GDK calls - * {@code gdk_window_process_all_updates()} on your behalf, so there’s no need - * to call this function unless you want to force expose events to be delivered - * immediately and synchronously (vs. the usual case, where GDK delivers them in - * an idle handler). Occasionally this is useful to produce nicer scrolling - * behavior, for example. - * - * @param update_children Whether to also process updates for child windows. - */ - public static void gdk_window_process_updates(GdkWindow window, boolean update_children) { - runSafe(() -> InstanceHolder.gdk_window_process_updates.invoke(window.segment(), update_children)); - } -} diff --git a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk3/GTK3.java b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk3/GTK3.java index 71b7af7d3..57b403cc0 100644 --- a/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk3/GTK3.java +++ b/org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk3/GTK3.java @@ -12,11 +12,12 @@ *******************************************************************************/ package org.eclipse.wb.internal.os.linux.gtk3; +import org.eclipse.wb.internal.os.linux.Cairo; import org.eclipse.wb.internal.os.linux.GTK; +import org.eclipse.wb.internal.os.linux.GtkAllocation; import org.eclipse.wb.internal.os.linux.GtkWidget; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; @@ -26,26 +27,22 @@ public final class GTK3 extends GTK { private static class InstanceHolder { - private static final MethodHandle gtk_widget_get_window = createHandle(GTK, "gtk_widget_get_window", - FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS)); - private static final MethodHandle gtk_window_set_keep_above = createHandle(GTK, "gtk_window_set_keep_above", FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.JAVA_BOOLEAN)); + private static final MethodHandle gtk_widget_get_preferred_size = createHandle(GTK, "gtk_widget_get_preferred_size", + FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.ADDRESS)); + + private static final MethodHandle gtk_widget_size_allocate = createHandle(GTK, "gtk_widget_size_allocate", + FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.ADDRESS)); + + private static final MethodHandle gtk_widget_draw = createHandle(GTK, "gtk_widget_draw", + FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.ADDRESS)); + private static final MethodHandle gtk_widget_show_now = createHandle(GTK, "gtk_widget_show_now", FunctionDescriptor.ofVoid(ValueLayout.ADDRESS)); } - /** - * Returns the widget’s window if it is realized, {@code NULL} otherwise. - * - * @return {@code widget}’s window. - */ - public static GdkWindow gtk_widget_get_window(GtkWidget widget) { - MemorySegment handle = (MemorySegment) callSafe(() -> InstanceHolder.gtk_widget_get_window.invoke(widget.segment())); - return new GdkWindow(handle); - } - /** * Shows a widget. If the widget is an unmapped toplevel widget (i.e. a * {@code GtkWindow} that has not yet been shown), enter the main loop and wait @@ -79,4 +76,75 @@ public static void gtk_widget_show_now(GtkWidget widget) { public static void gtk_window_set_keep_above(GtkWindow window, boolean setting) { runSafe(() -> InstanceHolder.gtk_window_set_keep_above.invoke(window.segment(), setting)); } + + /** + * Retrieves the minimum and natural size of a widget, taking into account the + * widget’s preference for height-for-width management. + * + * This is used to retrieve a suitable size by container widgets which do not + * impose any restrictions on the child placement. It can be used to deduce + * toplevel window and menu sizes as well as child widgets in free-form + * containers such as {@code GtkFixed}. + * + * Handle with care. Note that the natural height of a height-for-width widget + * will generally be a smaller size than the minimum height, since the required + * height for the natural width is generally smaller than the required height + * for the minimum width. + * + * Use gtk_widget_measure() + * if you want to support baseline alignment. + * + * @param minimum_size Location for storing the minimum size. Can be + * {@code NULL}. + * @param natural_size Location for storing the natural size. Can be + * {@code NULL}. + */ + public static void gtk_widget_get_preferred_size(GtkWidget widget, GtkRequisition minimum_size, GtkRequisition natural_size) { + runSafe(() -> InstanceHolder.gtk_widget_get_preferred_size.invoke(widget.segment(), minimum_size.segment(), natural_size.segment())); + } + + /** + * This function is only used by {@code GtkContainer} subclasses, to assign a + * size and position to their child widgets. + * + * In this function, the allocation may be adjusted. It will be forced to a 1x1 + * minimum size, and the adjust_size_allocation virtual method on the child will + * be used to adjust the allocation. Standard adjustments include removing the + * widget’s margins, and applying the widget’s {@code GtkWidget:halign} and + * {@code GtkWidget:valign} properties. + * + * For baseline support in containers you need to use + * {@code gtk_widget_size_allocate_with_baseline()} instead. + * + * @param allocation Position and size to be allocated to {@code widget}. + */ + public static void gtk_widget_size_allocate(GtkWidget widget, GtkAllocation allocation) { + runSafe(() -> InstanceHolder.gtk_widget_size_allocate.invoke(widget.segment(), allocation.segment())); + } + + /** + * Draws {@code widget} to {@code cr}. The top left corner of the widget will be + * drawn to the currently set origin point of {@code cr}. + * + * You should pass a cairo context as {@code cr} argument that is in an original + * state. Otherwise the resulting drawing is undefined. For example changing the + * operator using {@code cairo_set_operator()} or the line width using + * {@code cairo_set_line_width()} might have unwanted side effects. You may + * however change the context’s transform matrix - like with + * {@code cairo_scale(),}, {@code cairo_translate()} or + * {@code cairo_set_matrix()} and clip region with {@code cairo_clip()} prior to + * calling this function. Also, it is fine to modify the context with + * {@code cairo_save()} and {@code cairo_push_group()} prior to calling this + * function. + * + * Note that special-purpose widgets may contain special code for rendering to + * the screen and might appear differently on screen and when rendered using + * gtk_widget_draw(). + * + * @param cr A cairo context to draw to. + */ + public static void gtk_widget_draw(GtkWidget widget, Cairo cr) { + runSafe(() -> InstanceHolder.gtk_widget_draw.invoke(widget.segment(), cr.segment())); + } } 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 46715fa6b..549857336 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 @@ -12,22 +12,17 @@ *******************************************************************************/ package org.eclipse.wb.internal.os.linux.gtk3; -import org.eclipse.wb.internal.core.utils.reflect.ReflectionUtils; -import org.eclipse.wb.internal.os.linux.GDK; -import org.eclipse.wb.internal.os.linux.GtkRuntimeException; +import org.eclipse.wb.internal.os.linux.Cairo; +import org.eclipse.wb.internal.os.linux.GTK; +import org.eclipse.wb.internal.os.linux.GtkAllocation; import org.eclipse.wb.internal.os.linux.GtkWidget; import org.eclipse.wb.internal.os.linux.ScreenshotMaker; -import org.eclipse.wb.internal.os.linux.cairo.Cairo; -import org.eclipse.wb.internal.os.linux.cairo.CairoContext; -import org.eclipse.wb.internal.os.linux.cairo.CairoFormat; -import org.eclipse.wb.internal.os.linux.cairo.CairoOperator; -import org.eclipse.wb.internal.os.linux.cairo.CairoRegion; -import org.eclipse.wb.internal.os.linux.cairo.CairoSurface; -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Device; +import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; +import java.lang.foreign.Arena; /** * Creates a screenshot of a given widget using the GTK3 API. */ @@ -35,69 +30,21 @@ public class GTK3ScreenshotMaker extends ScreenshotMaker { @Override protected Image getImageSurface(GtkWidget widget) { - GdkWindow window = GTK3.gtk_widget_get_window(widget); - if (!GDK3.gdk_window_is_visible(window)) { - // don't deal with unmapped windows - return null; - } - - int width = GDK3.gdk_window_get_width(window); - int height = GDK3.gdk_window_get_height(window); - // force paint. Note, not all widgets do this completely, known so far is - // GtkTreeViewer. - GDK3.gdk_window_process_updates(window, true); - // take screenshot - Image image = createImage(window, width, height); - // done - return image; - } - - private Image createImage(GdkWindow sourceWindow, int width, int height) { - // Create the Cairo surface on which the snapshot is drawn on - CairoSurface targetSurface = Cairo.cairo_image_surface_create(CairoFormat.CAIRO_FORMAT_ARGB32, width, height); - CairoContext cr = Cairo.cairo_create(targetSurface); - // Get the visible region of the window - // Wayland: Trying to take a screenshot of a partially unmapped widget - // results in a SIGFAULT. - CairoRegion visibleRegion = GDK3.gdk_window_get_visible_region(sourceWindow); - // Set the visible region as the clip for the Cairo context - GDK.gdk_cairo_region(cr, visibleRegion); - Cairo.cairo_clip(cr); - // Paint the surface - GDK3.gdk_cairo_set_source_window(cr, sourceWindow, 0, 0); - Cairo.cairo_set_operator(cr, CairoOperator.CAIRO_OPERATOR_SOURCE); - Cairo.cairo_paint(cr); - // Cleanup - Cairo.cairo_destroy(cr); - Cairo.cairo_surface_flush(targetSurface); - Cairo.cairo_region_destroy(visibleRegion); - return createImage(targetSurface.segment().address()); - } - - private Image createImage(long imageHandle) { - Image image = createImage0(imageHandle); - // BUG in SWT: Image instance is not fully initialized - // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=382175 - Image newImage = new Image(null, image.getImageData()); - image.dispose(); - return newImage; - } - - private Image createImage0(long imageHandle) { - try { - return (Image) ReflectionUtils.invokeMethod2( // - Image.class, // - "gtk_new", // - Device.class, // - int.class, // - long.class, // - long.class, // - null, // - SWT.BITMAP, // - imageHandle, // - 0); - } catch (Exception e) { - throw new GtkRuntimeException(e); + try (Arena arena = Arena.ofConfined()) { + GtkAllocation allocation = new GtkAllocation(arena); + GTK.gtk_widget_get_allocation(widget, allocation); + // Prevent allocation warnings + GTK3.gtk_widget_get_preferred_size(widget, GtkRequisition.NULL, GtkRequisition.NULL); + GTK3.gtk_widget_size_allocate(widget, allocation); + + int width = Math.max(1, allocation.width()); + int height = Math.max(1, allocation.height()); + + Image image = new Image(Display.getCurrent(), width, height); + GC gc = new GC(image); + GTK3.gtk_widget_draw(widget, Cairo.from(gc)); + gc.dispose(); + return image; } } 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/GtkRequisition.java similarity index 55% rename from org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk3/GdkWindow.java rename to org.eclipse.wb.os.linux/src_java24/org/eclipse/wb/internal/os/linux/gtk3/GtkRequisition.java index 38392d28d..fbc98d3d5 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/GtkRequisition.java @@ -13,10 +13,25 @@ package org.eclipse.wb.internal.os.linux.gtk3; import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; /** - * A GDK window. + * Represents the desired size of a widget. */ -public record GdkWindow(MemorySegment segment) { +public record GtkRequisition(MemorySegment segment) { + public static final GtkRequisition NULL = new GtkRequisition(MemorySegment.NULL); + /** + * @return The widget’s desired width. + */ + public int width() { + return segment.getAtIndex(ValueLayout.JAVA_INT, 0); + } + + /** + * @return The widget’s desired height. + */ + public int height() { + return segment.getAtIndex(ValueLayout.JAVA_INT, 0); + } }