Skip to content

Commit

Permalink
[GTK] Move snapshot mechanism to Java layer
Browse files Browse the repository at this point in the history
This exposes the GDK/Cairo methods to allow us to implement taking a
screenshot of an SWT widget directly in Java.

To improve readability, the C methods have been split into separate GDK,
GTK and Cairo files.

See eclipse-windowbuilder#871
  • Loading branch information
ptziegler committed Sep 23, 2024
1 parent 6f10446 commit d2d21d6
Show file tree
Hide file tree
Showing 7 changed files with 350 additions and 119 deletions.
62 changes: 62 additions & 0 deletions org.eclipse.wb.os.linux/native/gtk/common/cairo.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*******************************************************************************
* Copyright (c) 2024 Patrick Ziegler and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Patrick Ziegler - initial API and implementation
******************************************************************************/
#include "wbp.h"
#include <jni.h>

JNIEXPORT jint JNICALL OS_NATIVE(_1CAIRO_1FORMAT_1ARGB32)
(JNIEnv *envir, jobject that) {
return (jint) CAIRO_FORMAT_ARGB32;
}

JNIEXPORT jint JNICALL OS_NATIVE(_1CAIRO_1OPERATOR_1SOURCE)
(JNIEnv *envir, jobject that) {
return (jint) CAIRO_OPERATOR_SOURCE;
}

JNIEXPORT JHANDLE JNICALL OS_NATIVE(_1cairo_1create)
(JNIEnv *envir, jobject that, JHANDLE target) {
return (JHANDLE) cairo_create((cairo_surface_t *)(CHANDLE) target);
}

JNIEXPORT JHANDLE JNICALL OS_NATIVE(_1cairo_1image_1surface_1create)
(JNIEnv *envir, jobject that, jint format, jint width, jint height) {
return (JHANDLE) cairo_image_surface_create((cairo_format_t)format, (int) width, (int) height);
}

JNIEXPORT void JNICALL OS_NATIVE(_1cairo_1clip)
(JNIEnv *envir, jobject that, JHANDLE cr) {
cairo_clip((cairo_t *)(CHANDLE) cr);
}

JNIEXPORT void JNICALL OS_NATIVE(_1cairo_1paint)
(JNIEnv *envir, jobject that, JHANDLE cr) {
cairo_paint((cairo_t *)(CHANDLE) cr);
}

JNIEXPORT void JNICALL OS_NATIVE(_1cairo_1set_1operator)
(JNIEnv *envir, jobject that, JHANDLE cr, jint op) {
cairo_set_operator((cairo_t *)(CHANDLE) cr, (cairo_operator_t) op);
}

JNIEXPORT void JNICALL OS_NATIVE(_1cairo_1destroy)
(JNIEnv *envir, jobject that, JHANDLE cr) {
cairo_destroy((cairo_t *)(CHANDLE) cr);
}

JNIEXPORT void JNICALL OS_NATIVE(_1cairo_1surface_1flush)
(JNIEnv *envir, jobject that, JHANDLE surface) {
cairo_surface_flush((cairo_surface_t *)(CHANDLE) surface);
}

JNIEXPORT void JNICALL OS_NATIVE(_1cairo_1region_1destroy)
(JNIEnv *envir, jobject that, JHANDLE region) {
cairo_region_destroy((cairo_region_t *)(CHANDLE) region);
}
64 changes: 64 additions & 0 deletions org.eclipse.wb.os.linux/native/gtk/common/gdk.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*******************************************************************************
* Copyright (c) 2024 Patrick Ziegler and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Patrick Ziegler - initial API and implementation
******************************************************************************/
#include "wbp.h"
#include <jni.h>

////////////////////////////////////////////////////////////////////////////
//
// JNI
//
////////////////////////////////////////////////////////////////////////////

JNIEXPORT jboolean JNICALL OS_NATIVE(_1gdk_1window_1is_1visible)
(JNIEnv *envir, jobject that, JHANDLE windowHandle) {
return gdk_window_is_visible((GdkWindow*)(CHANDLE) windowHandle);
}

