From d2d21d67816f055232d9c541a0fef1978916308d Mon Sep 17 00:00:00 2001 From: Patrick Ziegler Date: Mon, 23 Sep 2024 18:40:20 +0200 Subject: [PATCH] [GTK] Move snapshot mechanism to Java layer 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 https://github.com/eclipse-windowbuilder/windowbuilder/issues/871 --- .../native/gtk/common/cairo.c | 62 ++++++ .../native/gtk/common/gdk.c | 64 ++++++ .../native/gtk/common/wbp.h | 42 ++++ .../native/gtk/gtk3/CMakeLists.txt | 2 +- org.eclipse.wb.os.linux/native/gtk/gtk3/rcp.c | 102 --------- .../os/linux/x86_64/libwbp3.so | Bin 27112 -> 28544 bytes .../wb/internal/os/linux/OSSupportLinux.java | 197 ++++++++++++++++-- 7 files changed, 350 insertions(+), 119 deletions(-) create mode 100644 org.eclipse.wb.os.linux/native/gtk/common/cairo.c create mode 100644 org.eclipse.wb.os.linux/native/gtk/common/gdk.c diff --git a/org.eclipse.wb.os.linux/native/gtk/common/cairo.c b/org.eclipse.wb.os.linux/native/gtk/common/cairo.c new file mode 100644 index 000000000..0afda4514 --- /dev/null +++ b/org.eclipse.wb.os.linux/native/gtk/common/cairo.c @@ -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 + +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); +} \ No newline at end of file diff --git a/org.eclipse.wb.os.linux/native/gtk/common/gdk.c b/org.eclipse.wb.os.linux/native/gtk/common/gdk.c new file mode 100644 index 000000000..8145c9955 --- /dev/null +++ b/org.eclipse.wb.os.linux/native/gtk/common/gdk.c @@ -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 +// +//////////////////////////////////////////////////////////////////////////// + +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); +} \ No newline at end of file diff --git a/org.eclipse.wb.os.linux/native/gtk/common/wbp.h b/org.eclipse.wb.os.linux/native/gtk/common/wbp.h index 666c9cdb2..40df24c5c 100644 --- a/org.eclipse.wb.os.linux/native/gtk/common/wbp.h +++ b/org.eclipse.wb.os.linux/native/gtk/common/wbp.h @@ -13,6 +13,7 @@ #include #include +#include #if (defined(__LP64__) && (!defined(WBP_ARCH64))) || defined(_WIN64) #if !defined(WBP_ARCH64) @@ -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); +} diff --git a/org.eclipse.wb.os.linux/native/gtk/gtk3/CMakeLists.txt b/org.eclipse.wb.os.linux/native/gtk/gtk3/CMakeLists.txt index 9a5965eb5..c8a52e7e1 100644 --- a/org.eclipse.wb.os.linux/native/gtk/gtk3/CMakeLists.txt +++ b/org.eclipse.wb.os.linux/native/gtk/gtk3/CMakeLists.txt @@ -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") diff --git a/org.eclipse.wb.os.linux/native/gtk/gtk3/rcp.c b/org.eclipse.wb.os.linux/native/gtk/gtk3/rcp.c index bcae2737d..10d2701a3 100644 --- a/org.eclipse.wb.os.linux/native/gtk/gtk3/rcp.c +++ b/org.eclipse.wb.os.linux/native/gtk/gtk3/rcp.c @@ -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); @@ -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 @@ -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) { diff --git a/org.eclipse.wb.os.linux/os/linux/x86_64/libwbp3.so b/org.eclipse.wb.os.linux/os/linux/x86_64/libwbp3.so index 02f8f985afb4f43d6ccc61173b3c5c6ece9b1897..8218c5b6c8814fdb029cfd3f6ebeee8cbfa6f1ab 100755 GIT binary patch literal 28544 zcmeHQdwf*Yoj(Z#so*4N#v)W5C@8*C0uez#^I%}Wghxn}w&=_xGf8HfWYWomhaYXR zRLeAtrCr)myQ|dg1KZWjwyC0R`NY;D>smMZp)J|pY@in%+AUZTqcO?goz1J%!~%E5N)xTg1;Au3#IHt zla*FGHC>Syiadprj~uFc=nslV{xx9a!wP3;)sy~;wY&}HpQY#wt$LHd>a0-w&YM%= zya@_l%qOGUnGG`Ob!)wDt;bN102K`3XI`(V29eT7JL%ZH#t!IaGv>rq9L1p9d zcxWX4)@!|G#nOzh!;l^f4B6iX=uupTTXZ-@rS|9459{@LGt};7EXf9qnN@46Dcs%E z7Ts-H}d;ZPmKD(*(K;Dm+oUH!ctu>FsZN^eCMTm1Va5@o; zO!Rv&pEA)eM8{;JzlaXWM2EQWWbpf@gMIghGTZqkIzE&A#TXcw=rm?B`Tc@J`%4|# zbIaM8?O*9&eC3Y@Sf(x{KjcmV5xHW<0L*Sn)Tw!~Gsj=ME$FX{GzS|aw}!>4svGl+jN~%_`TPxNCw*>vsSiL`7 z+ZbtS4g0s&_#@4*4sn09)!!ItZrkQxw{c@zOG`8sFPEslus;4Nf8o|hs6HGALVvbK znnTg8f*6`kWKA6lFOgP%VQsXjCE6N^heK9J;8+-A;Y*%Tv?W*@iEp=hB07`D!cQwj zjtyEuM|38S&`&&CU*8z^7Y1vhTf$?~&8-d5t^UI1)J~Q}qK&YbDFdk?5(-p0X!InrE4VQfy-qsRq#uTEV)Y#V4EWJr7DMT`ch?W>r|Fz-9M)XN69L(TZG9}MX zbH4Ud{@R95`Rjs_#!*E#EeI{acmo=Np@+$op&gPBHgQLP_R3owdct%iX_7P=`zh;B zu(2^(8;nPys!uIzkcBfsZ)GK`E7$o8SFWq9D5>%nmQ=1PUASPRq0z?Ht@l=zRIRJ@ z7j9fvU0LQGskz!)hew?qca~0x5i`9?b&4F%NFq|f*)Io z{7B>+ddij1w6+-$+%aa0e*{j2pJU>7NS66?6g;4xQ!@S3M-{y%pdiy3@AfM?Zg1rC zD}@Ph*g_{BmmUjUH$}3JTIjhf4A^U-(=!>P1XK;Tk%*#MCI?iV=(vS4N?t%!iH=)6 zqf8H|D$#N4W)x3ARf&#UNu$gQs4CIv;4(j;YDD805oK|J{wi5?fKiGAs!H;>#Wad9 zpsGZlYEXq(Z=s`Ojk3u?cNtV60v7r-3%$WYw>~dvvCz-6$ZxaI={YHv9RXFNcDk7e zxGSI#qMxs-^6&0|LWs`KB#5#%Kz~(y^doT)k$q53yD_k0`f zvf+05Gs{!;-?rhe+3;6v_=`6Dw>JD48-B=!AGF~Q+wg~M`1fr1y*B*oHvDcIewPjZ zj1Avn!<%h*oejUmhTmwz*BZF5WBCRkzK)zV1eTnRyD=3PEmKqRL&@IUi+7-R#V<94 z{46Pd^$Y@+o$)13`5u4mdf(%xC-|}s`JO!!&xe8cmMR02lf7yjus+S#9m|iyzG%Cm z+P8E0?;$CCiR1C9zV_t@LGOF?y=1a4gwcN}=jXt)ZicSmpY;2;f=QKX=wb25^CfOS z?Mt+sg5v?z^4vQoU{Ub3dr$k?0d}1xC+*dc044ETU*gH+v%W+Q`O`w>?K@6k-SD+v z{Qwg?+_%gGCGo7U!+oQyJz0XaDUtJKmS`8IT8xPu?n@1|Syr_GOIX#GGO@#b3O6Fk z+kmWTZ-p#jRh!7f4)>#m+E1-&*^oGqwfjGQX^E7_h*YAwFEt*0Kg9esW8uM?>1%g? zcPW7i9k*u?H;O=ebzdSoP4*5)+0uz=!ke9iaV?PDhS2I(T;nV}b@(Uv6vmZlx?eg~Hq<@+sJ)4uOl+SAjt6Dit1HMDv7VVfpauQ0x
XX2f2{8Idzb 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 diff --git a/org.eclipse.wb.os.linux/src/org/eclipse/wb/internal/os/linux/OSSupportLinux.java b/org.eclipse.wb.os.linux/src/org/eclipse/wb/internal/os/linux/OSSupportLinux.java index 4e7bf8287..0b18d2bb2 100644 --- a/org.eclipse.wb.os.linux/src/org/eclipse/wb/internal/os/linux/OSSupportLinux.java +++ b/org.eclipse.wb.os.linux/src/org/eclipse/wb/internal/os/linux/OSSupportLinux.java @@ -365,9 +365,26 @@ private Image createImage(long imageHandle) throws Exception { return newImage; } - private Image createImage(long windowHandle, int width, int height) throws Exception { - long imageHandle = _getImageSurface(windowHandle, width, height); - return createImage(imageHandle); + private Image createImage(long sourceWindow, int width, int height) throws Exception { + // Create the Cairo surface on which the snapshot is drawn on + long /*cairo_surface_t*/ targetSurface = _cairo_image_surface_create(_CAIRO_FORMAT_ARGB32(), width, height); + long /*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. + long /* 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 createImage(targetSurface); } //////////////////////////////////////////////////////////////////////////// @@ -549,19 +566,6 @@ public void runAwt(Runnable job) { */ private static native void _gtk_widget_get_allocation(long widgetHandle, GtkAllocation rect); - /** - * Paints the surface of the given window onto a image. The image is created - * within the native C code and its handle returned by this method. The caller - * of this method needs to ensure that this handle is disposed properly. The - * size of the image matches {@code width} and {@code height}. - * - * @param windowHandle The memory address of the window to capture. - * @param width The width of the window. - * @param height The height of the window. - * @return The memory address of the image handle. - */ - private static native long _getImageSurface(long windowHandle, int width, int height); - /** * Toggles the "above" X Window property. If forceToggle is false then * no toggling if window already has the "above" property set. @@ -743,6 +747,167 @@ private static native boolean _gtk_tree_view_get_path_at_pos(long tree_view, int */ private static native void _gtk_tree_path_free(long path); + //////////////////////////////////////////////////////////////////////////// + // + // GDK + // + //////////////////////////////////////////////////////////////////////////// + + /** + * + * + * 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 {@code cairo_region_t}. This must be freed with + * cairo_region_destroy() when you are done. + */ + private static native long _gdk_window_get_visible_region(long window); + + /** + * Adds the given region to the current path of {@code cr}. + */ + private static native void _gdk_cairo_region(long cr, long region); + + /** + * + * + * 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 {@code window} is {@code x}, {@code y}. The window + * contains all its subwindows when rendering. + * + * Note that the contents of {@code window} are undefined outside of the visible + * part of {@code window}, so use this function with care. + */ + private static native void _gdk_cairo_set_source_window(long cr, long window, double x, double y); + + //////////////////////////////////////////////////////////////////////////// + // + // Cairo + // + //////////////////////////////////////////////////////////////////////////// + + private static native int _CAIRO_FORMAT_ARGB32(); + + private static native int _CAIRO_OPERATOR_SOURCE(); + + /** + * Creates a new cairo_t with all graphics state parameters set to default + * values and with {@code target} as a target surface. The target surface should + * be constructed with a backend-specific function such as + * {@link #_cairo_image_surface_create()} (or any other + * cairo_backend_surface_create() variant). + * + * This function references {@code target} , so you can immediately call + * {@link _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 + * {@link #_cairo_destroy()} when you are done using the + * {@code cairo_t}. This function never returns {@code NULL}. If memory + * cannot be allocated, a special {@code cairo_t} object will be + * returned on which {@link #_cairo_status()} returns + * {@code CAIRO_STATUS_NO_MEMORY}. If you attempt to target a surface + * which does not support writing (such as 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. + */ + private static native long _cairo_create(long target); + + /** + * 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 {@link #_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 {@link #_cairo_surface_status()} to check for this. + */ + private static native long _cairo_image_surface_create(int format, int width, int height); + + /** + * Establishes a new clip region by intersecting the current clip region with + * the current path as it would be filled by {@link _cairo_fill()} and according + * to the current fill rule (see {@link #_cairo_set_fill_rule()}). + * + * After {@link #_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 {@link #_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 + * {@link #_cairo_clip()} within a + * {@link #_cairo_save()}/{@link #_cairo_restore()} pair. The only other means + * of increasing the size of the clip region is {@link _cairo_reset_clip()}. + * + * @param cr a cairo context + */ + private static native void _cairo_clip(long cr); + + /** + * A drawing operator that paints the current source everywhere within the + * current clip region. + * + * @param cr a cairo context + */ + private static native void _cairo_paint(long cr); + + /** + * 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 cairo_t + * @param op a compositing operator, specified as a cairo_operator_t + */ + private static native void _cairo_set_operator(long cr, int op); + + /** + * Decreases the reference count on {@code cr} by one. If the result is zero, + * then {@code cr} and all associated resources are freed. See + * {@link #_cairo_reference()}. + * + * @param cr a cairo_t + */ + private static native void _cairo_destroy(long cr); + + /** + * 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 cairo_surface_t + */ + private static native void _cairo_surface_flush(long surface); + + /** + * Destroys a {@code cairo_region_t} object created with + * {@link #_cairo_region_create()}, {@link #_cairo_region_copy()}, or or + * {@link #_cairo_region_create_rectangle()}. + * + * @param region a cairo_region_t + */ + private static native void _cairo_region_destroy(long region); + //////////////////////////////////////////////////////////////////////////// // // GDK/GTK wrappers