JNIEXPORT void JNICALL OS_NATIVE(_1gdk_1window_1get_1geometry)
(JNIEnv *envir, jobject that, JHANDLE windowHandle, jintArray x, jintArray y, jintArray width, jintArray height) {
jint x1;
jint y1;
jint width1;
jint height1;
gdk_window_get_geometry((GdkWindow*)(CHANDLE) windowHandle, &x1, &y1, &width1, &height1);
if (x != NULL) {
(*envir) -> SetIntArrayRegion(envir, x, 0, 1, &x1);
}
if (y != NULL) {
(*envir) -> SetIntArrayRegion(envir, y, 0, 1, &y1);
}
if (width != NULL) {
(*envir) -> SetIntArrayRegion(envir, width, 0, 1, &width1);
}
if (height != NULL) {
(*envir) -> SetIntArrayRegion(envir, height, 0, 1, &height1);
}
}

JNIEXPORT void JNICALL OS_NATIVE(_1gdk_1window_1process_1updates)
(JNIEnv *envir, jobject that, JHANDLE widgetHandle, jboolean update_children) {
gdk_window_process_updates((GdkWindow*)(CHANDLE) widgetHandle, (gboolean) update_children);
}

JNIEXPORT JHANDLE JNICALL OS_NATIVE(_1gdk_1window_1get_1visible_1region)
(JNIEnv *envir, jobject that, JHANDLE window) {
return (JHANDLE) gdk_window_get_visible_region((GdkWindow *)(CHANDLE) window);
}

JNIEXPORT void JNICALL OS_NATIVE(_1gdk_1cairo_1region)
(JNIEnv *envir, jobject that, JHANDLE cr, JHANDLE region) {
gdk_cairo_region((cairo_t *)(CHANDLE) cr, (cairo_region_t *)(CHANDLE) region);
}

JNIEXPORT void JNICALL OS_NATIVE(_1gdk_1cairo_1set_1source_1window)
(JNIEnv *envir, jobject that, JHANDLE cr, JHANDLE window, jdouble x, jdouble y) {
gdk_cairo_set_source_window((cairo_t *)(CHANDLE) cr, (GdkWindow *)(CHANDLE) window, (gdouble)x, (gdouble)y);
}
42 changes: 42 additions & 0 deletions org.eclipse.wb.os.linux/native/gtk/common/wbp.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <jni.h>

#if (defined(__LP64__) && (!defined(WBP_ARCH64))) || defined(_WIN64)
#if !defined(WBP_ARCH64)
Expand All @@ -36,3 +37,44 @@
#define CALLBACK_SIG "(Ljava/lang/Number;Ljava/lang/Number;)V"
#define OS_NATIVE(func) Java_org_eclipse_wb_internal_os_linux_OSSupportLinux_##func

////////////////////////////////////////////////////////////////////////////
//
// GdkRectangle
//
////////////////////////////////////////////////////////////////////////////

typedef struct JGdkRectangle {
jclass clazz;
jfieldID x;
jfieldID y;
jfieldID width;
jfieldID height;
} JGdkRectangle;
static JGdkRectangle GDK_RECTANGLE = { .clazz = NULL};

static void init_gdk_rectangle(JNIEnv *envir, jobject jrectangle) {
if (GDK_RECTANGLE.clazz != NULL) {
return;
}
GDK_RECTANGLE.clazz = (*envir)->GetObjectClass(envir, jrectangle);
GDK_RECTANGLE.x = (*envir)->GetFieldID(envir, GDK_RECTANGLE.clazz, "x", "I");
GDK_RECTANGLE.y = (*envir)->GetFieldID(envir, GDK_RECTANGLE.clazz, "y", "I");
GDK_RECTANGLE.width = (*envir)->GetFieldID(envir, GDK_RECTANGLE.clazz, "width", "I");
GDK_RECTANGLE.height = (*envir)->GetFieldID(envir, GDK_RECTANGLE.clazz, "height", "I");
}

static void get_gdk_rectangle(JNIEnv *envir, jobject jrectangle, GdkRectangle *rectangle) {
init_gdk_rectangle(envir, jrectangle);
rectangle->x = (*envir)->GetIntField(envir, jrectangle, GDK_RECTANGLE.x);
rectangle->y = (*envir)->GetIntField(envir, jrectangle, GDK_RECTANGLE.y);
rectangle->width = (*envir)->GetIntField(envir, jrectangle, GDK_RECTANGLE.width);
rectangle->height = (*envir)->GetIntField(envir, jrectangle, GDK_RECTANGLE.height);
}

static void set_gdk_rectangle(JNIEnv *envir, jobject jrectangle, GdkRectangle *rectangle) {
init_gdk_rectangle(envir, jrectangle);
(*envir)->SetIntField(envir, jrectangle, GDK_RECTANGLE.x, (jint)rectangle->x);
(*envir)->SetIntField(envir, jrectangle, GDK_RECTANGLE.y, (jint)rectangle->y);
(*envir)->SetIntField(envir, jrectangle, GDK_RECTANGLE.width, (jint)rectangle->width);
(*envir)->SetIntField(envir, jrectangle, GDK_RECTANGLE.height, (jint)rectangle->height);
}
2 changes: 1 addition & 1 deletion org.eclipse.wb.os.linux/native/gtk/gtk3/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ if (GTK3_FOUND)
link_directories(${GTK3_LIBRARY_DIRS})
add_definitions(${GTK3_CFLAGS_OTHER})
add_definitions(${WBPLIBS_BITNESS_DEF})
add_library(wbp3 SHARED rcp.c)
add_library(wbp3 SHARED rcp.c ../common/cairo.c ../common/gdk.c)
target_link_libraries(wbp3 ${GTK3_LIBRARIES})
else()
message (FATAL_ERROR "Cannot found dev libs for Gtk3")
Expand Down
102 changes: 0 additions & 102 deletions org.eclipse.wb.os.linux/native/gtk/gtk3/rcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,105 +56,11 @@ static void set_gtk_allocation(JNIEnv *envir, jobject jallocation, GtkAllocation
(*envir)->SetIntField(envir, jallocation, GTK_ALLOCATION.height, (jint)allocation->height);
}

////////////////////////////////////////////////////////////////////////////
//
// GdkRectangle
//
////////////////////////////////////////////////////////////////////////////

typedef struct JGdkRectangle {
jclass clazz;
jfieldID x;
jfieldID y;
jfieldID width;
jfieldID height;
} JGdkRectangle;
JGdkRectangle GDK_RECTANGLE = { .clazz = NULL};

static void init_gdk_rectangle(JNIEnv *envir, jobject jrectangle) {
if (GDK_RECTANGLE.clazz != NULL) {
return;
}
GDK_RECTANGLE.clazz = (*envir)->GetObjectClass(envir, jrectangle);
GDK_RECTANGLE.x = (*envir)->GetFieldID(envir, GDK_RECTANGLE.clazz, "x", "I");
GDK_RECTANGLE.y = (*envir)->GetFieldID(envir, GDK_RECTANGLE.clazz, "y", "I");
GDK_RECTANGLE.width = (*envir)->GetFieldID(envir, GDK_RECTANGLE.clazz, "width", "I");
GDK_RECTANGLE.height = (*envir)->GetFieldID(envir, GDK_RECTANGLE.clazz, "height", "I");
}

static void get_gdk_rectangle(JNIEnv *envir, jobject jrectangle, GdkRectangle *rectangle) {
init_gdk_rectangle(envir, jrectangle);
rectangle->x = (*envir)->GetIntField(envir, jrectangle, GDK_RECTANGLE.x);
rectangle->y = (*envir)->GetIntField(envir, jrectangle, GDK_RECTANGLE.y);
rectangle->width = (*envir)->GetIntField(envir, jrectangle, GDK_RECTANGLE.width);
rectangle->height = (*envir)->GetIntField(envir, jrectangle, GDK_RECTANGLE.height);
}

static void set_gdk_rectangle(JNIEnv *envir, jobject jrectangle, GdkRectangle *rectangle) {
init_gdk_rectangle(envir, jrectangle);
(*envir)->SetIntField(envir, jrectangle, GDK_RECTANGLE.x, (jint)rectangle->x);
(*envir)->SetIntField(envir, jrectangle, GDK_RECTANGLE.y, (jint)rectangle->y);
(*envir)->SetIntField(envir, jrectangle, GDK_RECTANGLE.width, (jint)rectangle->width);
(*envir)->SetIntField(envir, jrectangle, GDK_RECTANGLE.height, (jint)rectangle->height);
}

////////////////////////////////////////////////////////////////////////////
//
// Screenshot
//
////////////////////////////////////////////////////////////////////////////

static cairo_surface_t* copyImageSurface(GdkWindow *sourceWindow, gint width, gint height) {
// Create the Cairo surface on which the snapshot is drawn on
cairo_surface_t *targetSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
cairo_t *cr = 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.
cairo_region_t *visibleRegion = gdk_window_get_visible_region(sourceWindow);
// Set the visible region as the clip for the Cairo context
gdk_cairo_region(cr, visibleRegion);
cairo_clip(cr);
// Paint the surface
gdk_cairo_set_source_window(cr, sourceWindow, 0, 0);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_paint(cr);
// Cleanup
cairo_destroy(cr);
cairo_surface_flush(targetSurface);
cairo_region_destroy(visibleRegion);
return targetSurface;
}

////////////////////////////////////////////////////////////////////////////
//
// JNI
//
////////////////////////////////////////////////////////////////////////////
JNIEXPORT jboolean JNICALL OS_NATIVE(_1gdk_1window_1is_1visible)
(JNIEnv *envir, jobject that, JHANDLE windowHandle) {
return gdk_window_is_visible((GdkWindow*)(CHANDLE) windowHandle);
}
JNIEXPORT void JNICALL OS_NATIVE(_1gdk_1window_1get_1geometry)
(JNIEnv *envir, jobject that, JHANDLE windowHandle, jintArray x, jintArray y, jintArray width, jintArray height) {
jint x1;
jint y1;
jint width1;
jint height1;
gdk_window_get_geometry((GdkWindow*)(CHANDLE) windowHandle, &x1, &y1, &width1, &height1);
if (x != NULL) {
(*envir) -> SetIntArrayRegion(envir, x, 0, 1, &x1);
}
if (y != NULL) {
(*envir) -> SetIntArrayRegion(envir, y, 0, 1, &y1);
}
if (width != NULL) {
(*envir) -> SetIntArrayRegion(envir, width, 0, 1, &width1);
}
if (height != NULL) {
(*envir) -> SetIntArrayRegion(envir, height, 0, 1, &height1);
}
}
JNIEXPORT JHANDLE JNICALL OS_NATIVE(_1gtk_1widget_1get_1window)
(JNIEnv *envir, jobject that, JHANDLE widgetHandle) {
return (JHANDLE) gtk_widget_get_window((GtkWidget*)(CHANDLE) widgetHandle);
Expand All @@ -171,10 +77,6 @@ JNIEXPORT void JNICALL OS_NATIVE(_1gtk_1widget_1set_1opacity)
(JNIEnv *envir, jobject that, JHANDLE jhandle, jdouble jalpha) {
gtk_widget_set_opacity((GtkWidget*)(CHANDLE) jhandle, (double)jalpha);
}
JNIEXPORT void JNICALL OS_NATIVE(_1gdk_1window_1process_1updates)
(JNIEnv *envir, jobject that, JHANDLE widgetHandle, jboolean update_children) {
gdk_window_process_updates((GdkWindow*)(CHANDLE) widgetHandle, update_children);
}
JNIEXPORT jboolean JNICALL OS_NATIVE(_1toggle_1above)
(JNIEnv *envir, jobject that, JHANDLE widgetHandle, jboolean forceToggle) {
// NOT IMPLEMENTED
Expand All @@ -190,10 +92,6 @@ JNIEXPORT void JNICALL OS_NATIVE(_1gtk_1widget_1hide)
// hide then
gtk_widget_hide((GtkWidget*)(CHANDLE) widgetHandle);
}
JNIEXPORT JHANDLE JNICALL OS_NATIVE(_1getImageSurface)
(JNIEnv *envir, jobject that, JHANDLE windowHandle, jint width, jint height) {
return (JHANDLE) copyImageSurface((GdkWindow*)(CHANDLE) windowHandle, width, height);
}
// tree items
JNIEXPORT JHANDLE JNICALL OS_NATIVE(_1gtk_1tree_1view_1get_1expander_1column)
(JNIEnv *envir, jobject that, JHANDLE tree_view) {
Expand Down
Binary file modified org.eclipse.wb.os.linux/os/linux/x86_64/libwbp3.so
Binary file not shown.
Loading

0 comments on commit d2d21d6

Please sign in to comment.